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, ®s) < 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(®s)); 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