Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / soc / imx / soc-imx8.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2019 NXP.
4  */
5
6 #include <linux/init.h>
7 #include <linux/io.h>
8 #include <linux/of_address.h>
9 #include <linux/slab.h>
10 #include <linux/sys_soc.h>
11 #include <linux/platform_device.h>
12 #include <linux/of.h>
13
14 #define REV_B1                          0x21
15
16 #define IMX8MQ_SW_INFO_B1               0x40
17 #define IMX8MQ_SW_MAGIC_B1              0xff0055aa
18
19 /* Same as ANADIG_DIGPROG_IMX7D */
20 #define ANADIG_DIGPROG_IMX8MM   0x800
21
22 struct imx8_soc_data {
23         char *name;
24         u32 (*soc_revision)(void);
25 };
26
27 static u32 __init imx8mq_soc_revision(void)
28 {
29         struct device_node *np;
30         void __iomem *ocotp_base;
31         u32 magic;
32         u32 rev = 0;
33
34         np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp");
35         if (!np)
36                 goto out;
37
38         ocotp_base = of_iomap(np, 0);
39         WARN_ON(!ocotp_base);
40
41         magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1);
42         if (magic == IMX8MQ_SW_MAGIC_B1)
43                 rev = REV_B1;
44
45         iounmap(ocotp_base);
46
47 out:
48         of_node_put(np);
49         return rev;
50 }
51
52 static u32 __init imx8mm_soc_revision(void)
53 {
54         struct device_node *np;
55         void __iomem *anatop_base;
56         u32 rev;
57
58         np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop");
59         if (!np)
60                 return 0;
61
62         anatop_base = of_iomap(np, 0);
63         WARN_ON(!anatop_base);
64
65         rev = readl_relaxed(anatop_base + ANADIG_DIGPROG_IMX8MM);
66
67         iounmap(anatop_base);
68         of_node_put(np);
69         return rev;
70 }
71
72 static const struct imx8_soc_data imx8mq_soc_data = {
73         .name = "i.MX8MQ",
74         .soc_revision = imx8mq_soc_revision,
75 };
76
77 static const struct imx8_soc_data imx8mm_soc_data = {
78         .name = "i.MX8MM",
79         .soc_revision = imx8mm_soc_revision,
80 };
81
82 static const struct imx8_soc_data imx8mn_soc_data = {
83         .name = "i.MX8MN",
84         .soc_revision = imx8mm_soc_revision,
85 };
86
87 static const struct of_device_id imx8_soc_match[] = {
88         { .compatible = "fsl,imx8mq", .data = &imx8mq_soc_data, },
89         { .compatible = "fsl,imx8mm", .data = &imx8mm_soc_data, },
90         { .compatible = "fsl,imx8mn", .data = &imx8mn_soc_data, },
91         { }
92 };
93
94 #define imx8_revision(soc_rev) \
95         soc_rev ? \
96         kasprintf(GFP_KERNEL, "%d.%d", (soc_rev >> 4) & 0xf,  soc_rev & 0xf) : \
97         "unknown"
98
99 static int __init imx8_soc_init(void)
100 {
101         struct soc_device_attribute *soc_dev_attr;
102         struct soc_device *soc_dev;
103         const struct of_device_id *id;
104         u32 soc_rev = 0;
105         const struct imx8_soc_data *data;
106         int ret;
107
108         soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
109         if (!soc_dev_attr)
110                 return -ENOMEM;
111
112         soc_dev_attr->family = "Freescale i.MX";
113
114         ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
115         if (ret)
116                 goto free_soc;
117
118         id = of_match_node(imx8_soc_match, of_root);
119         if (!id) {
120                 ret = -ENODEV;
121                 goto free_soc;
122         }
123
124         data = id->data;
125         if (data) {
126                 soc_dev_attr->soc_id = data->name;
127                 if (data->soc_revision)
128                         soc_rev = data->soc_revision();
129         }
130
131         soc_dev_attr->revision = imx8_revision(soc_rev);
132         if (!soc_dev_attr->revision) {
133                 ret = -ENOMEM;
134                 goto free_soc;
135         }
136
137         soc_dev = soc_device_register(soc_dev_attr);
138         if (IS_ERR(soc_dev)) {
139                 ret = PTR_ERR(soc_dev);
140                 goto free_rev;
141         }
142
143         if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT))
144                 platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);
145
146         return 0;
147
148 free_rev:
149         if (strcmp(soc_dev_attr->revision, "unknown"))
150                 kfree(soc_dev_attr->revision);
151 free_soc:
152         kfree(soc_dev_attr);
153         return ret;
154 }
155 device_initcall(imx8_soc_init);