b1bd8e2543ac529b1c581c4617e192bf89bca1a0
[linux-2.6-microblaze.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 struct imx8_soc_data {
20         char *name;
21         u32 (*soc_revision)(void);
22 };
23
24 static u32 __init imx8mq_soc_revision(void)
25 {
26         struct device_node *np;
27         void __iomem *ocotp_base;
28         u32 magic;
29         u32 rev = 0;
30
31         np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp");
32         if (!np)
33                 goto out;
34
35         ocotp_base = of_iomap(np, 0);
36         WARN_ON(!ocotp_base);
37
38         magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1);
39         if (magic == IMX8MQ_SW_MAGIC_B1)
40                 rev = REV_B1;
41
42         iounmap(ocotp_base);
43
44 out:
45         of_node_put(np);
46         return rev;
47 }
48
49 static const struct imx8_soc_data imx8mq_soc_data = {
50         .name = "i.MX8MQ",
51         .soc_revision = imx8mq_soc_revision,
52 };
53
54 static const struct of_device_id imx8_soc_match[] = {
55         { .compatible = "fsl,imx8mq", .data = &imx8mq_soc_data, },
56         { }
57 };
58
59 #define imx8_revision(soc_rev) \
60         soc_rev ? \
61         kasprintf(GFP_KERNEL, "%d.%d", (soc_rev >> 4) & 0xf,  soc_rev & 0xf) : \
62         "unknown"
63
64 static int __init imx8_soc_init(void)
65 {
66         struct soc_device_attribute *soc_dev_attr;
67         struct soc_device *soc_dev;
68         struct device_node *root;
69         const struct of_device_id *id;
70         u32 soc_rev = 0;
71         const struct imx8_soc_data *data;
72         int ret;
73
74         soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
75         if (!soc_dev_attr)
76                 return -ENODEV;
77
78         soc_dev_attr->family = "Freescale i.MX";
79
80         root = of_find_node_by_path("/");
81         ret = of_property_read_string(root, "model", &soc_dev_attr->machine);
82         if (ret)
83                 goto free_soc;
84
85         id = of_match_node(imx8_soc_match, root);
86         if (!id)
87                 goto free_soc;
88
89         of_node_put(root);
90
91         data = id->data;
92         if (data) {
93                 soc_dev_attr->soc_id = data->name;
94                 if (data->soc_revision)
95                         soc_rev = data->soc_revision();
96         }
97
98         soc_dev_attr->revision = imx8_revision(soc_rev);
99         if (!soc_dev_attr->revision)
100                 goto free_soc;
101
102         soc_dev = soc_device_register(soc_dev_attr);
103         if (IS_ERR(soc_dev))
104                 goto free_rev;
105
106         if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT))
107                 platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);
108
109         return 0;
110
111 free_rev:
112         kfree(soc_dev_attr->revision);
113 free_soc:
114         kfree(soc_dev_attr);
115         of_node_put(root);
116         return -ENODEV;
117 }
118 device_initcall(imx8_soc_init);