]>
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 }, | |
36 | }; | |
37 | ||
38 | static const struct meson_gx_package_id { | |
39 | const char *name; | |
40 | unsigned int major_id; | |
41 | unsigned int pack_id; | |
42 | } soc_packages[] = { | |
43 | { "S905", 0x1f, 0 }, | |
44 | { "S905M", 0x1f, 0x20 }, | |
45 | { "S905D", 0x21, 0 }, | |
46 | { "S905X", 0x21, 0x80 }, | |
47 | { "S905L", 0x21, 0xc0 }, | |
48 | { "S905M2", 0x21, 0xe0 }, | |
49 | { "S912", 0x22, 0 }, | |
50 | }; | |
51 | ||
52 | static inline unsigned int socinfo_to_major(u32 socinfo) | |
53 | { | |
54 | return FIELD_GET(SOCINFO_MAJOR, socinfo); | |
55 | } | |
56 | ||
57 | static inline unsigned int socinfo_to_minor(u32 socinfo) | |
58 | { | |
59 | return FIELD_GET(SOCINFO_MINOR, socinfo); | |
60 | } | |
61 | ||
62 | static inline unsigned int socinfo_to_pack(u32 socinfo) | |
63 | { | |
64 | return FIELD_GET(SOCINFO_PACK, socinfo); | |
65 | } | |
66 | ||
67 | static inline unsigned int socinfo_to_misc(u32 socinfo) | |
68 | { | |
69 | return FIELD_GET(SOCINFO_MISC, socinfo); | |
70 | } | |
71 | ||
72 | static const char *socinfo_to_package_id(u32 socinfo) | |
73 | { | |
74 | unsigned int pack = socinfo_to_pack(socinfo) & 0xf0; | |
75 | unsigned int major = socinfo_to_major(socinfo); | |
76 | int i; | |
77 | ||
78 | for (i = 0 ; i < ARRAY_SIZE(soc_packages) ; ++i) { | |
79 | if (soc_packages[i].major_id == major && | |
80 | soc_packages[i].pack_id == pack) | |
81 | return soc_packages[i].name; | |
82 | } | |
83 | ||
84 | return "Unknown"; | |
85 | } | |
86 | ||
87 | static const char *socinfo_to_soc_id(u32 socinfo) | |
88 | { | |
89 | unsigned int id = socinfo_to_major(socinfo); | |
90 | int i; | |
91 | ||
92 | for (i = 0 ; i < ARRAY_SIZE(soc_ids) ; ++i) { | |
93 | if (soc_ids[i].id == id) | |
94 | return soc_ids[i].name; | |
95 | } | |
96 | ||
97 | return "Unknown"; | |
98 | } | |
99 | ||
100 | int __init meson_gx_socinfo_init(void) | |
101 | { | |
102 | struct soc_device_attribute *soc_dev_attr; | |
103 | struct soc_device *soc_dev; | |
104 | struct device_node *np; | |
105 | struct regmap *regmap; | |
106 | unsigned int socinfo; | |
107 | struct device *dev; | |
108 | int ret; | |
109 | ||
110 | /* look up for chipid node */ | |
111 | np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gx-ao-secure"); | |
112 | if (!np) | |
113 | return -ENODEV; | |
114 | ||
115 | /* check if interface is enabled */ | |
116 | if (!of_device_is_available(np)) | |
117 | return -ENODEV; | |
118 | ||
119 | /* check if chip-id is available */ | |
120 | if (!of_property_read_bool(np, "amlogic,has-chip-id")) | |
121 | return -ENODEV; | |
122 | ||
123 | /* node should be a syscon */ | |
124 | regmap = syscon_node_to_regmap(np); | |
125 | of_node_put(np); | |
126 | if (IS_ERR(regmap)) { | |
127 | pr_err("%s: failed to get regmap\n", __func__); | |
128 | return -ENODEV; | |
129 | } | |
130 | ||
131 | ret = regmap_read(regmap, AO_SEC_SOCINFO_OFFSET, &socinfo); | |
132 | if (ret < 0) | |
133 | return ret; | |
134 | ||
135 | if (!socinfo) { | |
136 | pr_err("%s: invalid chipid value\n", __func__); | |
137 | return -EINVAL; | |
138 | } | |
139 | ||
140 | soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); | |
141 | if (!soc_dev_attr) | |
142 | return -ENODEV; | |
143 | ||
144 | soc_dev_attr->family = "Amlogic Meson"; | |
145 | ||
146 | np = of_find_node_by_path("/"); | |
147 | of_property_read_string(np, "model", &soc_dev_attr->machine); | |
148 | of_node_put(np); | |
149 | ||
150 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%x:%x - %x:%x", | |
151 | socinfo_to_major(socinfo), | |
152 | socinfo_to_minor(socinfo), | |
153 | socinfo_to_pack(socinfo), | |
154 | socinfo_to_misc(socinfo)); | |
155 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%s (%s)", | |
156 | socinfo_to_soc_id(socinfo), | |
157 | socinfo_to_package_id(socinfo)); | |
158 | ||
159 | soc_dev = soc_device_register(soc_dev_attr); | |
160 | if (IS_ERR(soc_dev)) { | |
161 | kfree(soc_dev_attr->revision); | |
162 | kfree_const(soc_dev_attr->soc_id); | |
163 | kfree(soc_dev_attr); | |
164 | return PTR_ERR(soc_dev); | |
165 | } | |
166 | dev = soc_device_to_device(soc_dev); | |
167 | ||
168 | dev_info(dev, "Amlogic Meson %s Revision %x:%x (%x:%x) Detected\n", | |
169 | soc_dev_attr->soc_id, | |
170 | socinfo_to_major(socinfo), | |
171 | socinfo_to_minor(socinfo), | |
172 | socinfo_to_pack(socinfo), | |
173 | socinfo_to_misc(socinfo)); | |
174 | ||
175 | return 0; | |
176 | } | |
177 | device_initcall(meson_gx_socinfo_init); |