1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2014 Linaro Ltd. |
4 | * |
5 | * Author: Linus Walleij <linus.walleij@linaro.org> |
6 | */ |
7 | #include <linux/init.h> |
8 | #include <linux/io.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/sys_soc.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/mfd/syscon.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/of.h> |
15 | |
16 | #define INTEGRATOR_HDR_ID_OFFSET 0x00 |
17 | |
18 | static u32 integrator_coreid; |
19 | |
20 | static const struct of_device_id integrator_cm_match[] = { |
21 | { .compatible = "arm,core-module-integrator" , }, |
22 | { } |
23 | }; |
24 | |
25 | static const char *integrator_arch_str(u32 id) |
26 | { |
27 | switch ((id >> 16) & 0xff) { |
28 | case 0x00: |
29 | return "ASB little-endian" ; |
30 | case 0x01: |
31 | return "AHB little-endian" ; |
32 | case 0x03: |
33 | return "AHB-Lite system bus, bi-endian" ; |
34 | case 0x04: |
35 | return "AHB" ; |
36 | case 0x08: |
37 | return "AHB system bus, ASB processor bus" ; |
38 | default: |
39 | return "Unknown" ; |
40 | } |
41 | } |
42 | |
43 | static const char *integrator_fpga_str(u32 id) |
44 | { |
45 | switch ((id >> 12) & 0xf) { |
46 | case 0x01: |
47 | return "XC4062" ; |
48 | case 0x02: |
49 | return "XC4085" ; |
50 | case 0x03: |
51 | return "XVC600" ; |
52 | case 0x04: |
53 | return "EPM7256AE (Altera PLD)" ; |
54 | default: |
55 | return "Unknown" ; |
56 | } |
57 | } |
58 | |
59 | static ssize_t |
60 | manufacturer_show(struct device *dev, struct device_attribute *attr, char *buf) |
61 | { |
62 | return sprintf(buf, fmt: "%02x\n" , integrator_coreid >> 24); |
63 | } |
64 | |
65 | static DEVICE_ATTR_RO(manufacturer); |
66 | |
67 | static ssize_t |
68 | arch_show(struct device *dev, struct device_attribute *attr, char *buf) |
69 | { |
70 | return sprintf(buf, fmt: "%s\n" , integrator_arch_str(id: integrator_coreid)); |
71 | } |
72 | |
73 | static DEVICE_ATTR_RO(arch); |
74 | |
75 | static ssize_t |
76 | fpga_show(struct device *dev, struct device_attribute *attr, char *buf) |
77 | { |
78 | return sprintf(buf, fmt: "%s\n" , integrator_fpga_str(id: integrator_coreid)); |
79 | } |
80 | |
81 | static DEVICE_ATTR_RO(fpga); |
82 | |
83 | static ssize_t |
84 | build_show(struct device *dev, struct device_attribute *attr, char *buf) |
85 | { |
86 | return sprintf(buf, fmt: "%02x\n" , (integrator_coreid >> 4) & 0xFF); |
87 | } |
88 | |
89 | static DEVICE_ATTR_RO(build); |
90 | |
91 | static struct attribute *integrator_attrs[] = { |
92 | &dev_attr_manufacturer.attr, |
93 | &dev_attr_arch.attr, |
94 | &dev_attr_fpga.attr, |
95 | &dev_attr_build.attr, |
96 | NULL |
97 | }; |
98 | |
99 | ATTRIBUTE_GROUPS(integrator); |
100 | |
101 | static int __init integrator_soc_init(void) |
102 | { |
103 | struct regmap *syscon_regmap; |
104 | struct soc_device *soc_dev; |
105 | struct soc_device_attribute *soc_dev_attr; |
106 | struct device_node *np; |
107 | struct device *dev; |
108 | u32 val; |
109 | int ret; |
110 | |
111 | np = of_find_matching_node(NULL, matches: integrator_cm_match); |
112 | if (!np) |
113 | return -ENODEV; |
114 | |
115 | syscon_regmap = syscon_node_to_regmap(np); |
116 | if (IS_ERR(ptr: syscon_regmap)) |
117 | return PTR_ERR(ptr: syscon_regmap); |
118 | |
119 | ret = regmap_read(map: syscon_regmap, INTEGRATOR_HDR_ID_OFFSET, |
120 | val: &val); |
121 | if (ret) |
122 | return -ENODEV; |
123 | integrator_coreid = val; |
124 | |
125 | soc_dev_attr = kzalloc(size: sizeof(*soc_dev_attr), GFP_KERNEL); |
126 | if (!soc_dev_attr) |
127 | return -ENOMEM; |
128 | |
129 | soc_dev_attr->soc_id = "Integrator" ; |
130 | soc_dev_attr->machine = "Integrator" ; |
131 | soc_dev_attr->family = "Versatile" ; |
132 | soc_dev_attr->custom_attr_group = integrator_groups[0]; |
133 | soc_dev = soc_device_register(soc_plat_dev_attr: soc_dev_attr); |
134 | if (IS_ERR(ptr: soc_dev)) { |
135 | kfree(objp: soc_dev_attr); |
136 | return -ENODEV; |
137 | } |
138 | dev = soc_device_to_device(soc: soc_dev); |
139 | |
140 | dev_info(dev, "Detected ARM core module:\n" ); |
141 | dev_info(dev, " Manufacturer: %02x\n" , (val >> 24)); |
142 | dev_info(dev, " Architecture: %s\n" , integrator_arch_str(val)); |
143 | dev_info(dev, " FPGA: %s\n" , integrator_fpga_str(val)); |
144 | dev_info(dev, " Build: %02x\n" , (val >> 4) & 0xFF); |
145 | dev_info(dev, " Rev: %c\n" , ('A' + (val & 0x03))); |
146 | |
147 | return 0; |
148 | } |
149 | device_initcall(integrator_soc_init); |
150 | |