aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/soc/tegra/fuse/fuse-tegra.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc/tegra/fuse/fuse-tegra.c')
-rw-r--r--drivers/soc/tegra/fuse/fuse-tegra.c198
1 files changed, 146 insertions, 52 deletions
diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c
index 3eb44e65b326..4d719d4b8d5a 100644
--- a/drivers/soc/tegra/fuse/fuse-tegra.c
+++ b/drivers/soc/tegra/fuse/fuse-tegra.c
@@ -8,6 +8,8 @@
#include <linux/kobject.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/nvmem-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
@@ -31,50 +33,6 @@ static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
[TEGRA_REVISION_A04] = "A04",
};
-static u8 fuse_readb(struct tegra_fuse *fuse, unsigned int offset)
-{
- u32 val;
-
- val = fuse->read(fuse, round_down(offset, 4));
- val >>= (offset % 4) * 8;
- val &= 0xff;
-
- return val;
-}
-
-static ssize_t fuse_read(struct file *fd, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
- loff_t pos, size_t size)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct tegra_fuse *fuse = dev_get_drvdata(dev);
- int i;
-
- if (pos < 0 || pos >= attr->size)
- return 0;
-
- if (size > attr->size - pos)
- size = attr->size - pos;
-
- for (i = 0; i < size; i++)
- buf[i] = fuse_readb(fuse, pos + i);
-
- return i;
-}
-
-static struct bin_attribute fuse_bin_attr = {
- .attr = { .name = "fuse", .mode = S_IRUGO, },
- .read = fuse_read,
-};
-
-static int tegra_fuse_create_sysfs(struct device *dev, unsigned int size,
- const struct tegra_fuse_info *info)
-{
- fuse_bin_attr.size = size;
-
- return device_create_bin_file(dev, &fuse_bin_attr);
-}
-
static const struct of_device_id car_match[] __initconst = {
{ .compatible = "nvidia,tegra20-car", },
{ .compatible = "nvidia,tegra30-car", },
@@ -115,9 +73,111 @@ static const struct of_device_id tegra_fuse_match[] = {
{ /* sentinel */ }
};
+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;
+}
+
+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,
+ },
+};
+
static int tegra_fuse_probe(struct platform_device *pdev)
{
void __iomem *base = fuse->base;
+ struct nvmem_config nvmem;
struct resource *res;
int err;
@@ -146,20 +206,42 @@ static int tegra_fuse_probe(struct platform_device *pdev)
if (fuse->soc->probe) {
err = fuse->soc->probe(fuse);
- if (err < 0) {
- fuse->base = base;
- return err;
- }
+ if (err < 0)
+ goto restore;
}
- if (tegra_fuse_create_sysfs(&pdev->dev, fuse->soc->info->size,
- fuse->soc->info))
- return -ENODEV;
+ memset(&nvmem, 0, sizeof(nvmem));
+ nvmem.dev = &pdev->dev;
+ nvmem.name = "fuse";
+ nvmem.id = -1;
+ nvmem.owner = THIS_MODULE;
+ nvmem.cells = tegra_fuse_cells;
+ nvmem.ncells = ARRAY_SIZE(tegra_fuse_cells);
+ 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);
+ goto restore;
+ }
/* release the early I/O memory mapping */
iounmap(base);
return 0;
+
+restore:
+ fuse->base = base;
+ return err;
}
static struct platform_driver tegra_fuse_driver = {
@@ -186,9 +268,12 @@ u32 __init tegra_fuse_read_early(unsigned int offset)
int tegra_fuse_readl(unsigned long offset, u32 *value)
{
- if (!fuse->read)
+ if (!fuse->read || !fuse->clk)
return -EPROBE_DEFER;
+ if (IS_ERR(fuse->clk))
+ return PTR_ERR(fuse->clk);
+
*value = fuse->read(fuse, offset);
return 0;
@@ -338,6 +423,15 @@ static int __init tegra_init_fuse(void)
pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n",
tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id);
+ 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);
+ }
return 0;
}