18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014, NVIDIA CORPORATION.  All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/device.h>
88c2ecf20Sopenharmony_ci#include <linux/kobject.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/nvmem-consumer.h>
128c2ecf20Sopenharmony_ci#include <linux/nvmem-provider.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/of_address.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/sys_soc.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <soc/tegra/common.h>
208c2ecf20Sopenharmony_ci#include <soc/tegra/fuse.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "fuse.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistruct tegra_sku_info tegra_sku_info;
258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tegra_sku_info);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
288c2ecf20Sopenharmony_ci	[TEGRA_REVISION_UNKNOWN] = "unknown",
298c2ecf20Sopenharmony_ci	[TEGRA_REVISION_A01]     = "A01",
308c2ecf20Sopenharmony_ci	[TEGRA_REVISION_A02]     = "A02",
318c2ecf20Sopenharmony_ci	[TEGRA_REVISION_A03]     = "A03",
328c2ecf20Sopenharmony_ci	[TEGRA_REVISION_A03p]    = "A03 prime",
338c2ecf20Sopenharmony_ci	[TEGRA_REVISION_A04]     = "A04",
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic const struct of_device_id car_match[] __initconst = {
378c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra20-car", },
388c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra30-car", },
398c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra114-car", },
408c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra124-car", },
418c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra132-car", },
428c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra210-car", },
438c2ecf20Sopenharmony_ci	{},
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic struct tegra_fuse *fuse = &(struct tegra_fuse) {
478c2ecf20Sopenharmony_ci	.base = NULL,
488c2ecf20Sopenharmony_ci	.soc = NULL,
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_fuse_match[] = {
528c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_234_SOC
538c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra234-efuse", .data = &tegra234_fuse_soc },
548c2ecf20Sopenharmony_ci#endif
558c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_194_SOC
568c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra194-efuse", .data = &tegra194_fuse_soc },
578c2ecf20Sopenharmony_ci#endif
588c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_186_SOC
598c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra186-efuse", .data = &tegra186_fuse_soc },
608c2ecf20Sopenharmony_ci#endif
618c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_210_SOC
628c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra210-efuse", .data = &tegra210_fuse_soc },
638c2ecf20Sopenharmony_ci#endif
648c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_132_SOC
658c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra132-efuse", .data = &tegra124_fuse_soc },
668c2ecf20Sopenharmony_ci#endif
678c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_124_SOC
688c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra124-efuse", .data = &tegra124_fuse_soc },
698c2ecf20Sopenharmony_ci#endif
708c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_114_SOC
718c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra114-efuse", .data = &tegra114_fuse_soc },
728c2ecf20Sopenharmony_ci#endif
738c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_3x_SOC
748c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra30-efuse", .data = &tegra30_fuse_soc },
758c2ecf20Sopenharmony_ci#endif
768c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_2x_SOC
778c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra20-efuse", .data = &tegra20_fuse_soc },
788c2ecf20Sopenharmony_ci#endif
798c2ecf20Sopenharmony_ci	{ /* sentinel */ }
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int tegra_fuse_read(void *priv, unsigned int offset, void *value,
838c2ecf20Sopenharmony_ci			   size_t bytes)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	unsigned int count = bytes / 4, i;
868c2ecf20Sopenharmony_ci	struct tegra_fuse *fuse = priv;
878c2ecf20Sopenharmony_ci	u32 *buffer = value;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++)
908c2ecf20Sopenharmony_ci		buffer[i] = fuse->read(fuse, offset + i * 4);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return 0;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic const struct nvmem_cell_info tegra_fuse_cells[] = {
968c2ecf20Sopenharmony_ci	{
978c2ecf20Sopenharmony_ci		.name = "tsensor-cpu1",
988c2ecf20Sopenharmony_ci		.offset = 0x084,
998c2ecf20Sopenharmony_ci		.bytes = 4,
1008c2ecf20Sopenharmony_ci		.bit_offset = 0,
1018c2ecf20Sopenharmony_ci		.nbits = 32,
1028c2ecf20Sopenharmony_ci	}, {
1038c2ecf20Sopenharmony_ci		.name = "tsensor-cpu2",
1048c2ecf20Sopenharmony_ci		.offset = 0x088,
1058c2ecf20Sopenharmony_ci		.bytes = 4,
1068c2ecf20Sopenharmony_ci		.bit_offset = 0,
1078c2ecf20Sopenharmony_ci		.nbits = 32,
1088c2ecf20Sopenharmony_ci	}, {
1098c2ecf20Sopenharmony_ci		.name = "tsensor-cpu0",
1108c2ecf20Sopenharmony_ci		.offset = 0x098,
1118c2ecf20Sopenharmony_ci		.bytes = 4,
1128c2ecf20Sopenharmony_ci		.bit_offset = 0,
1138c2ecf20Sopenharmony_ci		.nbits = 32,
1148c2ecf20Sopenharmony_ci	}, {
1158c2ecf20Sopenharmony_ci		.name = "xusb-pad-calibration",
1168c2ecf20Sopenharmony_ci		.offset = 0x0f0,
1178c2ecf20Sopenharmony_ci		.bytes = 4,
1188c2ecf20Sopenharmony_ci		.bit_offset = 0,
1198c2ecf20Sopenharmony_ci		.nbits = 32,
1208c2ecf20Sopenharmony_ci	}, {
1218c2ecf20Sopenharmony_ci		.name = "tsensor-cpu3",
1228c2ecf20Sopenharmony_ci		.offset = 0x12c,
1238c2ecf20Sopenharmony_ci		.bytes = 4,
1248c2ecf20Sopenharmony_ci		.bit_offset = 0,
1258c2ecf20Sopenharmony_ci		.nbits = 32,
1268c2ecf20Sopenharmony_ci	}, {
1278c2ecf20Sopenharmony_ci		.name = "sata-calibration",
1288c2ecf20Sopenharmony_ci		.offset = 0x124,
1298c2ecf20Sopenharmony_ci		.bytes = 1,
1308c2ecf20Sopenharmony_ci		.bit_offset = 0,
1318c2ecf20Sopenharmony_ci		.nbits = 2,
1328c2ecf20Sopenharmony_ci	}, {
1338c2ecf20Sopenharmony_ci		.name = "tsensor-gpu",
1348c2ecf20Sopenharmony_ci		.offset = 0x154,
1358c2ecf20Sopenharmony_ci		.bytes = 4,
1368c2ecf20Sopenharmony_ci		.bit_offset = 0,
1378c2ecf20Sopenharmony_ci		.nbits = 32,
1388c2ecf20Sopenharmony_ci	}, {
1398c2ecf20Sopenharmony_ci		.name = "tsensor-mem0",
1408c2ecf20Sopenharmony_ci		.offset = 0x158,
1418c2ecf20Sopenharmony_ci		.bytes = 4,
1428c2ecf20Sopenharmony_ci		.bit_offset = 0,
1438c2ecf20Sopenharmony_ci		.nbits = 32,
1448c2ecf20Sopenharmony_ci	}, {
1458c2ecf20Sopenharmony_ci		.name = "tsensor-mem1",
1468c2ecf20Sopenharmony_ci		.offset = 0x15c,
1478c2ecf20Sopenharmony_ci		.bytes = 4,
1488c2ecf20Sopenharmony_ci		.bit_offset = 0,
1498c2ecf20Sopenharmony_ci		.nbits = 32,
1508c2ecf20Sopenharmony_ci	}, {
1518c2ecf20Sopenharmony_ci		.name = "tsensor-pllx",
1528c2ecf20Sopenharmony_ci		.offset = 0x160,
1538c2ecf20Sopenharmony_ci		.bytes = 4,
1548c2ecf20Sopenharmony_ci		.bit_offset = 0,
1558c2ecf20Sopenharmony_ci		.nbits = 32,
1568c2ecf20Sopenharmony_ci	}, {
1578c2ecf20Sopenharmony_ci		.name = "tsensor-common",
1588c2ecf20Sopenharmony_ci		.offset = 0x180,
1598c2ecf20Sopenharmony_ci		.bytes = 4,
1608c2ecf20Sopenharmony_ci		.bit_offset = 0,
1618c2ecf20Sopenharmony_ci		.nbits = 32,
1628c2ecf20Sopenharmony_ci	}, {
1638c2ecf20Sopenharmony_ci		.name = "tsensor-realignment",
1648c2ecf20Sopenharmony_ci		.offset = 0x1fc,
1658c2ecf20Sopenharmony_ci		.bytes = 4,
1668c2ecf20Sopenharmony_ci		.bit_offset = 0,
1678c2ecf20Sopenharmony_ci		.nbits = 32,
1688c2ecf20Sopenharmony_ci	}, {
1698c2ecf20Sopenharmony_ci		.name = "gpu-calibration",
1708c2ecf20Sopenharmony_ci		.offset = 0x204,
1718c2ecf20Sopenharmony_ci		.bytes = 4,
1728c2ecf20Sopenharmony_ci		.bit_offset = 0,
1738c2ecf20Sopenharmony_ci		.nbits = 32,
1748c2ecf20Sopenharmony_ci	}, {
1758c2ecf20Sopenharmony_ci		.name = "xusb-pad-calibration-ext",
1768c2ecf20Sopenharmony_ci		.offset = 0x250,
1778c2ecf20Sopenharmony_ci		.bytes = 4,
1788c2ecf20Sopenharmony_ci		.bit_offset = 0,
1798c2ecf20Sopenharmony_ci		.nbits = 32,
1808c2ecf20Sopenharmony_ci	},
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int tegra_fuse_probe(struct platform_device *pdev)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	void __iomem *base = fuse->base;
1868c2ecf20Sopenharmony_ci	struct nvmem_config nvmem;
1878c2ecf20Sopenharmony_ci	struct resource *res;
1888c2ecf20Sopenharmony_ci	int err;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/* take over the memory region from the early initialization */
1918c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1928c2ecf20Sopenharmony_ci	fuse->phys = res->start;
1938c2ecf20Sopenharmony_ci	fuse->base = devm_ioremap_resource(&pdev->dev, res);
1948c2ecf20Sopenharmony_ci	if (IS_ERR(fuse->base)) {
1958c2ecf20Sopenharmony_ci		err = PTR_ERR(fuse->base);
1968c2ecf20Sopenharmony_ci		fuse->base = base;
1978c2ecf20Sopenharmony_ci		return err;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	fuse->clk = devm_clk_get(&pdev->dev, "fuse");
2018c2ecf20Sopenharmony_ci	if (IS_ERR(fuse->clk)) {
2028c2ecf20Sopenharmony_ci		if (PTR_ERR(fuse->clk) != -EPROBE_DEFER)
2038c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "failed to get FUSE clock: %ld",
2048c2ecf20Sopenharmony_ci				PTR_ERR(fuse->clk));
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		fuse->base = base;
2078c2ecf20Sopenharmony_ci		return PTR_ERR(fuse->clk);
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, fuse);
2118c2ecf20Sopenharmony_ci	fuse->dev = &pdev->dev;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (fuse->soc->probe) {
2148c2ecf20Sopenharmony_ci		err = fuse->soc->probe(fuse);
2158c2ecf20Sopenharmony_ci		if (err < 0)
2168c2ecf20Sopenharmony_ci			goto restore;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	memset(&nvmem, 0, sizeof(nvmem));
2208c2ecf20Sopenharmony_ci	nvmem.dev = &pdev->dev;
2218c2ecf20Sopenharmony_ci	nvmem.name = "fuse";
2228c2ecf20Sopenharmony_ci	nvmem.id = -1;
2238c2ecf20Sopenharmony_ci	nvmem.owner = THIS_MODULE;
2248c2ecf20Sopenharmony_ci	nvmem.cells = tegra_fuse_cells;
2258c2ecf20Sopenharmony_ci	nvmem.ncells = ARRAY_SIZE(tegra_fuse_cells);
2268c2ecf20Sopenharmony_ci	nvmem.type = NVMEM_TYPE_OTP;
2278c2ecf20Sopenharmony_ci	nvmem.read_only = true;
2288c2ecf20Sopenharmony_ci	nvmem.root_only = true;
2298c2ecf20Sopenharmony_ci	nvmem.reg_read = tegra_fuse_read;
2308c2ecf20Sopenharmony_ci	nvmem.size = fuse->soc->info->size;
2318c2ecf20Sopenharmony_ci	nvmem.word_size = 4;
2328c2ecf20Sopenharmony_ci	nvmem.stride = 4;
2338c2ecf20Sopenharmony_ci	nvmem.priv = fuse;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	fuse->nvmem = devm_nvmem_register(&pdev->dev, &nvmem);
2368c2ecf20Sopenharmony_ci	if (IS_ERR(fuse->nvmem)) {
2378c2ecf20Sopenharmony_ci		err = PTR_ERR(fuse->nvmem);
2388c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register NVMEM device: %d\n",
2398c2ecf20Sopenharmony_ci			err);
2408c2ecf20Sopenharmony_ci		goto restore;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/* release the early I/O memory mapping */
2448c2ecf20Sopenharmony_ci	iounmap(base);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cirestore:
2498c2ecf20Sopenharmony_ci	fuse->base = base;
2508c2ecf20Sopenharmony_ci	return err;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic struct platform_driver tegra_fuse_driver = {
2548c2ecf20Sopenharmony_ci	.driver = {
2558c2ecf20Sopenharmony_ci		.name = "tegra-fuse",
2568c2ecf20Sopenharmony_ci		.of_match_table = tegra_fuse_match,
2578c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
2588c2ecf20Sopenharmony_ci	},
2598c2ecf20Sopenharmony_ci	.probe = tegra_fuse_probe,
2608c2ecf20Sopenharmony_ci};
2618c2ecf20Sopenharmony_cibuiltin_platform_driver(tegra_fuse_driver);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ciu32 __init tegra_fuse_read_spare(unsigned int spare)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	unsigned int offset = fuse->soc->info->spare + spare * 4;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return fuse->read_early(fuse, offset) & 1;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ciu32 __init tegra_fuse_read_early(unsigned int offset)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	return fuse->read_early(fuse, offset);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ciint tegra_fuse_readl(unsigned long offset, u32 *value)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	if (!fuse->read || !fuse->clk)
2788c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (IS_ERR(fuse->clk))
2818c2ecf20Sopenharmony_ci		return PTR_ERR(fuse->clk);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	*value = fuse->read(fuse, offset);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tegra_fuse_readl);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic void tegra_enable_fuse_clk(void __iomem *base)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	u32 reg;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	reg = readl_relaxed(base + 0x48);
2948c2ecf20Sopenharmony_ci	reg |= 1 << 28;
2958c2ecf20Sopenharmony_ci	writel(reg, base + 0x48);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/*
2988c2ecf20Sopenharmony_ci	 * Enable FUSE clock. This needs to be hardcoded because the clock
2998c2ecf20Sopenharmony_ci	 * subsystem is not active during early boot.
3008c2ecf20Sopenharmony_ci	 */
3018c2ecf20Sopenharmony_ci	reg = readl(base + 0x14);
3028c2ecf20Sopenharmony_ci	reg |= 1 << 7;
3038c2ecf20Sopenharmony_ci	writel(reg, base + 0x14);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic ssize_t major_show(struct device *dev, struct device_attribute *attr,
3078c2ecf20Sopenharmony_ci			     char *buf)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", tegra_get_major_rev());
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(major);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic ssize_t minor_show(struct device *dev, struct device_attribute *attr,
3158c2ecf20Sopenharmony_ci			     char *buf)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", tegra_get_minor_rev());
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(minor);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic struct attribute *tegra_soc_attr[] = {
3238c2ecf20Sopenharmony_ci	&dev_attr_major.attr,
3248c2ecf20Sopenharmony_ci	&dev_attr_minor.attr,
3258c2ecf20Sopenharmony_ci	NULL,
3268c2ecf20Sopenharmony_ci};
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ciconst struct attribute_group tegra_soc_attr_group = {
3298c2ecf20Sopenharmony_ci	.attrs = tegra_soc_attr,
3308c2ecf20Sopenharmony_ci};
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
3338c2ecf20Sopenharmony_ci    IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
3348c2ecf20Sopenharmony_cistatic ssize_t platform_show(struct device *dev, struct device_attribute *attr,
3358c2ecf20Sopenharmony_ci			     char *buf)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	/*
3388c2ecf20Sopenharmony_ci	 * Displays the value in the 'pre_si_platform' field of the HIDREV
3398c2ecf20Sopenharmony_ci	 * register for Tegra194 devices. A value of 0 indicates that the
3408c2ecf20Sopenharmony_ci	 * platform type is silicon and all other non-zero values indicate
3418c2ecf20Sopenharmony_ci	 * the type of simulation platform is being used.
3428c2ecf20Sopenharmony_ci	 */
3438c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", tegra_get_platform());
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(platform);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic struct attribute *tegra194_soc_attr[] = {
3498c2ecf20Sopenharmony_ci	&dev_attr_major.attr,
3508c2ecf20Sopenharmony_ci	&dev_attr_minor.attr,
3518c2ecf20Sopenharmony_ci	&dev_attr_platform.attr,
3528c2ecf20Sopenharmony_ci	NULL,
3538c2ecf20Sopenharmony_ci};
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ciconst struct attribute_group tegra194_soc_attr_group = {
3568c2ecf20Sopenharmony_ci	.attrs = tegra194_soc_attr,
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci#endif
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistruct device * __init tegra_soc_device_register(void)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct soc_device_attribute *attr;
3638c2ecf20Sopenharmony_ci	struct soc_device *dev;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
3668c2ecf20Sopenharmony_ci	if (!attr)
3678c2ecf20Sopenharmony_ci		return NULL;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	attr->family = kasprintf(GFP_KERNEL, "Tegra");
3708c2ecf20Sopenharmony_ci	attr->revision = kasprintf(GFP_KERNEL, "%s",
3718c2ecf20Sopenharmony_ci		tegra_revision_name[tegra_sku_info.revision]);
3728c2ecf20Sopenharmony_ci	attr->soc_id = kasprintf(GFP_KERNEL, "%u", tegra_get_chip_id());
3738c2ecf20Sopenharmony_ci	attr->custom_attr_group = fuse->soc->soc_attr_group;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	dev = soc_device_register(attr);
3768c2ecf20Sopenharmony_ci	if (IS_ERR(dev)) {
3778c2ecf20Sopenharmony_ci		kfree(attr->soc_id);
3788c2ecf20Sopenharmony_ci		kfree(attr->revision);
3798c2ecf20Sopenharmony_ci		kfree(attr->family);
3808c2ecf20Sopenharmony_ci		kfree(attr);
3818c2ecf20Sopenharmony_ci		return ERR_CAST(dev);
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return soc_device_to_device(dev);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int __init tegra_init_fuse(void)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	const struct of_device_id *match;
3908c2ecf20Sopenharmony_ci	struct device_node *np;
3918c2ecf20Sopenharmony_ci	struct resource regs;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	tegra_init_apbmisc();
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	np = of_find_matching_node_and_match(NULL, tegra_fuse_match, &match);
3968c2ecf20Sopenharmony_ci	if (!np) {
3978c2ecf20Sopenharmony_ci		/*
3988c2ecf20Sopenharmony_ci		 * Fall back to legacy initialization for 32-bit ARM only. All
3998c2ecf20Sopenharmony_ci		 * 64-bit ARM device tree files for Tegra are required to have
4008c2ecf20Sopenharmony_ci		 * a FUSE node.
4018c2ecf20Sopenharmony_ci		 *
4028c2ecf20Sopenharmony_ci		 * This is for backwards-compatibility with old device trees
4038c2ecf20Sopenharmony_ci		 * that didn't contain a FUSE node.
4048c2ecf20Sopenharmony_ci		 */
4058c2ecf20Sopenharmony_ci		if (IS_ENABLED(CONFIG_ARM) && soc_is_tegra()) {
4068c2ecf20Sopenharmony_ci			u8 chip = tegra_get_chip_id();
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci			regs.start = 0x7000f800;
4098c2ecf20Sopenharmony_ci			regs.end = 0x7000fbff;
4108c2ecf20Sopenharmony_ci			regs.flags = IORESOURCE_MEM;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci			switch (chip) {
4138c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_2x_SOC
4148c2ecf20Sopenharmony_ci			case TEGRA20:
4158c2ecf20Sopenharmony_ci				fuse->soc = &tegra20_fuse_soc;
4168c2ecf20Sopenharmony_ci				break;
4178c2ecf20Sopenharmony_ci#endif
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_3x_SOC
4208c2ecf20Sopenharmony_ci			case TEGRA30:
4218c2ecf20Sopenharmony_ci				fuse->soc = &tegra30_fuse_soc;
4228c2ecf20Sopenharmony_ci				break;
4238c2ecf20Sopenharmony_ci#endif
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_114_SOC
4268c2ecf20Sopenharmony_ci			case TEGRA114:
4278c2ecf20Sopenharmony_ci				fuse->soc = &tegra114_fuse_soc;
4288c2ecf20Sopenharmony_ci				break;
4298c2ecf20Sopenharmony_ci#endif
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_124_SOC
4328c2ecf20Sopenharmony_ci			case TEGRA124:
4338c2ecf20Sopenharmony_ci				fuse->soc = &tegra124_fuse_soc;
4348c2ecf20Sopenharmony_ci				break;
4358c2ecf20Sopenharmony_ci#endif
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci			default:
4388c2ecf20Sopenharmony_ci				pr_warn("Unsupported SoC: %02x\n", chip);
4398c2ecf20Sopenharmony_ci				break;
4408c2ecf20Sopenharmony_ci			}
4418c2ecf20Sopenharmony_ci		} else {
4428c2ecf20Sopenharmony_ci			/*
4438c2ecf20Sopenharmony_ci			 * At this point we're not running on Tegra, so play
4448c2ecf20Sopenharmony_ci			 * nice with multi-platform kernels.
4458c2ecf20Sopenharmony_ci			 */
4468c2ecf20Sopenharmony_ci			return 0;
4478c2ecf20Sopenharmony_ci		}
4488c2ecf20Sopenharmony_ci	} else {
4498c2ecf20Sopenharmony_ci		/*
4508c2ecf20Sopenharmony_ci		 * Extract information from the device tree if we've found a
4518c2ecf20Sopenharmony_ci		 * matching node.
4528c2ecf20Sopenharmony_ci		 */
4538c2ecf20Sopenharmony_ci		if (of_address_to_resource(np, 0, &regs) < 0) {
4548c2ecf20Sopenharmony_ci			pr_err("failed to get FUSE register\n");
4558c2ecf20Sopenharmony_ci			return -ENXIO;
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		fuse->soc = match->data;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	np = of_find_matching_node(NULL, car_match);
4628c2ecf20Sopenharmony_ci	if (np) {
4638c2ecf20Sopenharmony_ci		void __iomem *base = of_iomap(np, 0);
4648c2ecf20Sopenharmony_ci		if (base) {
4658c2ecf20Sopenharmony_ci			tegra_enable_fuse_clk(base);
4668c2ecf20Sopenharmony_ci			iounmap(base);
4678c2ecf20Sopenharmony_ci		} else {
4688c2ecf20Sopenharmony_ci			pr_err("failed to map clock registers\n");
4698c2ecf20Sopenharmony_ci			return -ENXIO;
4708c2ecf20Sopenharmony_ci		}
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	fuse->base = ioremap(regs.start, resource_size(&regs));
4748c2ecf20Sopenharmony_ci	if (!fuse->base) {
4758c2ecf20Sopenharmony_ci		pr_err("failed to map FUSE registers\n");
4768c2ecf20Sopenharmony_ci		return -ENXIO;
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	fuse->soc->init(fuse);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n",
4828c2ecf20Sopenharmony_ci		tegra_revision_name[tegra_sku_info.revision],
4838c2ecf20Sopenharmony_ci		tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id,
4848c2ecf20Sopenharmony_ci		tegra_sku_info.soc_process_id);
4858c2ecf20Sopenharmony_ci	pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n",
4868c2ecf20Sopenharmony_ci		 tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (fuse->soc->lookups) {
4898c2ecf20Sopenharmony_ci		size_t size = sizeof(*fuse->lookups) * fuse->soc->num_lookups;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		fuse->lookups = kmemdup(fuse->soc->lookups, size, GFP_KERNEL);
4928c2ecf20Sopenharmony_ci		if (!fuse->lookups)
4938c2ecf20Sopenharmony_ci			return -ENOMEM;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups);
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	return 0;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ciearly_initcall(tegra_init_fuse);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64
5038c2ecf20Sopenharmony_cistatic int __init tegra_init_soc(void)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct device_node *np;
5068c2ecf20Sopenharmony_ci	struct device *soc;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/* make sure we're running on Tegra */
5098c2ecf20Sopenharmony_ci	np = of_find_matching_node(NULL, tegra_fuse_match);
5108c2ecf20Sopenharmony_ci	if (!np)
5118c2ecf20Sopenharmony_ci		return 0;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	of_node_put(np);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	soc = tegra_soc_device_register();
5168c2ecf20Sopenharmony_ci	if (IS_ERR(soc)) {
5178c2ecf20Sopenharmony_ci		pr_err("failed to register SoC device: %ld\n", PTR_ERR(soc));
5188c2ecf20Sopenharmony_ci		return PTR_ERR(soc);
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	return 0;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_cidevice_initcall(tegra_init_soc);
5248c2ecf20Sopenharmony_ci#endif
525