Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  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#define OCOTP_UID_LOW			0x410
 20#define OCOTP_UID_HIGH			0x420
 21
 22/* Same as ANADIG_DIGPROG_IMX7D */
 23#define ANADIG_DIGPROG_IMX8MM	0x800
 24
 25struct imx8_soc_data {
 26	char *name;
 27	u32 (*soc_revision)(void);
 28};
 29
 30static u64 soc_uid;
 31
 32static ssize_t soc_uid_show(struct device *dev,
 33			    struct device_attribute *attr, char *buf)
 34{
 35	return sprintf(buf, "%016llX\n", soc_uid);
 36}
 37
 38static DEVICE_ATTR_RO(soc_uid);
 39
 40static u32 __init imx8mq_soc_revision(void)
 41{
 42	struct device_node *np;
 43	void __iomem *ocotp_base;
 44	u32 magic;
 45	u32 rev = 0;
 46
 47	np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp");
 48	if (!np)
 49		goto out;
 50
 51	ocotp_base = of_iomap(np, 0);
 52	WARN_ON(!ocotp_base);
 53
 54	magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1);
 55	if (magic == IMX8MQ_SW_MAGIC_B1)
 56		rev = REV_B1;
 57
 58	soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
 59	soc_uid <<= 32;
 60	soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
 61
 62	iounmap(ocotp_base);
 63
 64out:
 65	of_node_put(np);
 66	return rev;
 67}
 68
 69static void __init imx8mm_soc_uid(void)
 70{
 71	void __iomem *ocotp_base;
 72	struct device_node *np;
 73
 74	np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp");
 75	if (!np)
 76		return;
 77
 78	ocotp_base = of_iomap(np, 0);
 79	WARN_ON(!ocotp_base);
 80
 81	soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
 82	soc_uid <<= 32;
 83	soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
 84
 85	iounmap(ocotp_base);
 86	of_node_put(np);
 87}
 88
 89static u32 __init imx8mm_soc_revision(void)
 90{
 91	struct device_node *np;
 92	void __iomem *anatop_base;
 93	u32 rev;
 94
 95	np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop");
 96	if (!np)
 97		return 0;
 98
 99	anatop_base = of_iomap(np, 0);
100	WARN_ON(!anatop_base);
101
102	rev = readl_relaxed(anatop_base + ANADIG_DIGPROG_IMX8MM);
103
104	iounmap(anatop_base);
105	of_node_put(np);
106
107	imx8mm_soc_uid();
108
109	return rev;
110}
111
112static const struct imx8_soc_data imx8mq_soc_data = {
113	.name = "i.MX8MQ",
114	.soc_revision = imx8mq_soc_revision,
115};
116
117static const struct imx8_soc_data imx8mm_soc_data = {
118	.name = "i.MX8MM",
119	.soc_revision = imx8mm_soc_revision,
120};
121
122static const struct imx8_soc_data imx8mn_soc_data = {
123	.name = "i.MX8MN",
124	.soc_revision = imx8mm_soc_revision,
125};
126
127static const struct of_device_id imx8_soc_match[] = {
128	{ .compatible = "fsl,imx8mq", .data = &imx8mq_soc_data, },
129	{ .compatible = "fsl,imx8mm", .data = &imx8mm_soc_data, },
130	{ .compatible = "fsl,imx8mn", .data = &imx8mn_soc_data, },
131	{ }
132};
133
134#define imx8_revision(soc_rev) \
135	soc_rev ? \
136	kasprintf(GFP_KERNEL, "%d.%d", (soc_rev >> 4) & 0xf,  soc_rev & 0xf) : \
137	"unknown"
138
139static int __init imx8_soc_init(void)
140{
141	struct soc_device_attribute *soc_dev_attr;
142	struct soc_device *soc_dev;
143	const struct of_device_id *id;
144	u32 soc_rev = 0;
145	const struct imx8_soc_data *data;
146	int ret;
147
148	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
149	if (!soc_dev_attr)
150		return -ENOMEM;
151
152	soc_dev_attr->family = "Freescale i.MX";
153
154	ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
155	if (ret)
156		goto free_soc;
157
158	id = of_match_node(imx8_soc_match, of_root);
159	if (!id) {
160		ret = -ENODEV;
161		goto free_soc;
162	}
163
164	data = id->data;
165	if (data) {
166		soc_dev_attr->soc_id = data->name;
167		if (data->soc_revision)
168			soc_rev = data->soc_revision();
169	}
170
171	soc_dev_attr->revision = imx8_revision(soc_rev);
172	if (!soc_dev_attr->revision) {
173		ret = -ENOMEM;
174		goto free_soc;
175	}
176
177	soc_dev = soc_device_register(soc_dev_attr);
178	if (IS_ERR(soc_dev)) {
179		ret = PTR_ERR(soc_dev);
180		goto free_rev;
181	}
182
183	ret = device_create_file(soc_device_to_device(soc_dev),
184				 &dev_attr_soc_uid);
185	if (ret)
186		goto free_rev;
187
188	if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT))
189		platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);
190
191	return 0;
192
193free_rev:
194	if (strcmp(soc_dev_attr->revision, "unknown"))
195		kfree(soc_dev_attr->revision);
196free_soc:
197	kfree(soc_dev_attr);
198	return ret;
199}
200device_initcall(imx8_soc_init);