162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Freescale QorIQ Platforms GUTS Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016 Freescale Semiconductor, Inc.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of_fdt.h>
1262306a36Sopenharmony_ci#include <linux/sys_soc.h>
1362306a36Sopenharmony_ci#include <linux/of_address.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/fsl/guts.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct fsl_soc_die_attr {
1862306a36Sopenharmony_ci	char	*die;
1962306a36Sopenharmony_ci	u32	svr;
2062306a36Sopenharmony_ci	u32	mask;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct fsl_soc_data {
2462306a36Sopenharmony_ci	const char *sfp_compat;
2562306a36Sopenharmony_ci	u32 uid_offset;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* SoC die attribute definition for QorIQ platform */
2962306a36Sopenharmony_cistatic const struct fsl_soc_die_attr fsl_soc_die[] = {
3062306a36Sopenharmony_ci	/*
3162306a36Sopenharmony_ci	 * Power Architecture-based SoCs T Series
3262306a36Sopenharmony_ci	 */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* Die: T4240, SoC: T4240/T4160/T4080 */
3562306a36Sopenharmony_ci	{ .die		= "T4240",
3662306a36Sopenharmony_ci	  .svr		= 0x82400000,
3762306a36Sopenharmony_ci	  .mask		= 0xfff00000,
3862306a36Sopenharmony_ci	},
3962306a36Sopenharmony_ci	/* Die: T1040, SoC: T1040/T1020/T1042/T1022 */
4062306a36Sopenharmony_ci	{ .die		= "T1040",
4162306a36Sopenharmony_ci	  .svr		= 0x85200000,
4262306a36Sopenharmony_ci	  .mask		= 0xfff00000,
4362306a36Sopenharmony_ci	},
4462306a36Sopenharmony_ci	/* Die: T2080, SoC: T2080/T2081 */
4562306a36Sopenharmony_ci	{ .die		= "T2080",
4662306a36Sopenharmony_ci	  .svr		= 0x85300000,
4762306a36Sopenharmony_ci	  .mask		= 0xfff00000,
4862306a36Sopenharmony_ci	},
4962306a36Sopenharmony_ci	/* Die: T1024, SoC: T1024/T1014/T1023/T1013 */
5062306a36Sopenharmony_ci	{ .die		= "T1024",
5162306a36Sopenharmony_ci	  .svr		= 0x85400000,
5262306a36Sopenharmony_ci	  .mask		= 0xfff00000,
5362306a36Sopenharmony_ci	},
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/*
5662306a36Sopenharmony_ci	 * ARM-based SoCs LS Series
5762306a36Sopenharmony_ci	 */
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* Die: LS1043A, SoC: LS1043A/LS1023A */
6062306a36Sopenharmony_ci	{ .die		= "LS1043A",
6162306a36Sopenharmony_ci	  .svr		= 0x87920000,
6262306a36Sopenharmony_ci	  .mask		= 0xffff0000,
6362306a36Sopenharmony_ci	},
6462306a36Sopenharmony_ci	/* Die: LS2080A, SoC: LS2080A/LS2040A/LS2085A */
6562306a36Sopenharmony_ci	{ .die		= "LS2080A",
6662306a36Sopenharmony_ci	  .svr		= 0x87010000,
6762306a36Sopenharmony_ci	  .mask		= 0xff3f0000,
6862306a36Sopenharmony_ci	},
6962306a36Sopenharmony_ci	/* Die: LS1088A, SoC: LS1088A/LS1048A/LS1084A/LS1044A */
7062306a36Sopenharmony_ci	{ .die		= "LS1088A",
7162306a36Sopenharmony_ci	  .svr		= 0x87030000,
7262306a36Sopenharmony_ci	  .mask		= 0xff3f0000,
7362306a36Sopenharmony_ci	},
7462306a36Sopenharmony_ci	/* Die: LS1012A, SoC: LS1012A */
7562306a36Sopenharmony_ci	{ .die		= "LS1012A",
7662306a36Sopenharmony_ci	  .svr		= 0x87040000,
7762306a36Sopenharmony_ci	  .mask		= 0xffff0000,
7862306a36Sopenharmony_ci	},
7962306a36Sopenharmony_ci	/* Die: LS1046A, SoC: LS1046A/LS1026A */
8062306a36Sopenharmony_ci	{ .die		= "LS1046A",
8162306a36Sopenharmony_ci	  .svr		= 0x87070000,
8262306a36Sopenharmony_ci	  .mask		= 0xffff0000,
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci	/* Die: LS2088A, SoC: LS2088A/LS2048A/LS2084A/LS2044A */
8562306a36Sopenharmony_ci	{ .die		= "LS2088A",
8662306a36Sopenharmony_ci	  .svr		= 0x87090000,
8762306a36Sopenharmony_ci	  .mask		= 0xff3f0000,
8862306a36Sopenharmony_ci	},
8962306a36Sopenharmony_ci	/* Die: LS1021A, SoC: LS1021A/LS1020A/LS1022A */
9062306a36Sopenharmony_ci	{ .die		= "LS1021A",
9162306a36Sopenharmony_ci	  .svr		= 0x87000000,
9262306a36Sopenharmony_ci	  .mask		= 0xfff70000,
9362306a36Sopenharmony_ci	},
9462306a36Sopenharmony_ci	/* Die: LX2160A, SoC: LX2160A/LX2120A/LX2080A */
9562306a36Sopenharmony_ci	{ .die          = "LX2160A",
9662306a36Sopenharmony_ci	  .svr          = 0x87360000,
9762306a36Sopenharmony_ci	  .mask         = 0xff3f0000,
9862306a36Sopenharmony_ci	},
9962306a36Sopenharmony_ci	/* Die: LS1028A, SoC: LS1028A */
10062306a36Sopenharmony_ci	{ .die          = "LS1028A",
10162306a36Sopenharmony_ci	  .svr          = 0x870b0000,
10262306a36Sopenharmony_ci	  .mask         = 0xff3f0000,
10362306a36Sopenharmony_ci	},
10462306a36Sopenharmony_ci	{ },
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic const struct fsl_soc_die_attr *fsl_soc_die_match(
10862306a36Sopenharmony_ci	u32 svr, const struct fsl_soc_die_attr *matches)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	while (matches->svr) {
11162306a36Sopenharmony_ci		if (matches->svr == (svr & matches->mask))
11262306a36Sopenharmony_ci			return matches;
11362306a36Sopenharmony_ci		matches++;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci	return NULL;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct device_node *np;
12162306a36Sopenharmony_ci	void __iomem *sfp_base;
12262306a36Sopenharmony_ci	u64 uid;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, compat);
12562306a36Sopenharmony_ci	if (!np)
12662306a36Sopenharmony_ci		return 0;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	sfp_base = of_iomap(np, 0);
12962306a36Sopenharmony_ci	if (!sfp_base) {
13062306a36Sopenharmony_ci		of_node_put(np);
13162306a36Sopenharmony_ci		return 0;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	uid = ioread32(sfp_base + offset);
13562306a36Sopenharmony_ci	uid <<= 32;
13662306a36Sopenharmony_ci	uid |= ioread32(sfp_base + offset + 4);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	iounmap(sfp_base);
13962306a36Sopenharmony_ci	of_node_put(np);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return uid;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic const struct fsl_soc_data ls1028a_data = {
14562306a36Sopenharmony_ci	.sfp_compat = "fsl,ls1028a-sfp",
14662306a36Sopenharmony_ci	.uid_offset = 0x21c,
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci * Table for matching compatible strings, for device tree
15162306a36Sopenharmony_ci * guts node, for Freescale QorIQ SOCs.
15262306a36Sopenharmony_ci */
15362306a36Sopenharmony_cistatic const struct of_device_id fsl_guts_of_match[] = {
15462306a36Sopenharmony_ci	{ .compatible = "fsl,qoriq-device-config-1.0", },
15562306a36Sopenharmony_ci	{ .compatible = "fsl,qoriq-device-config-2.0", },
15662306a36Sopenharmony_ci	{ .compatible = "fsl,p1010-guts", },
15762306a36Sopenharmony_ci	{ .compatible = "fsl,p1020-guts", },
15862306a36Sopenharmony_ci	{ .compatible = "fsl,p1021-guts", },
15962306a36Sopenharmony_ci	{ .compatible = "fsl,p1022-guts", },
16062306a36Sopenharmony_ci	{ .compatible = "fsl,p1023-guts", },
16162306a36Sopenharmony_ci	{ .compatible = "fsl,p2020-guts", },
16262306a36Sopenharmony_ci	{ .compatible = "fsl,bsc9131-guts", },
16362306a36Sopenharmony_ci	{ .compatible = "fsl,bsc9132-guts", },
16462306a36Sopenharmony_ci	{ .compatible = "fsl,mpc8536-guts", },
16562306a36Sopenharmony_ci	{ .compatible = "fsl,mpc8544-guts", },
16662306a36Sopenharmony_ci	{ .compatible = "fsl,mpc8548-guts", },
16762306a36Sopenharmony_ci	{ .compatible = "fsl,mpc8568-guts", },
16862306a36Sopenharmony_ci	{ .compatible = "fsl,mpc8569-guts", },
16962306a36Sopenharmony_ci	{ .compatible = "fsl,mpc8572-guts", },
17062306a36Sopenharmony_ci	{ .compatible = "fsl,ls1021a-dcfg", },
17162306a36Sopenharmony_ci	{ .compatible = "fsl,ls1043a-dcfg", },
17262306a36Sopenharmony_ci	{ .compatible = "fsl,ls2080a-dcfg", },
17362306a36Sopenharmony_ci	{ .compatible = "fsl,ls1088a-dcfg", },
17462306a36Sopenharmony_ci	{ .compatible = "fsl,ls1012a-dcfg", },
17562306a36Sopenharmony_ci	{ .compatible = "fsl,ls1046a-dcfg", },
17662306a36Sopenharmony_ci	{ .compatible = "fsl,lx2160a-dcfg", },
17762306a36Sopenharmony_ci	{ .compatible = "fsl,ls1028a-dcfg", .data = &ls1028a_data},
17862306a36Sopenharmony_ci	{}
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int __init fsl_guts_init(void)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct soc_device_attribute *soc_dev_attr;
18462306a36Sopenharmony_ci	static struct soc_device *soc_dev;
18562306a36Sopenharmony_ci	const struct fsl_soc_die_attr *soc_die;
18662306a36Sopenharmony_ci	const struct fsl_soc_data *soc_data;
18762306a36Sopenharmony_ci	const struct of_device_id *match;
18862306a36Sopenharmony_ci	struct ccsr_guts __iomem *regs;
18962306a36Sopenharmony_ci	const char *machine = NULL;
19062306a36Sopenharmony_ci	struct device_node *np;
19162306a36Sopenharmony_ci	bool little_endian;
19262306a36Sopenharmony_ci	u64 soc_uid = 0;
19362306a36Sopenharmony_ci	u32 svr;
19462306a36Sopenharmony_ci	int ret;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	np = of_find_matching_node_and_match(NULL, fsl_guts_of_match, &match);
19762306a36Sopenharmony_ci	if (!np)
19862306a36Sopenharmony_ci		return 0;
19962306a36Sopenharmony_ci	soc_data = match->data;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	regs = of_iomap(np, 0);
20262306a36Sopenharmony_ci	if (!regs) {
20362306a36Sopenharmony_ci		of_node_put(np);
20462306a36Sopenharmony_ci		return -ENOMEM;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	little_endian = of_property_read_bool(np, "little-endian");
20862306a36Sopenharmony_ci	if (little_endian)
20962306a36Sopenharmony_ci		svr = ioread32(&regs->svr);
21062306a36Sopenharmony_ci	else
21162306a36Sopenharmony_ci		svr = ioread32be(&regs->svr);
21262306a36Sopenharmony_ci	iounmap(regs);
21362306a36Sopenharmony_ci	of_node_put(np);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Register soc device */
21662306a36Sopenharmony_ci	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
21762306a36Sopenharmony_ci	if (!soc_dev_attr)
21862306a36Sopenharmony_ci		return -ENOMEM;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (of_property_read_string(of_root, "model", &machine))
22162306a36Sopenharmony_ci		of_property_read_string_index(of_root, "compatible", 0, &machine);
22262306a36Sopenharmony_ci	if (machine) {
22362306a36Sopenharmony_ci		soc_dev_attr->machine = kstrdup(machine, GFP_KERNEL);
22462306a36Sopenharmony_ci		if (!soc_dev_attr->machine)
22562306a36Sopenharmony_ci			goto err_nomem;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	soc_die = fsl_soc_die_match(svr, fsl_soc_die);
22962306a36Sopenharmony_ci	if (soc_die) {
23062306a36Sopenharmony_ci		soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ %s",
23162306a36Sopenharmony_ci						 soc_die->die);
23262306a36Sopenharmony_ci	} else {
23362306a36Sopenharmony_ci		soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ");
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci	if (!soc_dev_attr->family)
23662306a36Sopenharmony_ci		goto err_nomem;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "svr:0x%08x", svr);
23962306a36Sopenharmony_ci	if (!soc_dev_attr->soc_id)
24062306a36Sopenharmony_ci		goto err_nomem;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d.%d",
24362306a36Sopenharmony_ci					   (svr >>  4) & 0xf, svr & 0xf);
24462306a36Sopenharmony_ci	if (!soc_dev_attr->revision)
24562306a36Sopenharmony_ci		goto err_nomem;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (soc_data)
24862306a36Sopenharmony_ci		soc_uid = fsl_guts_get_soc_uid(soc_data->sfp_compat,
24962306a36Sopenharmony_ci					       soc_data->uid_offset);
25062306a36Sopenharmony_ci	if (soc_uid)
25162306a36Sopenharmony_ci		soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX",
25262306a36Sopenharmony_ci							soc_uid);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	soc_dev = soc_device_register(soc_dev_attr);
25562306a36Sopenharmony_ci	if (IS_ERR(soc_dev)) {
25662306a36Sopenharmony_ci		ret = PTR_ERR(soc_dev);
25762306a36Sopenharmony_ci		goto err;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	pr_info("Machine: %s\n", soc_dev_attr->machine);
26162306a36Sopenharmony_ci	pr_info("SoC family: %s\n", soc_dev_attr->family);
26262306a36Sopenharmony_ci	pr_info("SoC ID: %s, Revision: %s\n",
26362306a36Sopenharmony_ci		soc_dev_attr->soc_id, soc_dev_attr->revision);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return 0;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cierr_nomem:
26862306a36Sopenharmony_ci	ret = -ENOMEM;
26962306a36Sopenharmony_cierr:
27062306a36Sopenharmony_ci	kfree(soc_dev_attr->machine);
27162306a36Sopenharmony_ci	kfree(soc_dev_attr->family);
27262306a36Sopenharmony_ci	kfree(soc_dev_attr->soc_id);
27362306a36Sopenharmony_ci	kfree(soc_dev_attr->revision);
27462306a36Sopenharmony_ci	kfree(soc_dev_attr->serial_number);
27562306a36Sopenharmony_ci	kfree(soc_dev_attr);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return ret;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_cicore_initcall(fsl_guts_init);
280