diff options
-rw-r--r-- | drivers/soc/imx/Kconfig | 9 | ||||
-rw-r--r-- | drivers/soc/imx/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/imx/soc-imx-scu.c | 144 | ||||
-rw-r--r-- | drivers/soc/imx/soc-imx8.c | 63 |
4 files changed, 204 insertions, 13 deletions
diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig index ade1b46d669c..8aaebf13e2e6 100644 --- a/drivers/soc/imx/Kconfig +++ b/drivers/soc/imx/Kconfig | |||
@@ -8,4 +8,13 @@ config IMX_GPCV2_PM_DOMAINS | |||
8 | select PM_GENERIC_DOMAINS | 8 | select PM_GENERIC_DOMAINS |
9 | default y if SOC_IMX7D | 9 | default y if SOC_IMX7D |
10 | 10 | ||
11 | config IMX_SCU_SOC | ||
12 | bool "i.MX System Controller Unit SoC info support" | ||
13 | depends on IMX_SCU | ||
14 | select SOC_BUS | ||
15 | help | ||
16 | If you say yes here you get support for the NXP i.MX System | ||
17 | Controller Unit SoC info module, it will provide the SoC info | ||
18 | like SoC family, ID and revision etc. | ||
19 | |||
11 | endmenu | 20 | endmenu |
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile index caa8653600f2..cf9ca42ff739 100644 --- a/drivers/soc/imx/Makefile +++ b/drivers/soc/imx/Makefile | |||
@@ -2,3 +2,4 @@ | |||
2 | obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o | 2 | obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o |
3 | obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o | 3 | obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o |
4 | obj-$(CONFIG_ARCH_MXC) += soc-imx8.o | 4 | obj-$(CONFIG_ARCH_MXC) += soc-imx8.o |
5 | obj-$(CONFIG_IMX_SCU_SOC) += soc-imx-scu.o | ||
diff --git a/drivers/soc/imx/soc-imx-scu.c b/drivers/soc/imx/soc-imx-scu.c new file mode 100644 index 000000000000..676f612f6488 --- /dev/null +++ b/drivers/soc/imx/soc-imx-scu.c | |||
@@ -0,0 +1,144 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright 2019 NXP. | ||
4 | */ | ||
5 | |||
6 | #include <dt-bindings/firmware/imx/rsrc.h> | ||
7 | #include <linux/firmware/imx/sci.h> | ||
8 | #include <linux/slab.h> | ||
9 | #include <linux/sys_soc.h> | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/of.h> | ||
12 | |||
13 | #define IMX_SCU_SOC_DRIVER_NAME "imx-scu-soc" | ||
14 | |||
15 | static struct imx_sc_ipc *soc_ipc_handle; | ||
16 | |||
17 | struct imx_sc_msg_misc_get_soc_id { | ||
18 | struct imx_sc_rpc_msg hdr; | ||
19 | union { | ||
20 | struct { | ||
21 | u32 control; | ||
22 | u16 resource; | ||
23 | } __packed req; | ||
24 | struct { | ||
25 | u32 id; | ||
26 | } resp; | ||
27 | } data; | ||
28 | } __packed; | ||
29 | |||
30 | static int imx_scu_soc_id(void) | ||
31 | { | ||
32 | struct imx_sc_msg_misc_get_soc_id msg; | ||
33 | struct imx_sc_rpc_msg *hdr = &msg.hdr; | ||
34 | int ret; | ||
35 | |||
36 | hdr->ver = IMX_SC_RPC_VERSION; | ||
37 | hdr->svc = IMX_SC_RPC_SVC_MISC; | ||
38 | hdr->func = IMX_SC_MISC_FUNC_GET_CONTROL; | ||
39 | hdr->size = 3; | ||
40 | |||
41 | msg.data.req.control = IMX_SC_C_ID; | ||
42 | msg.data.req.resource = IMX_SC_R_SYSTEM; | ||
43 | |||
44 | ret = imx_scu_call_rpc(soc_ipc_handle, &msg, true); | ||
45 | if (ret) { | ||
46 | pr_err("%s: get soc info failed, ret %d\n", __func__, ret); | ||
47 | return ret; | ||
48 | } | ||
49 | |||
50 | return msg.data.resp.id; | ||
51 | } | ||
52 | |||
53 | static int imx_scu_soc_probe(struct platform_device *pdev) | ||
54 | { | ||
55 | struct soc_device_attribute *soc_dev_attr; | ||
56 | struct soc_device *soc_dev; | ||
57 | int id, ret; | ||
58 | u32 val; | ||
59 | |||
60 | ret = imx_scu_get_handle(&soc_ipc_handle); | ||
61 | if (ret) | ||
62 | return ret; | ||
63 | |||
64 | soc_dev_attr = devm_kzalloc(&pdev->dev, sizeof(*soc_dev_attr), | ||
65 | GFP_KERNEL); | ||
66 | if (!soc_dev_attr) | ||
67 | return -ENOMEM; | ||
68 | |||
69 | soc_dev_attr->family = "Freescale i.MX"; | ||
70 | |||
71 | ret = of_property_read_string(of_root, | ||
72 | "model", | ||
73 | &soc_dev_attr->machine); | ||
74 | if (ret) | ||
75 | return ret; | ||
76 | |||
77 | id = imx_scu_soc_id(); | ||
78 | if (id < 0) | ||
79 | return -EINVAL; | ||
80 | |||
81 | /* format soc_id value passed from SCU firmware */ | ||
82 | val = id & 0x1f; | ||
83 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x", val); | ||
84 | if (!soc_dev_attr->soc_id) | ||
85 | return -ENOMEM; | ||
86 | |||
87 | /* format revision value passed from SCU firmware */ | ||
88 | val = (id >> 5) & 0xf; | ||
89 | val = (((val >> 2) + 1) << 4) | (val & 0x3); | ||
90 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, | ||
91 | "%d.%d", | ||
92 | (val >> 4) & 0xf, | ||
93 | val & 0xf); | ||
94 | if (!soc_dev_attr->revision) { | ||
95 | ret = -ENOMEM; | ||
96 | goto free_soc_id; | ||
97 | } | ||
98 | |||
99 | soc_dev = soc_device_register(soc_dev_attr); | ||
100 | if (IS_ERR(soc_dev)) { | ||
101 | ret = PTR_ERR(soc_dev); | ||
102 | goto free_revision; | ||
103 | } | ||
104 | |||
105 | return 0; | ||
106 | |||
107 | free_revision: | ||
108 | kfree(soc_dev_attr->revision); | ||
109 | free_soc_id: | ||
110 | kfree(soc_dev_attr->soc_id); | ||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | static struct platform_driver imx_scu_soc_driver = { | ||
115 | .driver = { | ||
116 | .name = IMX_SCU_SOC_DRIVER_NAME, | ||
117 | }, | ||
118 | .probe = imx_scu_soc_probe, | ||
119 | }; | ||
120 | |||
121 | static int __init imx_scu_soc_init(void) | ||
122 | { | ||
123 | struct platform_device *pdev; | ||
124 | struct device_node *np; | ||
125 | int ret; | ||
126 | |||
127 | np = of_find_compatible_node(NULL, NULL, "fsl,imx-scu"); | ||
128 | if (!np) | ||
129 | return -ENODEV; | ||
130 | |||
131 | of_node_put(np); | ||
132 | |||
133 | ret = platform_driver_register(&imx_scu_soc_driver); | ||
134 | if (ret) | ||
135 | return ret; | ||
136 | |||
137 | pdev = platform_device_register_simple(IMX_SCU_SOC_DRIVER_NAME, | ||
138 | -1, NULL, 0); | ||
139 | if (IS_ERR(pdev)) | ||
140 | platform_driver_unregister(&imx_scu_soc_driver); | ||
141 | |||
142 | return PTR_ERR_OR_ZERO(pdev); | ||
143 | } | ||
144 | device_initcall(imx_scu_soc_init); | ||
diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c index fc6429f9170a..9fb5293469f8 100644 --- a/drivers/soc/imx/soc-imx8.c +++ b/drivers/soc/imx/soc-imx8.c | |||
@@ -16,6 +16,9 @@ | |||
16 | #define IMX8MQ_SW_INFO_B1 0x40 | 16 | #define IMX8MQ_SW_INFO_B1 0x40 |
17 | #define IMX8MQ_SW_MAGIC_B1 0xff0055aa | 17 | #define IMX8MQ_SW_MAGIC_B1 0xff0055aa |
18 | 18 | ||
19 | /* Same as ANADIG_DIGPROG_IMX7D */ | ||
20 | #define ANADIG_DIGPROG_IMX8MM 0x800 | ||
21 | |||
19 | struct imx8_soc_data { | 22 | struct imx8_soc_data { |
20 | char *name; | 23 | char *name; |
21 | u32 (*soc_revision)(void); | 24 | u32 (*soc_revision)(void); |
@@ -46,13 +49,45 @@ out: | |||
46 | return rev; | 49 | return rev; |
47 | } | 50 | } |
48 | 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 | |||
49 | static const struct imx8_soc_data imx8mq_soc_data = { | 72 | static const struct imx8_soc_data imx8mq_soc_data = { |
50 | .name = "i.MX8MQ", | 73 | .name = "i.MX8MQ", |
51 | .soc_revision = imx8mq_soc_revision, | 74 | .soc_revision = imx8mq_soc_revision, |
52 | }; | 75 | }; |
53 | 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 | |||
54 | static const struct of_device_id imx8_soc_match[] = { | 87 | static const struct of_device_id imx8_soc_match[] = { |
55 | { .compatible = "fsl,imx8mq", .data = &imx8mq_soc_data, }, | 88 | { .compatible = "fsl,imx8mq", .data = &imx8mq_soc_data, }, |
89 | { .compatible = "fsl,imx8mm", .data = &imx8mm_soc_data, }, | ||
90 | { .compatible = "fsl,imx8mn", .data = &imx8mn_soc_data, }, | ||
56 | { } | 91 | { } |
57 | }; | 92 | }; |
58 | 93 | ||
@@ -65,7 +100,6 @@ static int __init imx8_soc_init(void) | |||
65 | { | 100 | { |
66 | struct soc_device_attribute *soc_dev_attr; | 101 | struct soc_device_attribute *soc_dev_attr; |
67 | struct soc_device *soc_dev; | 102 | struct soc_device *soc_dev; |
68 | struct device_node *root; | ||
69 | const struct of_device_id *id; | 103 | const struct of_device_id *id; |
70 | u32 soc_rev = 0; | 104 | u32 soc_rev = 0; |
71 | const struct imx8_soc_data *data; | 105 | const struct imx8_soc_data *data; |
@@ -73,20 +107,19 @@ static int __init imx8_soc_init(void) | |||
73 | 107 | ||
74 | soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); | 108 | soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); |
75 | if (!soc_dev_attr) | 109 | if (!soc_dev_attr) |
76 | return -ENODEV; | 110 | return -ENOMEM; |
77 | 111 | ||
78 | soc_dev_attr->family = "Freescale i.MX"; | 112 | soc_dev_attr->family = "Freescale i.MX"; |
79 | 113 | ||
80 | root = of_find_node_by_path("/"); | 114 | ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine); |
81 | ret = of_property_read_string(root, "model", &soc_dev_attr->machine); | ||
82 | if (ret) | 115 | if (ret) |
83 | goto free_soc; | 116 | goto free_soc; |
84 | 117 | ||
85 | id = of_match_node(imx8_soc_match, root); | 118 | id = of_match_node(imx8_soc_match, of_root); |
86 | if (!id) | 119 | if (!id) { |
120 | ret = -ENODEV; | ||
87 | goto free_soc; | 121 | goto free_soc; |
88 | 122 | } | |
89 | of_node_put(root); | ||
90 | 123 | ||
91 | data = id->data; | 124 | data = id->data; |
92 | if (data) { | 125 | if (data) { |
@@ -96,20 +129,24 @@ static int __init imx8_soc_init(void) | |||
96 | } | 129 | } |
97 | 130 | ||
98 | soc_dev_attr->revision = imx8_revision(soc_rev); | 131 | soc_dev_attr->revision = imx8_revision(soc_rev); |
99 | if (!soc_dev_attr->revision) | 132 | if (!soc_dev_attr->revision) { |
133 | ret = -ENOMEM; | ||
100 | goto free_soc; | 134 | goto free_soc; |
135 | } | ||
101 | 136 | ||
102 | soc_dev = soc_device_register(soc_dev_attr); | 137 | soc_dev = soc_device_register(soc_dev_attr); |
103 | if (IS_ERR(soc_dev)) | 138 | if (IS_ERR(soc_dev)) { |
139 | ret = PTR_ERR(soc_dev); | ||
104 | goto free_rev; | 140 | goto free_rev; |
141 | } | ||
105 | 142 | ||
106 | return 0; | 143 | return 0; |
107 | 144 | ||
108 | free_rev: | 145 | free_rev: |
109 | kfree(soc_dev_attr->revision); | 146 | if (strcmp(soc_dev_attr->revision, "unknown")) |
147 | kfree(soc_dev_attr->revision); | ||
110 | free_soc: | 148 | free_soc: |
111 | kfree(soc_dev_attr); | 149 | kfree(soc_dev_attr); |
112 | of_node_put(root); | 150 | return ret; |
113 | return -ENODEV; | ||
114 | } | 151 | } |
115 | device_initcall(imx8_soc_init); | 152 | device_initcall(imx8_soc_init); |