1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * TI K3 SoC info driver |
4 | * |
5 | * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com |
6 | */ |
7 | |
8 | #include <linux/mfd/syscon.h> |
9 | #include <linux/of.h> |
10 | #include <linux/of_address.h> |
11 | #include <linux/regmap.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/string.h> |
15 | #include <linux/sys_soc.h> |
16 | |
17 | #define CTRLMMR_WKUP_JTAGID_REG 0 |
18 | /* |
19 | * Bits: |
20 | * 31-28 VARIANT Device variant |
21 | * 27-12 PARTNO Part number |
22 | * 11-1 MFG Indicates TI as manufacturer (0x17) |
23 | * 0 Always 1 |
24 | */ |
25 | #define CTRLMMR_WKUP_JTAGID_VARIANT_SHIFT (28) |
26 | #define CTRLMMR_WKUP_JTAGID_VARIANT_MASK GENMASK(31, 28) |
27 | |
28 | #define CTRLMMR_WKUP_JTAGID_PARTNO_SHIFT (12) |
29 | #define CTRLMMR_WKUP_JTAGID_PARTNO_MASK GENMASK(27, 12) |
30 | |
31 | #define CTRLMMR_WKUP_JTAGID_MFG_SHIFT (1) |
32 | #define CTRLMMR_WKUP_JTAGID_MFG_MASK GENMASK(11, 1) |
33 | |
34 | #define CTRLMMR_WKUP_JTAGID_MFG_TI 0x17 |
35 | |
36 | #define JTAG_ID_PARTNO_AM65X 0xBB5A |
37 | #define JTAG_ID_PARTNO_J721E 0xBB64 |
38 | #define JTAG_ID_PARTNO_J7200 0xBB6D |
39 | #define JTAG_ID_PARTNO_AM64X 0xBB38 |
40 | #define JTAG_ID_PARTNO_J721S2 0xBB75 |
41 | #define JTAG_ID_PARTNO_AM62X 0xBB7E |
42 | #define JTAG_ID_PARTNO_J784S4 0xBB80 |
43 | #define JTAG_ID_PARTNO_AM62AX 0xBB8D |
44 | #define JTAG_ID_PARTNO_AM62PX 0xBB9D |
45 | #define JTAG_ID_PARTNO_J722S 0xBBA0 |
46 | |
47 | static const struct k3_soc_id { |
48 | unsigned int id; |
49 | const char *family_name; |
50 | } k3_soc_ids[] = { |
51 | { JTAG_ID_PARTNO_AM65X, "AM65X" }, |
52 | { JTAG_ID_PARTNO_J721E, "J721E" }, |
53 | { JTAG_ID_PARTNO_J7200, "J7200" }, |
54 | { JTAG_ID_PARTNO_AM64X, "AM64X" }, |
55 | { JTAG_ID_PARTNO_J721S2, "J721S2" }, |
56 | { JTAG_ID_PARTNO_AM62X, "AM62X" }, |
57 | { JTAG_ID_PARTNO_J784S4, "J784S4" }, |
58 | { JTAG_ID_PARTNO_AM62AX, "AM62AX" }, |
59 | { JTAG_ID_PARTNO_AM62PX, "AM62PX" }, |
60 | { JTAG_ID_PARTNO_J722S, "J722S" }, |
61 | }; |
62 | |
63 | static const char * const j721e_rev_string_map[] = { |
64 | "1.0" , "1.1" , |
65 | }; |
66 | |
67 | static int |
68 | k3_chipinfo_partno_to_names(unsigned int partno, |
69 | struct soc_device_attribute *soc_dev_attr) |
70 | { |
71 | int i; |
72 | |
73 | for (i = 0; i < ARRAY_SIZE(k3_soc_ids); i++) |
74 | if (partno == k3_soc_ids[i].id) { |
75 | soc_dev_attr->family = k3_soc_ids[i].family_name; |
76 | return 0; |
77 | } |
78 | |
79 | return -ENODEV; |
80 | } |
81 | |
82 | static int |
83 | k3_chipinfo_variant_to_sr(unsigned int partno, unsigned int variant, |
84 | struct soc_device_attribute *soc_dev_attr) |
85 | { |
86 | switch (partno) { |
87 | case JTAG_ID_PARTNO_J721E: |
88 | if (variant >= ARRAY_SIZE(j721e_rev_string_map)) |
89 | goto err_unknown_variant; |
90 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, fmt: "SR%s" , |
91 | j721e_rev_string_map[variant]); |
92 | break; |
93 | default: |
94 | variant++; |
95 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, fmt: "SR%x.0" , |
96 | variant); |
97 | } |
98 | |
99 | if (!soc_dev_attr->revision) |
100 | return -ENOMEM; |
101 | |
102 | return 0; |
103 | |
104 | err_unknown_variant: |
105 | return -ENODEV; |
106 | } |
107 | |
108 | static int k3_chipinfo_probe(struct platform_device *pdev) |
109 | { |
110 | struct device_node *node = pdev->dev.of_node; |
111 | struct soc_device_attribute *soc_dev_attr; |
112 | struct device *dev = &pdev->dev; |
113 | struct soc_device *soc_dev; |
114 | struct regmap *regmap; |
115 | u32 partno_id; |
116 | u32 variant; |
117 | u32 jtag_id; |
118 | u32 mfg; |
119 | int ret; |
120 | |
121 | regmap = device_node_to_regmap(np: node); |
122 | if (IS_ERR(ptr: regmap)) |
123 | return PTR_ERR(ptr: regmap); |
124 | |
125 | ret = regmap_read(map: regmap, CTRLMMR_WKUP_JTAGID_REG, val: &jtag_id); |
126 | if (ret < 0) |
127 | return ret; |
128 | |
129 | mfg = (jtag_id & CTRLMMR_WKUP_JTAGID_MFG_MASK) >> |
130 | CTRLMMR_WKUP_JTAGID_MFG_SHIFT; |
131 | |
132 | if (mfg != CTRLMMR_WKUP_JTAGID_MFG_TI) { |
133 | dev_err(dev, "Invalid MFG SoC\n" ); |
134 | return -ENODEV; |
135 | } |
136 | |
137 | variant = (jtag_id & CTRLMMR_WKUP_JTAGID_VARIANT_MASK) >> |
138 | CTRLMMR_WKUP_JTAGID_VARIANT_SHIFT; |
139 | |
140 | partno_id = (jtag_id & CTRLMMR_WKUP_JTAGID_PARTNO_MASK) >> |
141 | CTRLMMR_WKUP_JTAGID_PARTNO_SHIFT; |
142 | |
143 | soc_dev_attr = kzalloc(size: sizeof(*soc_dev_attr), GFP_KERNEL); |
144 | if (!soc_dev_attr) |
145 | return -ENOMEM; |
146 | |
147 | ret = k3_chipinfo_partno_to_names(partno: partno_id, soc_dev_attr); |
148 | if (ret) { |
149 | dev_err(dev, "Unknown SoC JTAGID[0x%08X]: %d\n" , jtag_id, ret); |
150 | goto err; |
151 | } |
152 | |
153 | ret = k3_chipinfo_variant_to_sr(partno: partno_id, variant, soc_dev_attr); |
154 | if (ret) { |
155 | dev_err(dev, "Unknown SoC SR[0x%08X]: %d\n" , jtag_id, ret); |
156 | goto err; |
157 | } |
158 | |
159 | node = of_find_node_by_path(path: "/" ); |
160 | of_property_read_string(np: node, propname: "model" , out_string: &soc_dev_attr->machine); |
161 | of_node_put(node); |
162 | |
163 | soc_dev = soc_device_register(soc_plat_dev_attr: soc_dev_attr); |
164 | if (IS_ERR(ptr: soc_dev)) { |
165 | ret = PTR_ERR(ptr: soc_dev); |
166 | goto err_free_rev; |
167 | } |
168 | |
169 | dev_info(dev, "Family:%s rev:%s JTAGID[0x%08x] Detected\n" , |
170 | soc_dev_attr->family, |
171 | soc_dev_attr->revision, jtag_id); |
172 | |
173 | return 0; |
174 | |
175 | err_free_rev: |
176 | kfree(objp: soc_dev_attr->revision); |
177 | err: |
178 | kfree(objp: soc_dev_attr); |
179 | return ret; |
180 | } |
181 | |
182 | static const struct of_device_id k3_chipinfo_of_match[] = { |
183 | { .compatible = "ti,am654-chipid" , }, |
184 | { /* sentinel */ }, |
185 | }; |
186 | |
187 | static struct platform_driver k3_chipinfo_driver = { |
188 | .driver = { |
189 | .name = "k3-chipinfo" , |
190 | .of_match_table = k3_chipinfo_of_match, |
191 | }, |
192 | .probe = k3_chipinfo_probe, |
193 | }; |
194 | |
195 | static int __init k3_chipinfo_init(void) |
196 | { |
197 | return platform_driver_register(&k3_chipinfo_driver); |
198 | } |
199 | subsys_initcall(k3_chipinfo_init); |
200 | |