summaryrefslogtreecommitdiff
path: root/drivers/soc/tegra/fuse/fuse-tegra.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-09-01 23:00:04 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2015-09-01 23:00:04 +0300
commit102178108e2246cb4b329d3fb7872cd3d7120205 (patch)
tree3c0720bd96e613631d3983bba385fc675dceb08e /drivers/soc/tegra/fuse/fuse-tegra.c
parent50686e8a3aed2f5d295e9d2e79ff43df461c7b76 (diff)
parent21815b9a24c6e6d3488703609561bd2892d3d9f3 (diff)
downloadlinux-102178108e2246cb4b329d3fb7872cd3d7120205.tar.xz
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Olof Johansson: "Some releases this branch is nearly empty, others we have more stuff. It tends to gather drivers that need SoC modification or dependencies such that they have to (also) go in through our tree. For this release, we have merged in part of the reset controller tree (with handshake that the parts we have merged in will remain stable), as well as dependencies on a few clock branches. In general, new items here are: - Qualcomm driver for SMM/SMD, which is how they communicate with the coprocessors on (some) of their platforms - memory controller work for ARM's PL172 memory controller - reset drivers for various platforms - PMU power domain support for Marvell platforms - Tegra support for T132/T210 SoCs: PMC, fuse, memory controller per-SoC support" * tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (49 commits) ARM: tegra: cpuidle: implement cpuidle_state.enter_freeze() ARM: tegra: Disable cpuidle if PSCI is available soc/tegra: pmc: Use existing pclk reference soc/tegra: pmc: Remove unnecessary return statement soc: tegra: Remove redundant $(CONFIG_ARCH_TEGRA) in Makefile memory: tegra: Add Tegra210 support memory: tegra: Add support for a variable-size client ID bitfield clk: shmobile: rz: Add CPG/MSTP Clock Domain support clk: shmobile: rcar-gen2: Add CPG/MSTP Clock Domain support clk: shmobile: r8a7779: Add CPG/MSTP Clock Domain support clk: shmobile: r8a7778: Add CPG/MSTP Clock Domain support clk: shmobile: Add CPG/MSTP Clock Domain support ARM: dove: create a proper PMU driver for power domains, PMU IRQs and resets reset: reset-zynq: Adding support for Xilinx Zynq reset controller. docs: dts: Added documentation for Xilinx Zynq Reset Controller bindings. MIPS: ath79: Add the reset controller to the AR9132 dtsi reset: Add a driver for the reset controller on the AR71XX/AR9XXX devicetree: Add bindings for the ATH79 reset controller reset: socfpga: Update reset-socfpga to read the altr,modrst-offset property doc: dt: add documentation for lpc1850-rgu reset driver ...
Diffstat (limited to 'drivers/soc/tegra/fuse/fuse-tegra.c')
-rw-r--r--drivers/soc/tegra/fuse/fuse-tegra.c257
1 files changed, 204 insertions, 53 deletions
diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c
index c0d660f1aaac..de2c1bfe28b5 100644
--- a/drivers/soc/tegra/fuse/fuse-tegra.c
+++ b/drivers/soc/tegra/fuse/fuse-tegra.c
@@ -15,9 +15,10 @@
*
*/
+#include <linux/clk.h>
#include <linux/device.h>
#include <linux/kobject.h>
-#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -28,8 +29,6 @@
#include "fuse.h"
-static u32 (*fuse_readl)(const unsigned int offset);
-static int fuse_size;
struct tegra_sku_info tegra_sku_info;
EXPORT_SYMBOL(tegra_sku_info);
@@ -42,11 +41,11 @@ static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
[TEGRA_REVISION_A04] = "A04",
};
-static u8 fuse_readb(const unsigned int offset)
+static u8 fuse_readb(struct tegra_fuse *fuse, unsigned int offset)
{
u32 val;
- val = fuse_readl(round_down(offset, 4));
+ val = fuse->read(fuse, round_down(offset, 4));
val >>= (offset % 4) * 8;
val &= 0xff;
@@ -54,19 +53,21 @@ static u8 fuse_readb(const unsigned int offset)
}
static ssize_t fuse_read(struct file *fd, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
- loff_t pos, size_t size)
+ 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 >= fuse_size)
+ if (pos < 0 || pos >= attr->size)
return 0;
- if (size > fuse_size - pos)
- size = fuse_size - pos;
+ if (size > attr->size - pos)
+ size = attr->size - pos;
for (i = 0; i < size; i++)
- buf[i] = fuse_readb(pos + i);
+ buf[i] = fuse_readb(fuse, pos + i);
return i;
}
@@ -76,89 +77,239 @@ static struct bin_attribute fuse_bin_attr = {
.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", },
{ .compatible = "nvidia,tegra114-car", },
{ .compatible = "nvidia,tegra124-car", },
{ .compatible = "nvidia,tegra132-car", },
+ { .compatible = "nvidia,tegra210-car", },
{},
};
-static void tegra_enable_fuse_clk(void __iomem *base)
+static struct tegra_fuse *fuse = &(struct tegra_fuse) {
+ .base = NULL,
+ .soc = NULL,
+};
+
+static const struct of_device_id tegra_fuse_match[] = {
+#ifdef CONFIG_ARCH_TEGRA_210_SOC
+ { .compatible = "nvidia,tegra210-efuse", .data = &tegra210_fuse_soc },
+#endif
+#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 */ }
+};
+
+static int tegra_fuse_probe(struct platform_device *pdev)
{
- u32 reg;
+ void __iomem *base = fuse->base;
+ struct resource *res;
+ int err;
+
+ /* take over the memory region from the early initialization */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fuse->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fuse->base))
+ return PTR_ERR(fuse->base);
+
+ fuse->clk = devm_clk_get(&pdev->dev, "fuse");
+ if (IS_ERR(fuse->clk)) {
+ dev_err(&pdev->dev, "failed to get FUSE clock: %ld",
+ PTR_ERR(fuse->clk));
+ return PTR_ERR(fuse->clk);
+ }
- reg = readl_relaxed(base + 0x48);
- reg |= 1 << 28;
- writel(reg, base + 0x48);
+ platform_set_drvdata(pdev, fuse);
+ fuse->dev = &pdev->dev;
- /*
- * 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);
+ if (fuse->soc->probe) {
+ err = fuse->soc->probe(fuse);
+ if (err < 0)
+ return err;
+ }
+
+ if (tegra_fuse_create_sysfs(&pdev->dev, fuse->soc->info->size,
+ fuse->soc->info))
+ return -ENODEV;
+
+ /* release the early I/O memory mapping */
+ iounmap(base);
+
+ return 0;
+}
+
+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,
+};
+module_platform_driver(tegra_fuse_driver);
+
+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);
}
int tegra_fuse_readl(unsigned long offset, u32 *value)
{
- if (!fuse_readl)
+ if (!fuse->read)
return -EPROBE_DEFER;
- *value = fuse_readl(offset);
+ *value = fuse->read(fuse, offset);
return 0;
}
EXPORT_SYMBOL(tegra_fuse_readl);
-int tegra_fuse_create_sysfs(struct device *dev, int size,
- u32 (*readl)(const unsigned int offset))
+static void tegra_enable_fuse_clk(void __iomem *base)
{
- if (fuse_size)
- return -ENODEV;
-
- fuse_bin_attr.size = size;
- fuse_bin_attr.read = fuse_read;
+ u32 reg;
- fuse_size = size;
- fuse_readl = readl;
+ reg = readl_relaxed(base + 0x48);
+ reg |= 1 << 28;
+ writel(reg, base + 0x48);
- return device_create_bin_file(dev, &fuse_bin_attr);
+ /*
+ * 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);
}
static int __init tegra_init_fuse(void)
{
+ const struct of_device_id *match;
struct device_node *np;
- void __iomem *car_base;
-
- if (!soc_is_tegra())
- return 0;
+ struct resource regs;
tegra_init_apbmisc();
- np = of_find_matching_node(NULL, car_match);
- car_base = of_iomap(np, 0);
- if (car_base) {
- tegra_enable_fuse_clk(car_base);
- iounmap(car_base);
+ 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;
+ }
} else {
- pr_err("Could not enable fuse clk. ioremap tegra car failed.\n");
+ /*
+ * Extract information from the device tree if we've found a
+ * matching node.
+ */
+ if (of_address_to_resource(np, 0, &regs) < 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;
+ }
+ }
+
+ fuse->base = ioremap_nocache(regs.start, resource_size(&regs));
+ if (!fuse->base) {
+ pr_err("failed to map FUSE registers\n");
return -ENXIO;
}
- if (tegra_get_chip_id() == TEGRA20)
- tegra20_init_fuse_early();
- else
- tegra30_init_fuse_early();
+ fuse->soc->init(fuse);
- pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n",
+ pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n",
tegra_revision_name[tegra_sku_info.revision],
tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id,
- tegra_sku_info.core_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);
+ 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);
return 0;
}