blob: 978723a1dd286c5ca19f0ee066affc5d16eba034 [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2020 MediaTek Inc.
4 * Author: Mac Lu <mac.lu@mediatek.com>
5 */
6
7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8
9#include <linux/device.h>
10#include <linux/io.h>
11#include <linux/module.h>
12#include <linux/mod_devicetable.h>
13#include <linux/nvmem-provider.h>
14#include <linux/of.h>
15#include <linux/of_device.h>
16#include <linux/platform_device.h>
17#include <linux/slab.h>
18
19struct mtk_devinfo_priv {
20 unsigned int *devinfo_data;
21};
22
23struct devinfo_tag {
24 unsigned int data_size;
25 unsigned int data[0];
26};
27
28static int devinfo_parse_dt(struct mtk_devinfo_priv *priv, struct device *dev)
29{
30 struct device_node *chosen_node;
31 struct devinfo_tag *tags;
32 unsigned int size = 0;
33
34 chosen_node = of_find_node_by_path("/chosen");
35 if (!chosen_node) {
36 chosen_node = of_find_node_by_path("/chosen@0");
37 if (!chosen_node) {
38 pr_warn("chosen node is not found!!\n");
39 return -ENXIO;
40 }
41 }
42
43 tags = (struct devinfo_tag *) of_get_property(chosen_node,
44 "atag,devinfo", NULL);
45
46 if (tags) {
47 size = tags->data_size;
48
49 priv->devinfo_data = devm_kzalloc(dev,
50 sizeof(struct devinfo_tag) + (size * sizeof(unsigned int)),
51 GFP_KERNEL);
52 if (!priv->devinfo_data)
53 return -ENOMEM;
54
55 WARN_ON(size > 300); /* for size integer too big protection */
56
57 memcpy(priv->devinfo_data, tags->data,
58 (size * sizeof(unsigned int)));
59
60 } else {
61 pr_warn("atag,devinfo is not found\n");
62 return -ENXIO;
63 }
64 return (size * sizeof(unsigned int));
65}
66
67static int mtk_reg_read(void *context,
68 unsigned int reg, void *_val, size_t bytes)
69{
70 struct mtk_devinfo_priv *priv = context;
71 unsigned int *val = _val;
72 int i = 0, words = bytes / 4;
73
74 while (words--) {
75 *val++ = priv->devinfo_data[i + (reg / 4)];
76 i++;
77 }
78 return 0;
79}
80
81static int mtk_devinfo_probe(struct platform_device *pdev)
82{
83 struct device *dev = &pdev->dev;
84 struct nvmem_device *nvmem;
85 struct nvmem_config econfig = {};
86 struct mtk_devinfo_priv *priv;
87 int ret_size = 0;
88
89 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
90 if (!priv)
91 return -ENOMEM;
92
93 ret_size = devinfo_parse_dt(priv, dev);
94 if (ret_size <= 0) {
95 pr_warn("parse devinfo failed\n");
96 return ret_size;
97 }
98
99 econfig.size = ret_size;
100 econfig.stride = 4;
101 econfig.word_size = 4;
102 econfig.name = "mtk-devinfo";
103 econfig.read_only = true;
104 econfig.reg_read = mtk_reg_read;
105 econfig.priv = priv;
106 econfig.dev = dev;
107 nvmem = devm_nvmem_register(dev, &econfig);
108
109 return PTR_ERR_OR_ZERO(nvmem);
110}
111
112static const struct of_device_id mtk_devinfo_of_match[] = {
113 { .compatible = "mediatek,devinfo",},
114 {/* sentinel */},
115};
116MODULE_DEVICE_TABLE(of, mtk_devinfo_of_match);
117
118static struct platform_driver mtk_devinfo_driver = {
119 .probe = mtk_devinfo_probe,
120 .driver = {
121 .name = "mediatek,devinfo",
122 .of_match_table = mtk_devinfo_of_match,
123 },
124};
125
126static int __init mtk_devinfo_init(void)
127{
128 int ret;
129
130 ret = platform_driver_register(&mtk_devinfo_driver);
131 if (ret) {
132 pr_err("Failed to register devinfo driver\n");
133 return ret;
134 }
135
136 return 0;
137}
138
139static void __exit mtk_devinfo_exit(void)
140{
141 return platform_driver_unregister(&mtk_devinfo_driver);
142}
143
144#ifdef MODULE
145module_init(mtk_devinfo_init);
146#else
147subsys_initcall(mtk_devinfo_init);
148#endif
149module_exit(mtk_devinfo_exit);
150
151MODULE_AUTHOR("Mac Lu <mac.lu@mediatek.com>");
152MODULE_DESCRIPTION("Mediatek device information driver");
153MODULE_LICENSE("GPL v2");