Commit | Line | Data |
---|---|---|
a9daaba2 NA |
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) | |
044d71bc AP |
23 | #define SOCINFO_PACK GENMASK(23, 16) |
24 | #define SOCINFO_MINOR GENMASK(15, 8) | |
a9daaba2 NA |
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 }, | |
f842c41a NA |
36 | { "TXLX", 0x24 }, |
37 | { "AXG", 0x25 }, | |
38 | { "GXLX", 0x26 }, | |
39 | { "TXHD", 0x27 }, | |
65f80df5 NA |
40 | { "G12A", 0x28 }, |
41 | { "G12B", 0x29 }, | |
a9daaba2 NA |
42 | }; |
43 | ||
44 | static const struct meson_gx_package_id { | |
45 | const char *name; | |
46 | unsigned int major_id; | |
47 | unsigned int pack_id; | |
dce47aed | 48 | unsigned int pack_mask; |
a9daaba2 | 49 | } soc_packages[] = { |
dce47aed NA |
50 | { "S905", 0x1f, 0, 0x20 }, /* pack_id != 0x20 */ |
51 | { "S905H", 0x1f, 0x3, 0xf }, /* pack_id & 0xf == 0x3 */ | |
52 | { "S905M", 0x1f, 0x20, 0xf0 }, /* pack_id == 0x20 */ | |
53 | { "S905D", 0x21, 0, 0xf0 }, | |
54 | { "S905X", 0x21, 0x80, 0xf0 }, | |
55 | { "S905W", 0x21, 0xa0, 0xf0 }, | |
56 | { "S905L", 0x21, 0xc0, 0xf0 }, | |
57 | { "S905M2", 0x21, 0xe0, 0xf0 }, | |
65f80df5 NA |
58 | { "S805X", 0x21, 0x30, 0xf0 }, |
59 | { "S805Y", 0x21, 0xb0, 0xf0 }, | |
dce47aed NA |
60 | { "S912", 0x22, 0, 0x0 }, /* Only S912 is known for GXM */ |
61 | { "962X", 0x24, 0x10, 0xf0 }, | |
62 | { "962E", 0x24, 0x20, 0xf0 }, | |
63 | { "A113X", 0x25, 0x37, 0xff }, | |
64 | { "A113D", 0x25, 0x22, 0xff }, | |
65f80df5 NA |
65 | { "S905D2", 0x28, 0x10, 0xf0 }, |
66 | { "S905X2", 0x28, 0x40, 0xf0 }, | |
67 | { "S922X", 0x29, 0x40, 0xf0 }, | |
a9daaba2 NA |
68 | }; |
69 | ||
70 | static inline unsigned int socinfo_to_major(u32 socinfo) | |
71 | { | |
72 | return FIELD_GET(SOCINFO_MAJOR, socinfo); | |
73 | } | |
74 | ||
75 | static inline unsigned int socinfo_to_minor(u32 socinfo) | |
76 | { | |
77 | return FIELD_GET(SOCINFO_MINOR, socinfo); | |
78 | } | |
79 | ||
80 | static inline unsigned int socinfo_to_pack(u32 socinfo) | |
81 | { | |
82 | return FIELD_GET(SOCINFO_PACK, socinfo); | |
83 | } | |
84 | ||
85 | static inline unsigned int socinfo_to_misc(u32 socinfo) | |
86 | { | |
87 | return FIELD_GET(SOCINFO_MISC, socinfo); | |
88 | } | |
89 | ||
90 | static const char *socinfo_to_package_id(u32 socinfo) | |
91 | { | |
dce47aed | 92 | unsigned int pack = socinfo_to_pack(socinfo); |
a9daaba2 NA |
93 | unsigned int major = socinfo_to_major(socinfo); |
94 | int i; | |
95 | ||
96 | for (i = 0 ; i < ARRAY_SIZE(soc_packages) ; ++i) { | |
97 | if (soc_packages[i].major_id == major && | |
dce47aed NA |
98 | soc_packages[i].pack_id == |
99 | (pack & soc_packages[i].pack_mask)) | |
a9daaba2 NA |
100 | return soc_packages[i].name; |
101 | } | |
102 | ||
103 | return "Unknown"; | |
104 | } | |
105 | ||
106 | static const char *socinfo_to_soc_id(u32 socinfo) | |
107 | { | |
108 | unsigned int id = socinfo_to_major(socinfo); | |
109 | int i; | |
110 | ||
111 | for (i = 0 ; i < ARRAY_SIZE(soc_ids) ; ++i) { | |
112 | if (soc_ids[i].id == id) | |
113 | return soc_ids[i].name; | |
114 | } | |
115 | ||
116 | return "Unknown"; | |
117 | } | |
118 | ||
01517dfc | 119 | static int __init meson_gx_socinfo_init(void) |
a9daaba2 NA |
120 | { |
121 | struct soc_device_attribute *soc_dev_attr; | |
122 | struct soc_device *soc_dev; | |
123 | struct device_node *np; | |
124 | struct regmap *regmap; | |
125 | unsigned int socinfo; | |
126 | struct device *dev; | |
127 | int ret; | |
128 | ||
129 | /* look up for chipid node */ | |
130 | np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gx-ao-secure"); | |
131 | if (!np) | |
132 | return -ENODEV; | |
133 | ||
134 | /* check if interface is enabled */ | |
fdda0a6a JL |
135 | if (!of_device_is_available(np)) { |
136 | of_node_put(np); | |
a9daaba2 | 137 | return -ENODEV; |
fdda0a6a | 138 | } |
a9daaba2 NA |
139 | |
140 | /* check if chip-id is available */ | |
141 | if (!of_property_read_bool(np, "amlogic,has-chip-id")) | |
142 | return -ENODEV; | |
143 | ||
144 | /* node should be a syscon */ | |
145 | regmap = syscon_node_to_regmap(np); | |
146 | of_node_put(np); | |
147 | if (IS_ERR(regmap)) { | |
148 | pr_err("%s: failed to get regmap\n", __func__); | |
149 | return -ENODEV; | |
150 | } | |
151 | ||
152 | ret = regmap_read(regmap, AO_SEC_SOCINFO_OFFSET, &socinfo); | |
153 | if (ret < 0) | |
154 | return ret; | |
155 | ||
156 | if (!socinfo) { | |
157 | pr_err("%s: invalid chipid value\n", __func__); | |
158 | return -EINVAL; | |
159 | } | |
160 | ||
161 | soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); | |
162 | if (!soc_dev_attr) | |
163 | return -ENODEV; | |
164 | ||
165 | soc_dev_attr->family = "Amlogic Meson"; | |
166 | ||
167 | np = of_find_node_by_path("/"); | |
168 | of_property_read_string(np, "model", &soc_dev_attr->machine); | |
169 | of_node_put(np); | |
170 | ||
171 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%x:%x - %x:%x", | |
172 | socinfo_to_major(socinfo), | |
173 | socinfo_to_minor(socinfo), | |
174 | socinfo_to_pack(socinfo), | |
175 | socinfo_to_misc(socinfo)); | |
176 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%s (%s)", | |
177 | socinfo_to_soc_id(socinfo), | |
178 | socinfo_to_package_id(socinfo)); | |
179 | ||
180 | soc_dev = soc_device_register(soc_dev_attr); | |
181 | if (IS_ERR(soc_dev)) { | |
182 | kfree(soc_dev_attr->revision); | |
183 | kfree_const(soc_dev_attr->soc_id); | |
184 | kfree(soc_dev_attr); | |
185 | return PTR_ERR(soc_dev); | |
186 | } | |
187 | dev = soc_device_to_device(soc_dev); | |
188 | ||
189 | dev_info(dev, "Amlogic Meson %s Revision %x:%x (%x:%x) Detected\n", | |
190 | soc_dev_attr->soc_id, | |
191 | socinfo_to_major(socinfo), | |
192 | socinfo_to_minor(socinfo), | |
193 | socinfo_to_pack(socinfo), | |
194 | socinfo_to_misc(socinfo)); | |
195 | ||
196 | return 0; | |
197 | } | |
198 | device_initcall(meson_gx_socinfo_init); |