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