1 | /* |
2 | * Copyright (c) 2017 BayLibre, SAS |
3 | * Author: Neil Armstrong <narmstrong@baylibre.com> |
4 | * |
5 | * SPDX-License-Identifier: GPL-2.0+ |
6 | */ |
7 | |
8 | #include <linux/io.h> |
9 | #include <linux/of.h> |
10 | #include <linux/of_address.h> |
11 | #include <linux/of_platform.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/sys_soc.h> |
15 | #include <linux/bitfield.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/mfd/syscon.h> |
18 | |
19 | #define AO_SEC_SD_CFG8 0xe0 |
20 | #define AO_SEC_SOCINFO_OFFSET AO_SEC_SD_CFG8 |
21 | |
22 | #define SOCINFO_MAJOR GENMASK(31, 24) |
23 | #define SOCINFO_PACK GENMASK(23, 16) |
24 | #define SOCINFO_MINOR GENMASK(15, 8) |
25 | #define SOCINFO_MISC GENMASK(7, 0) |
26 | |
27 | static const struct meson_gx_soc_id { |
28 | const char *name; |
29 | unsigned int id; |
30 | } soc_ids[] = { |
31 | { "GXBB" , 0x1f }, |
32 | { "GXTVBB" , 0x20 }, |
33 | { "GXL" , 0x21 }, |
34 | { "GXM" , 0x22 }, |
35 | { "TXL" , 0x23 }, |
36 | { "TXLX" , 0x24 }, |
37 | { "AXG" , 0x25 }, |
38 | { "GXLX" , 0x26 }, |
39 | { "TXHD" , 0x27 }, |
40 | { "G12A" , 0x28 }, |
41 | { "G12B" , 0x29 }, |
42 | { "SM1" , 0x2b }, |
43 | { "A1" , 0x2c }, |
44 | }; |
45 | |
46 | static const struct meson_gx_package_id { |
47 | const char *name; |
48 | unsigned int major_id; |
49 | unsigned int pack_id; |
50 | unsigned int pack_mask; |
51 | } soc_packages[] = { |
52 | { "S905" , 0x1f, 0, 0x20 }, /* pack_id != 0x20 */ |
53 | { "S905H" , 0x1f, 0x3, 0xf }, /* pack_id & 0xf == 0x3 */ |
54 | { "S905M" , 0x1f, 0x20, 0xf0 }, /* pack_id == 0x20 */ |
55 | { "S905D" , 0x21, 0, 0xf0 }, |
56 | { "S905X" , 0x21, 0x80, 0xf0 }, |
57 | { "S905W" , 0x21, 0xa0, 0xf0 }, |
58 | { "S905L" , 0x21, 0xc0, 0xf0 }, |
59 | { "S905M2" , 0x21, 0xe0, 0xf0 }, |
60 | { "S805X" , 0x21, 0x30, 0xf0 }, |
61 | { "S805Y" , 0x21, 0xb0, 0xf0 }, |
62 | { "S912" , 0x22, 0, 0x0 }, /* Only S912 is known for GXM */ |
63 | { "962X" , 0x24, 0x10, 0xf0 }, |
64 | { "962E" , 0x24, 0x20, 0xf0 }, |
65 | { "A113X" , 0x25, 0x37, 0xff }, |
66 | { "A113D" , 0x25, 0x22, 0xff }, |
67 | { "S905D2" , 0x28, 0x10, 0xf0 }, |
68 | { "S905Y2" , 0x28, 0x30, 0xf0 }, |
69 | { "S905X2" , 0x28, 0x40, 0xf0 }, |
70 | { "A311D" , 0x29, 0x10, 0xf0 }, |
71 | { "S922X" , 0x29, 0x40, 0xf0 }, |
72 | { "S905D3" , 0x2b, 0x4, 0xf5 }, |
73 | { "S905X3" , 0x2b, 0x5, 0xf5 }, |
74 | { "S905X3" , 0x2b, 0x10, 0x3f }, |
75 | { "S905D3" , 0x2b, 0x30, 0x3f }, |
76 | { "A113L" , 0x2c, 0x0, 0xf8 }, |
77 | }; |
78 | |
79 | static inline unsigned int socinfo_to_major(u32 socinfo) |
80 | { |
81 | return FIELD_GET(SOCINFO_MAJOR, socinfo); |
82 | } |
83 | |
84 | static inline unsigned int socinfo_to_minor(u32 socinfo) |
85 | { |
86 | return FIELD_GET(SOCINFO_MINOR, socinfo); |
87 | } |
88 | |
89 | static inline unsigned int socinfo_to_pack(u32 socinfo) |
90 | { |
91 | return FIELD_GET(SOCINFO_PACK, socinfo); |
92 | } |
93 | |
94 | static inline unsigned int socinfo_to_misc(u32 socinfo) |
95 | { |
96 | return FIELD_GET(SOCINFO_MISC, socinfo); |
97 | } |
98 | |
99 | static const char *socinfo_to_package_id(u32 socinfo) |
100 | { |
101 | unsigned int pack = socinfo_to_pack(socinfo); |
102 | unsigned int major = socinfo_to_major(socinfo); |
103 | int i; |
104 | |
105 | for (i = 0 ; i < ARRAY_SIZE(soc_packages) ; ++i) { |
106 | if (soc_packages[i].major_id == major && |
107 | soc_packages[i].pack_id == |
108 | (pack & soc_packages[i].pack_mask)) |
109 | return soc_packages[i].name; |
110 | } |
111 | |
112 | return "Unknown" ; |
113 | } |
114 | |
115 | static const char *socinfo_to_soc_id(u32 socinfo) |
116 | { |
117 | unsigned int id = socinfo_to_major(socinfo); |
118 | int i; |
119 | |
120 | for (i = 0 ; i < ARRAY_SIZE(soc_ids) ; ++i) { |
121 | if (soc_ids[i].id == id) |
122 | return soc_ids[i].name; |
123 | } |
124 | |
125 | return "Unknown" ; |
126 | } |
127 | |
128 | static int __init meson_gx_socinfo_init(void) |
129 | { |
130 | struct soc_device_attribute *soc_dev_attr; |
131 | struct soc_device *soc_dev; |
132 | struct device_node *np; |
133 | struct regmap *regmap; |
134 | unsigned int socinfo; |
135 | struct device *dev; |
136 | int ret; |
137 | |
138 | /* look up for chipid node */ |
139 | np = of_find_compatible_node(NULL, NULL, compat: "amlogic,meson-gx-ao-secure" ); |
140 | if (!np) |
141 | return -ENODEV; |
142 | |
143 | /* check if interface is enabled */ |
144 | if (!of_device_is_available(device: np)) { |
145 | of_node_put(node: np); |
146 | return -ENODEV; |
147 | } |
148 | |
149 | /* check if chip-id is available */ |
150 | if (!of_property_read_bool(np, propname: "amlogic,has-chip-id" )) { |
151 | of_node_put(node: np); |
152 | return -ENODEV; |
153 | } |
154 | |
155 | /* node should be a syscon */ |
156 | regmap = syscon_node_to_regmap(np); |
157 | of_node_put(node: np); |
158 | if (IS_ERR(ptr: regmap)) { |
159 | pr_err("%s: failed to get regmap\n" , __func__); |
160 | return -ENODEV; |
161 | } |
162 | |
163 | ret = regmap_read(map: regmap, AO_SEC_SOCINFO_OFFSET, val: &socinfo); |
164 | if (ret < 0) |
165 | return ret; |
166 | |
167 | if (!socinfo) { |
168 | pr_err("%s: invalid chipid value\n" , __func__); |
169 | return -EINVAL; |
170 | } |
171 | |
172 | soc_dev_attr = kzalloc(size: sizeof(*soc_dev_attr), GFP_KERNEL); |
173 | if (!soc_dev_attr) |
174 | return -ENODEV; |
175 | |
176 | soc_dev_attr->family = "Amlogic Meson" ; |
177 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, fmt: "%x:%x - %x:%x" , |
178 | socinfo_to_major(socinfo), |
179 | socinfo_to_minor(socinfo), |
180 | socinfo_to_pack(socinfo), |
181 | socinfo_to_misc(socinfo)); |
182 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, fmt: "%s (%s)" , |
183 | socinfo_to_soc_id(socinfo), |
184 | socinfo_to_package_id(socinfo)); |
185 | |
186 | soc_dev = soc_device_register(soc_plat_dev_attr: soc_dev_attr); |
187 | if (IS_ERR(ptr: soc_dev)) { |
188 | kfree(objp: soc_dev_attr->revision); |
189 | kfree_const(x: soc_dev_attr->soc_id); |
190 | kfree(objp: soc_dev_attr); |
191 | return PTR_ERR(ptr: soc_dev); |
192 | } |
193 | dev = soc_device_to_device(soc: soc_dev); |
194 | |
195 | dev_info(dev, "Amlogic Meson %s Revision %x:%x (%x:%x) Detected\n" , |
196 | soc_dev_attr->soc_id, |
197 | socinfo_to_major(socinfo), |
198 | socinfo_to_minor(socinfo), |
199 | socinfo_to_pack(socinfo), |
200 | socinfo_to_misc(socinfo)); |
201 | |
202 | return 0; |
203 | } |
204 | device_initcall(meson_gx_socinfo_init); |
205 | |