1 | /* |
2 | * Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> |
3 | * |
4 | * SPDX-License-Identifier: GPL-2.0+ |
5 | */ |
6 | |
7 | #include <linux/io.h> |
8 | #include <linux/of.h> |
9 | #include <linux/of_address.h> |
10 | #include <linux/of_platform.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/sys_soc.h> |
14 | #include <linux/bitfield.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/mfd/syscon.h> |
17 | |
18 | #define MESON_SOCINFO_MAJOR_VER_MESON6 0x16 |
19 | #define MESON_SOCINFO_MAJOR_VER_MESON8 0x19 |
20 | #define MESON_SOCINFO_MAJOR_VER_MESON8B 0x1b |
21 | |
22 | #define MESON_MX_ASSIST_HW_REV 0x14c |
23 | |
24 | #define MESON_MX_ANALOG_TOP_METAL_REVISION 0x0 |
25 | |
26 | #define MESON_MX_BOOTROM_MISC_VER 0x4 |
27 | |
28 | static const char *meson_mx_socinfo_revision(unsigned int major_ver, |
29 | unsigned int misc_ver, |
30 | unsigned int metal_rev) |
31 | { |
32 | unsigned int minor_ver; |
33 | |
34 | switch (major_ver) { |
35 | case MESON_SOCINFO_MAJOR_VER_MESON6: |
36 | minor_ver = 0xa; |
37 | break; |
38 | |
39 | case MESON_SOCINFO_MAJOR_VER_MESON8: |
40 | if (metal_rev == 0x11111112) |
41 | major_ver = 0x1d; |
42 | |
43 | if (metal_rev == 0x11111111 || metal_rev == 0x11111112) |
44 | minor_ver = 0xa; |
45 | else if (metal_rev == 0x11111113) |
46 | minor_ver = 0xb; |
47 | else if (metal_rev == 0x11111133) |
48 | minor_ver = 0xc; |
49 | else |
50 | minor_ver = 0xd; |
51 | |
52 | break; |
53 | |
54 | case MESON_SOCINFO_MAJOR_VER_MESON8B: |
55 | if (metal_rev == 0x11111111) |
56 | minor_ver = 0xa; |
57 | else |
58 | minor_ver = 0xb; |
59 | |
60 | break; |
61 | |
62 | default: |
63 | minor_ver = 0x0; |
64 | break; |
65 | } |
66 | |
67 | return kasprintf(GFP_KERNEL, fmt: "Rev%X (%x - 0:%X)" , minor_ver, major_ver, |
68 | misc_ver); |
69 | } |
70 | |
71 | static const char *meson_mx_socinfo_soc_id(unsigned int major_ver, |
72 | unsigned int metal_rev) |
73 | { |
74 | const char *soc_id; |
75 | |
76 | switch (major_ver) { |
77 | case MESON_SOCINFO_MAJOR_VER_MESON6: |
78 | soc_id = "Meson6 (AML8726-MX)" ; |
79 | break; |
80 | |
81 | case MESON_SOCINFO_MAJOR_VER_MESON8: |
82 | if (metal_rev == 0x11111112) |
83 | soc_id = "Meson8m2 (S812)" ; |
84 | else |
85 | soc_id = "Meson8 (S802)" ; |
86 | |
87 | break; |
88 | |
89 | case MESON_SOCINFO_MAJOR_VER_MESON8B: |
90 | soc_id = "Meson8b (S805)" ; |
91 | break; |
92 | |
93 | default: |
94 | soc_id = "Unknown" ; |
95 | break; |
96 | } |
97 | |
98 | return kstrdup_const(s: soc_id, GFP_KERNEL); |
99 | } |
100 | |
101 | static const struct of_device_id meson_mx_socinfo_analog_top_ids[] = { |
102 | { .compatible = "amlogic,meson8-analog-top" , }, |
103 | { .compatible = "amlogic,meson8b-analog-top" , }, |
104 | { /* sentinel */ } |
105 | }; |
106 | |
107 | static int __init meson_mx_socinfo_init(void) |
108 | { |
109 | struct soc_device_attribute *soc_dev_attr; |
110 | struct soc_device *soc_dev; |
111 | struct device_node *np; |
112 | struct regmap *assist_regmap, *bootrom_regmap, *analog_top_regmap; |
113 | unsigned int major_ver, misc_ver, metal_rev = 0; |
114 | int ret; |
115 | |
116 | assist_regmap = |
117 | syscon_regmap_lookup_by_compatible(s: "amlogic,meson-mx-assist" ); |
118 | if (IS_ERR(ptr: assist_regmap)) |
119 | return PTR_ERR(ptr: assist_regmap); |
120 | |
121 | bootrom_regmap = |
122 | syscon_regmap_lookup_by_compatible(s: "amlogic,meson-mx-bootrom" ); |
123 | if (IS_ERR(ptr: bootrom_regmap)) |
124 | return PTR_ERR(ptr: bootrom_regmap); |
125 | |
126 | np = of_find_matching_node(NULL, matches: meson_mx_socinfo_analog_top_ids); |
127 | if (np) { |
128 | analog_top_regmap = syscon_node_to_regmap(np); |
129 | of_node_put(node: np); |
130 | if (IS_ERR(ptr: analog_top_regmap)) |
131 | return PTR_ERR(ptr: analog_top_regmap); |
132 | |
133 | ret = regmap_read(map: analog_top_regmap, |
134 | MESON_MX_ANALOG_TOP_METAL_REVISION, |
135 | val: &metal_rev); |
136 | if (ret) |
137 | return ret; |
138 | } |
139 | |
140 | ret = regmap_read(map: assist_regmap, MESON_MX_ASSIST_HW_REV, val: &major_ver); |
141 | if (ret < 0) |
142 | return ret; |
143 | |
144 | ret = regmap_read(map: bootrom_regmap, MESON_MX_BOOTROM_MISC_VER, |
145 | val: &misc_ver); |
146 | if (ret < 0) |
147 | return ret; |
148 | |
149 | soc_dev_attr = kzalloc(size: sizeof(*soc_dev_attr), GFP_KERNEL); |
150 | if (!soc_dev_attr) |
151 | return -ENODEV; |
152 | |
153 | soc_dev_attr->family = "Amlogic Meson" ; |
154 | |
155 | np = of_find_node_by_path(path: "/" ); |
156 | of_property_read_string(np, propname: "model" , out_string: &soc_dev_attr->machine); |
157 | of_node_put(node: np); |
158 | |
159 | soc_dev_attr->revision = meson_mx_socinfo_revision(major_ver, misc_ver, |
160 | metal_rev); |
161 | soc_dev_attr->soc_id = meson_mx_socinfo_soc_id(major_ver, metal_rev); |
162 | |
163 | soc_dev = soc_device_register(soc_plat_dev_attr: soc_dev_attr); |
164 | if (IS_ERR(ptr: soc_dev)) { |
165 | kfree_const(x: soc_dev_attr->revision); |
166 | kfree_const(x: soc_dev_attr->soc_id); |
167 | kfree(objp: soc_dev_attr); |
168 | return PTR_ERR(ptr: soc_dev); |
169 | } |
170 | |
171 | dev_info(soc_device_to_device(soc_dev), "Amlogic %s %s detected\n" , |
172 | soc_dev_attr->soc_id, soc_dev_attr->revision); |
173 | |
174 | return 0; |
175 | } |
176 | device_initcall(meson_mx_socinfo_init); |
177 | |