2019-05-29 00:10:04 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2014-06-12 22:36:37 +07:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
#include <linux/clk.h>
|
2014-06-12 22:36:37 +07:00
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/kobject.h>
|
2016-11-14 02:03:01 +07:00
|
|
|
#include <linux/init.h>
|
2017-08-17 21:42:17 +07:00
|
|
|
#include <linux/io.h>
|
2019-08-20 20:57:16 +07:00
|
|
|
#include <linux/nvmem-consumer.h>
|
|
|
|
#include <linux/nvmem-provider.h>
|
2014-06-12 22:36:37 +07:00
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_address.h>
|
2017-08-17 21:42:17 +07:00
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/sys_soc.h>
|
2014-06-12 22:36:37 +07:00
|
|
|
|
2014-07-11 16:13:30 +07:00
|
|
|
#include <soc/tegra/common.h>
|
2014-06-12 22:36:37 +07:00
|
|
|
#include <soc/tegra/fuse.h>
|
|
|
|
|
|
|
|
#include "fuse.h"
|
|
|
|
|
|
|
|
struct tegra_sku_info tegra_sku_info;
|
2014-12-02 11:50:32 +07:00
|
|
|
EXPORT_SYMBOL(tegra_sku_info);
|
2014-06-12 22:36:37 +07:00
|
|
|
|
|
|
|
static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
|
|
|
|
[TEGRA_REVISION_UNKNOWN] = "unknown",
|
|
|
|
[TEGRA_REVISION_A01] = "A01",
|
|
|
|
[TEGRA_REVISION_A02] = "A02",
|
|
|
|
[TEGRA_REVISION_A03] = "A03",
|
|
|
|
[TEGRA_REVISION_A03p] = "A03 prime",
|
|
|
|
[TEGRA_REVISION_A04] = "A04",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct of_device_id car_match[] __initconst = {
|
|
|
|
{ .compatible = "nvidia,tegra20-car", },
|
|
|
|
{ .compatible = "nvidia,tegra30-car", },
|
|
|
|
{ .compatible = "nvidia,tegra114-car", },
|
|
|
|
{ .compatible = "nvidia,tegra124-car", },
|
2015-01-09 17:49:33 +07:00
|
|
|
{ .compatible = "nvidia,tegra132-car", },
|
2015-04-29 21:55:57 +07:00
|
|
|
{ .compatible = "nvidia,tegra210-car", },
|
2014-06-12 22:36:37 +07:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
static struct tegra_fuse *fuse = &(struct tegra_fuse) {
|
|
|
|
.base = NULL,
|
|
|
|
.soc = NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct of_device_id tegra_fuse_match[] = {
|
2020-01-03 15:30:17 +07:00
|
|
|
#ifdef CONFIG_ARCH_TEGRA_194_SOC
|
|
|
|
{ .compatible = "nvidia,tegra194-efuse", .data = &tegra194_fuse_soc },
|
|
|
|
#endif
|
2017-03-06 20:47:20 +07:00
|
|
|
#ifdef CONFIG_ARCH_TEGRA_186_SOC
|
|
|
|
{ .compatible = "nvidia,tegra186-efuse", .data = &tegra186_fuse_soc },
|
|
|
|
#endif
|
2015-04-29 21:55:57 +07:00
|
|
|
#ifdef CONFIG_ARCH_TEGRA_210_SOC
|
|
|
|
{ .compatible = "nvidia,tegra210-efuse", .data = &tegra210_fuse_soc },
|
|
|
|
#endif
|
2015-04-29 21:54:04 +07:00
|
|
|
#ifdef CONFIG_ARCH_TEGRA_132_SOC
|
|
|
|
{ .compatible = "nvidia,tegra132-efuse", .data = &tegra124_fuse_soc },
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARCH_TEGRA_124_SOC
|
|
|
|
{ .compatible = "nvidia,tegra124-efuse", .data = &tegra124_fuse_soc },
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARCH_TEGRA_114_SOC
|
|
|
|
{ .compatible = "nvidia,tegra114-efuse", .data = &tegra114_fuse_soc },
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
|
|
|
|
{ .compatible = "nvidia,tegra30-efuse", .data = &tegra30_fuse_soc },
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
|
|
|
{ .compatible = "nvidia,tegra20-efuse", .data = &tegra20_fuse_soc },
|
|
|
|
#endif
|
|
|
|
{ /* sentinel */ }
|
|
|
|
};
|
|
|
|
|
2019-08-20 20:57:16 +07:00
|
|
|
static int tegra_fuse_read(void *priv, unsigned int offset, void *value,
|
|
|
|
size_t bytes)
|
|
|
|
{
|
|
|
|
unsigned int count = bytes / 4, i;
|
|
|
|
struct tegra_fuse *fuse = priv;
|
|
|
|
u32 *buffer = value;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
buffer[i] = fuse->read(fuse, offset + i * 4);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-20 20:59:49 +07:00
|
|
|
static const struct nvmem_cell_info tegra_fuse_cells[] = {
|
|
|
|
{
|
|
|
|
.name = "tsensor-cpu1",
|
|
|
|
.offset = 0x084,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "tsensor-cpu2",
|
|
|
|
.offset = 0x088,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "tsensor-cpu0",
|
|
|
|
.offset = 0x098,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "xusb-pad-calibration",
|
|
|
|
.offset = 0x0f0,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "tsensor-cpu3",
|
|
|
|
.offset = 0x12c,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "sata-calibration",
|
|
|
|
.offset = 0x124,
|
|
|
|
.bytes = 1,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 2,
|
|
|
|
}, {
|
|
|
|
.name = "tsensor-gpu",
|
|
|
|
.offset = 0x154,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "tsensor-mem0",
|
|
|
|
.offset = 0x158,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "tsensor-mem1",
|
|
|
|
.offset = 0x15c,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "tsensor-pllx",
|
|
|
|
.offset = 0x160,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "tsensor-common",
|
|
|
|
.offset = 0x180,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "tsensor-realignment",
|
|
|
|
.offset = 0x1fc,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "gpu-calibration",
|
|
|
|
.offset = 0x204,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
}, {
|
|
|
|
.name = "xusb-pad-calibration-ext",
|
|
|
|
.offset = 0x250,
|
|
|
|
.bytes = 4,
|
|
|
|
.bit_offset = 0,
|
|
|
|
.nbits = 32,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
static int tegra_fuse_probe(struct platform_device *pdev)
|
2014-06-12 22:36:37 +07:00
|
|
|
{
|
2015-04-29 21:54:04 +07:00
|
|
|
void __iomem *base = fuse->base;
|
2019-08-20 20:57:16 +07:00
|
|
|
struct nvmem_config nvmem;
|
2015-04-29 21:54:04 +07:00
|
|
|
struct resource *res;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* take over the memory region from the early initialization */
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
2017-10-20 05:08:03 +07:00
|
|
|
fuse->phys = res->start;
|
2015-04-29 21:54:04 +07:00
|
|
|
fuse->base = devm_ioremap_resource(&pdev->dev, res);
|
2018-12-30 22:58:08 +07:00
|
|
|
if (IS_ERR(fuse->base)) {
|
|
|
|
err = PTR_ERR(fuse->base);
|
|
|
|
fuse->base = base;
|
|
|
|
return err;
|
|
|
|
}
|
2015-04-29 21:54:04 +07:00
|
|
|
|
|
|
|
fuse->clk = devm_clk_get(&pdev->dev, "fuse");
|
|
|
|
if (IS_ERR(fuse->clk)) {
|
2019-06-04 22:38:38 +07:00
|
|
|
if (PTR_ERR(fuse->clk) != -EPROBE_DEFER)
|
|
|
|
dev_err(&pdev->dev, "failed to get FUSE clock: %ld",
|
|
|
|
PTR_ERR(fuse->clk));
|
|
|
|
|
2018-12-30 22:58:08 +07:00
|
|
|
fuse->base = base;
|
2015-04-29 21:54:04 +07:00
|
|
|
return PTR_ERR(fuse->clk);
|
|
|
|
}
|
2014-06-12 22:36:37 +07:00
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
platform_set_drvdata(pdev, fuse);
|
|
|
|
fuse->dev = &pdev->dev;
|
2014-06-12 22:36:37 +07:00
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
if (fuse->soc->probe) {
|
|
|
|
err = fuse->soc->probe(fuse);
|
2019-08-20 20:53:44 +07:00
|
|
|
if (err < 0)
|
|
|
|
goto restore;
|
2015-04-29 21:54:04 +07:00
|
|
|
}
|
|
|
|
|
2019-08-20 20:57:16 +07:00
|
|
|
memset(&nvmem, 0, sizeof(nvmem));
|
|
|
|
nvmem.dev = &pdev->dev;
|
|
|
|
nvmem.name = "fuse";
|
|
|
|
nvmem.id = -1;
|
|
|
|
nvmem.owner = THIS_MODULE;
|
2019-08-20 20:59:49 +07:00
|
|
|
nvmem.cells = tegra_fuse_cells;
|
|
|
|
nvmem.ncells = ARRAY_SIZE(tegra_fuse_cells);
|
2019-08-20 20:57:16 +07:00
|
|
|
nvmem.type = NVMEM_TYPE_OTP;
|
|
|
|
nvmem.read_only = true;
|
|
|
|
nvmem.root_only = true;
|
|
|
|
nvmem.reg_read = tegra_fuse_read;
|
|
|
|
nvmem.size = fuse->soc->info->size;
|
|
|
|
nvmem.word_size = 4;
|
|
|
|
nvmem.stride = 4;
|
|
|
|
nvmem.priv = fuse;
|
|
|
|
|
|
|
|
fuse->nvmem = devm_nvmem_register(&pdev->dev, &nvmem);
|
|
|
|
if (IS_ERR(fuse->nvmem)) {
|
|
|
|
err = PTR_ERR(fuse->nvmem);
|
|
|
|
dev_err(&pdev->dev, "failed to register NVMEM device: %d\n",
|
|
|
|
err);
|
2019-08-20 20:53:44 +07:00
|
|
|
goto restore;
|
|
|
|
}
|
2015-04-29 21:54:04 +07:00
|
|
|
|
|
|
|
/* release the early I/O memory mapping */
|
|
|
|
iounmap(base);
|
|
|
|
|
|
|
|
return 0;
|
2019-08-20 20:53:44 +07:00
|
|
|
|
|
|
|
restore:
|
|
|
|
fuse->base = base;
|
|
|
|
return err;
|
2015-04-29 21:54:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver tegra_fuse_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = "tegra-fuse",
|
|
|
|
.of_match_table = tegra_fuse_match,
|
|
|
|
.suppress_bind_attrs = true,
|
|
|
|
},
|
|
|
|
.probe = tegra_fuse_probe,
|
|
|
|
};
|
2016-11-14 02:03:01 +07:00
|
|
|
builtin_platform_driver(tegra_fuse_driver);
|
2015-04-29 21:54:04 +07:00
|
|
|
|
|
|
|
bool __init tegra_fuse_read_spare(unsigned int spare)
|
|
|
|
{
|
|
|
|
unsigned int offset = fuse->soc->info->spare + spare * 4;
|
|
|
|
|
|
|
|
return fuse->read_early(fuse, offset) & 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 __init tegra_fuse_read_early(unsigned int offset)
|
|
|
|
{
|
|
|
|
return fuse->read_early(fuse, offset);
|
2014-06-12 22:36:37 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
int tegra_fuse_readl(unsigned long offset, u32 *value)
|
|
|
|
{
|
2019-09-03 17:56:52 +07:00
|
|
|
if (!fuse->read || !fuse->clk)
|
2014-06-12 22:36:37 +07:00
|
|
|
return -EPROBE_DEFER;
|
|
|
|
|
2019-09-03 17:56:52 +07:00
|
|
|
if (IS_ERR(fuse->clk))
|
|
|
|
return PTR_ERR(fuse->clk);
|
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
*value = fuse->read(fuse, offset);
|
2014-06-12 22:36:37 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tegra_fuse_readl);
|
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
static void tegra_enable_fuse_clk(void __iomem *base)
|
2014-06-12 22:36:37 +07:00
|
|
|
{
|
2015-04-29 21:54:04 +07:00
|
|
|
u32 reg;
|
2014-06-12 22:36:37 +07:00
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
reg = readl_relaxed(base + 0x48);
|
|
|
|
reg |= 1 << 28;
|
|
|
|
writel(reg, base + 0x48);
|
2014-06-12 22:36:37 +07:00
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
/*
|
|
|
|
* Enable FUSE clock. This needs to be hardcoded because the clock
|
|
|
|
* subsystem is not active during early boot.
|
|
|
|
*/
|
|
|
|
reg = readl(base + 0x14);
|
|
|
|
reg |= 1 << 7;
|
|
|
|
writel(reg, base + 0x14);
|
2014-06-12 22:36:37 +07:00
|
|
|
}
|
|
|
|
|
soc/tegra: fuse: Add custom SoC attributes
Add a custom SoC attribute for Tegra to expose the HIDREV register
fields to userspace via the sysfs. This register provides additional
details about the type of device (eg, silicon, FPGA, etc) as well as
revision. Exposing this information is useful for identifying the
exact device revision and device type.
For Tegra devices up until Tegra186, the majorrev and minorrev fields of
the HIDREV register are used to determine the device revision and device
type. For Tegra194, the majorrev and minorrev fields only determine the
revision. Starting with Tegra194, there is an additional field,
pre_si_platform (which occupies bits 20-23), that now determines device
type. Therefore, for all Tegra devices, add a custom SoC attribute for
the majorrev and minorrev fields and for Tegra194 add an additional
attribute for the pre_si_platform field.
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
2020-04-17 19:39:48 +07:00
|
|
|
static ssize_t major_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
return sprintf(buf, "%d\n", tegra_get_major_rev());
|
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR_RO(major);
|
|
|
|
|
|
|
|
static ssize_t minor_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
return sprintf(buf, "%d\n", tegra_get_minor_rev());
|
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR_RO(minor);
|
|
|
|
|
|
|
|
static struct attribute *tegra_soc_attr[] = {
|
|
|
|
&dev_attr_major.attr,
|
|
|
|
&dev_attr_minor.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct attribute_group tegra_soc_attr_group = {
|
|
|
|
.attrs = tegra_soc_attr,
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef CONFIG_ARCH_TEGRA_194_SOC
|
|
|
|
static ssize_t platform_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Displays the value in the 'pre_si_platform' field of the HIDREV
|
|
|
|
* register for Tegra194 devices. A value of 0 indicates that the
|
|
|
|
* platform type is silicon and all other non-zero values indicate
|
|
|
|
* the type of simulation platform is being used.
|
|
|
|
*/
|
|
|
|
return sprintf(buf, "%d\n", (tegra_read_chipid() >> 20) & 0xf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR_RO(platform);
|
|
|
|
|
|
|
|
static struct attribute *tegra194_soc_attr[] = {
|
|
|
|
&dev_attr_major.attr,
|
|
|
|
&dev_attr_minor.attr,
|
|
|
|
&dev_attr_platform.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct attribute_group tegra194_soc_attr_group = {
|
|
|
|
.attrs = tegra194_soc_attr,
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2017-08-17 21:42:17 +07:00
|
|
|
struct device * __init tegra_soc_device_register(void)
|
|
|
|
{
|
|
|
|
struct soc_device_attribute *attr;
|
|
|
|
struct soc_device *dev;
|
|
|
|
|
|
|
|
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
|
|
|
|
if (!attr)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
attr->family = kasprintf(GFP_KERNEL, "Tegra");
|
2020-04-17 19:40:46 +07:00
|
|
|
attr->revision = kasprintf(GFP_KERNEL, "%s",
|
|
|
|
tegra_revision_name[tegra_sku_info.revision]);
|
2017-08-17 21:42:17 +07:00
|
|
|
attr->soc_id = kasprintf(GFP_KERNEL, "%u", tegra_get_chip_id());
|
soc/tegra: fuse: Add custom SoC attributes
Add a custom SoC attribute for Tegra to expose the HIDREV register
fields to userspace via the sysfs. This register provides additional
details about the type of device (eg, silicon, FPGA, etc) as well as
revision. Exposing this information is useful for identifying the
exact device revision and device type.
For Tegra devices up until Tegra186, the majorrev and minorrev fields of
the HIDREV register are used to determine the device revision and device
type. For Tegra194, the majorrev and minorrev fields only determine the
revision. Starting with Tegra194, there is an additional field,
pre_si_platform (which occupies bits 20-23), that now determines device
type. Therefore, for all Tegra devices, add a custom SoC attribute for
the majorrev and minorrev fields and for Tegra194 add an additional
attribute for the pre_si_platform field.
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
2020-04-17 19:39:48 +07:00
|
|
|
attr->custom_attr_group = fuse->soc->soc_attr_group;
|
2017-08-17 21:42:17 +07:00
|
|
|
|
|
|
|
dev = soc_device_register(attr);
|
|
|
|
if (IS_ERR(dev)) {
|
|
|
|
kfree(attr->soc_id);
|
|
|
|
kfree(attr->revision);
|
|
|
|
kfree(attr->family);
|
|
|
|
kfree(attr);
|
|
|
|
return ERR_CAST(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return soc_device_to_device(dev);
|
|
|
|
}
|
|
|
|
|
2014-07-11 16:13:30 +07:00
|
|
|
static int __init tegra_init_fuse(void)
|
2014-06-12 22:36:37 +07:00
|
|
|
{
|
2015-04-29 21:54:04 +07:00
|
|
|
const struct of_device_id *match;
|
2014-06-12 22:36:37 +07:00
|
|
|
struct device_node *np;
|
2015-04-29 21:54:04 +07:00
|
|
|
struct resource regs;
|
2014-07-11 16:13:30 +07:00
|
|
|
|
2014-06-12 22:36:37 +07:00
|
|
|
tegra_init_apbmisc();
|
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
np = of_find_matching_node_and_match(NULL, tegra_fuse_match, &match);
|
|
|
|
if (!np) {
|
|
|
|
/*
|
|
|
|
* Fall back to legacy initialization for 32-bit ARM only. All
|
|
|
|
* 64-bit ARM device tree files for Tegra are required to have
|
|
|
|
* a FUSE node.
|
|
|
|
*
|
|
|
|
* This is for backwards-compatibility with old device trees
|
|
|
|
* that didn't contain a FUSE node.
|
|
|
|
*/
|
|
|
|
if (IS_ENABLED(CONFIG_ARM) && soc_is_tegra()) {
|
|
|
|
u8 chip = tegra_get_chip_id();
|
|
|
|
|
|
|
|
regs.start = 0x7000f800;
|
|
|
|
regs.end = 0x7000fbff;
|
|
|
|
regs.flags = IORESOURCE_MEM;
|
|
|
|
|
|
|
|
switch (chip) {
|
|
|
|
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
|
|
|
case TEGRA20:
|
|
|
|
fuse->soc = &tegra20_fuse_soc;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
|
|
|
|
case TEGRA30:
|
|
|
|
fuse->soc = &tegra30_fuse_soc;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_ARCH_TEGRA_114_SOC
|
|
|
|
case TEGRA114:
|
|
|
|
fuse->soc = &tegra114_fuse_soc;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_ARCH_TEGRA_124_SOC
|
|
|
|
case TEGRA124:
|
|
|
|
fuse->soc = &tegra124_fuse_soc;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
pr_warn("Unsupported SoC: %02x\n", chip);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* At this point we're not running on Tegra, so play
|
|
|
|
* nice with multi-platform kernels.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
2014-06-12 22:36:37 +07:00
|
|
|
} else {
|
2015-04-29 21:54:04 +07:00
|
|
|
/*
|
|
|
|
* Extract information from the device tree if we've found a
|
|
|
|
* matching node.
|
|
|
|
*/
|
|
|
|
if (of_address_to_resource(np, 0, ®s) < 0) {
|
|
|
|
pr_err("failed to get FUSE register\n");
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
fuse->soc = match->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
np = of_find_matching_node(NULL, car_match);
|
|
|
|
if (np) {
|
|
|
|
void __iomem *base = of_iomap(np, 0);
|
|
|
|
if (base) {
|
|
|
|
tegra_enable_fuse_clk(base);
|
|
|
|
iounmap(base);
|
|
|
|
} else {
|
|
|
|
pr_err("failed to map clock registers\n");
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-06 15:43:50 +07:00
|
|
|
fuse->base = ioremap(regs.start, resource_size(®s));
|
2015-04-29 21:54:04 +07:00
|
|
|
if (!fuse->base) {
|
|
|
|
pr_err("failed to map FUSE registers\n");
|
2014-07-11 16:13:30 +07:00
|
|
|
return -ENXIO;
|
2014-06-12 22:36:37 +07:00
|
|
|
}
|
|
|
|
|
2015-04-29 21:54:04 +07:00
|
|
|
fuse->soc->init(fuse);
|
2014-06-12 22:36:37 +07:00
|
|
|
|
2015-03-23 20:44:08 +07:00
|
|
|
pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n",
|
2014-06-12 22:36:37 +07:00
|
|
|
tegra_revision_name[tegra_sku_info.revision],
|
|
|
|
tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id,
|
2015-03-23 20:44:08 +07:00
|
|
|
tegra_sku_info.soc_process_id);
|
|
|
|
pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n",
|
|
|
|
tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id);
|
2014-07-11 16:13:30 +07:00
|
|
|
|
2019-08-20 21:01:04 +07:00
|
|
|
if (fuse->soc->lookups) {
|
|
|
|
size_t size = sizeof(*fuse->lookups) * fuse->soc->num_lookups;
|
|
|
|
|
|
|
|
fuse->lookups = kmemdup(fuse->soc->lookups, size, GFP_KERNEL);
|
|
|
|
if (!fuse->lookups)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups);
|
|
|
|
}
|
2017-08-17 21:42:17 +07:00
|
|
|
|
2014-07-11 16:13:30 +07:00
|
|
|
return 0;
|
2014-06-12 22:36:37 +07:00
|
|
|
}
|
2014-07-11 16:13:30 +07:00
|
|
|
early_initcall(tegra_init_fuse);
|
2017-08-17 21:42:17 +07:00
|
|
|
|
|
|
|
#ifdef CONFIG_ARM64
|
|
|
|
static int __init tegra_init_soc(void)
|
|
|
|
{
|
2017-08-23 16:25:08 +07:00
|
|
|
struct device_node *np;
|
2017-08-17 21:42:17 +07:00
|
|
|
struct device *soc;
|
|
|
|
|
2017-08-23 16:25:08 +07:00
|
|
|
/* make sure we're running on Tegra */
|
|
|
|
np = of_find_matching_node(NULL, tegra_fuse_match);
|
|
|
|
if (!np)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
of_node_put(np);
|
|
|
|
|
2017-08-17 21:42:17 +07:00
|
|
|
soc = tegra_soc_device_register();
|
|
|
|
if (IS_ERR(soc)) {
|
|
|
|
pr_err("failed to register SoC device: %ld\n", PTR_ERR(soc));
|
|
|
|
return PTR_ERR(soc);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-08-23 16:25:09 +07:00
|
|
|
device_initcall(tegra_init_soc);
|
2017-08-17 21:42:17 +07:00
|
|
|
#endif
|