diff options
Diffstat (limited to 'drivers/soc/tegra')
-rw-r--r-- | drivers/soc/tegra/Makefile | 6 | ||||
-rw-r--r-- | drivers/soc/tegra/common.c | 2 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/Makefile | 2 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/fuse-tegra.c | 257 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/fuse-tegra20.c | 175 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/fuse-tegra30.c | 232 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/fuse.h | 95 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/speedo-tegra114.c | 22 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/speedo-tegra124.c | 26 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/speedo-tegra20.c | 28 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/speedo-tegra210.c | 184 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/speedo-tegra30.c | 48 | ||||
-rw-r--r-- | drivers/soc/tegra/fuse/tegra-apbmisc.c | 76 | ||||
-rw-r--r-- | drivers/soc/tegra/pmc.c | 121 |
14 files changed, 828 insertions, 446 deletions
diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile index cdaad9d53..ae857ff7d 100644 --- a/drivers/soc/tegra/Makefile +++ b/drivers/soc/tegra/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_ARCH_TEGRA) += fuse/ +obj-y += fuse/ -obj-$(CONFIG_ARCH_TEGRA) += common.o -obj-$(CONFIG_ARCH_TEGRA) += pmc.o +obj-y += common.o +obj-y += pmc.o diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c index a71cb74f3..cd8f41351 100644 --- a/drivers/soc/tegra/common.c +++ b/drivers/soc/tegra/common.c @@ -15,6 +15,8 @@ static const struct of_device_id tegra_machine_match[] = { { .compatible = "nvidia,tegra30", }, { .compatible = "nvidia,tegra114", }, { .compatible = "nvidia,tegra124", }, + { .compatible = "nvidia,tegra132", }, + { .compatible = "nvidia,tegra210", }, { } }; diff --git a/drivers/soc/tegra/fuse/Makefile b/drivers/soc/tegra/fuse/Makefile index 3af357da9..21bc27580 100644 --- a/drivers/soc/tegra/fuse/Makefile +++ b/drivers/soc/tegra/fuse/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += speedo-tegra20.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += speedo-tegra30.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += speedo-tegra114.o obj-$(CONFIG_ARCH_TEGRA_124_SOC) += speedo-tegra124.o +obj-$(CONFIG_ARCH_TEGRA_132_SOC) += speedo-tegra124.o +obj-$(CONFIG_ARCH_TEGRA_210_SOC) += speedo-tegra210.o diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index c0d660f1a..de2c1bfe2 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, ®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; + } + } + + fuse->base = ioremap_nocache(regs.start, resource_size(®s)); + 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; } diff --git a/drivers/soc/tegra/fuse/fuse-tegra20.c b/drivers/soc/tegra/fuse/fuse-tegra20.c index 6acc2c44e..294413a96 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra20.c +++ b/drivers/soc/tegra/fuse/fuse-tegra20.c @@ -34,159 +34,107 @@ #include "fuse.h" #define FUSE_BEGIN 0x100 -#define FUSE_SIZE 0x1f8 #define FUSE_UID_LOW 0x08 #define FUSE_UID_HIGH 0x0c -static phys_addr_t fuse_phys; -static struct clk *fuse_clk; -static void __iomem __initdata *fuse_base; - -static DEFINE_MUTEX(apb_dma_lock); -static DECLARE_COMPLETION(apb_dma_wait); -static struct dma_chan *apb_dma_chan; -static struct dma_slave_config dma_sconfig; -static u32 *apb_buffer; -static dma_addr_t apb_buffer_phys; +static u32 tegra20_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset) +{ + return readl_relaxed(fuse->base + FUSE_BEGIN + offset); +} static void apb_dma_complete(void *args) { - complete(&apb_dma_wait); + struct tegra_fuse *fuse = args; + + complete(&fuse->apbdma.wait); } -static u32 tegra20_fuse_readl(const unsigned int offset) +static u32 tegra20_fuse_read(struct tegra_fuse *fuse, unsigned int offset) { - int ret; - u32 val = 0; + unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; struct dma_async_tx_descriptor *dma_desc; unsigned long time_left; + u32 value = 0; + int err; + + mutex_lock(&fuse->apbdma.lock); - mutex_lock(&apb_dma_lock); + fuse->apbdma.config.src_addr = fuse->apbdma.phys + FUSE_BEGIN + offset; - dma_sconfig.src_addr = fuse_phys + FUSE_BEGIN + offset; - ret = dmaengine_slave_config(apb_dma_chan, &dma_sconfig); - if (ret) + err = dmaengine_slave_config(fuse->apbdma.chan, &fuse->apbdma.config); + if (err) goto out; - dma_desc = dmaengine_prep_slave_single(apb_dma_chan, apb_buffer_phys, - sizeof(u32), DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + dma_desc = dmaengine_prep_slave_single(fuse->apbdma.chan, + fuse->apbdma.phys, + sizeof(u32), DMA_DEV_TO_MEM, + flags); if (!dma_desc) goto out; dma_desc->callback = apb_dma_complete; - dma_desc->callback_param = NULL; + dma_desc->callback_param = fuse; - reinit_completion(&apb_dma_wait); + reinit_completion(&fuse->apbdma.wait); - clk_prepare_enable(fuse_clk); + clk_prepare_enable(fuse->clk); dmaengine_submit(dma_desc); - dma_async_issue_pending(apb_dma_chan); - time_left = wait_for_completion_timeout(&apb_dma_wait, + dma_async_issue_pending(fuse->apbdma.chan); + time_left = wait_for_completion_timeout(&fuse->apbdma.wait, msecs_to_jiffies(50)); if (WARN(time_left == 0, "apb read dma timed out")) - dmaengine_terminate_all(apb_dma_chan); + dmaengine_terminate_all(fuse->apbdma.chan); else - val = *apb_buffer; + value = *fuse->apbdma.virt; - clk_disable_unprepare(fuse_clk); -out: - mutex_unlock(&apb_dma_lock); + clk_disable_unprepare(fuse->clk); - return val; +out: + mutex_unlock(&fuse->apbdma.lock); + return value; } -static const struct of_device_id tegra20_fuse_of_match[] = { - { .compatible = "nvidia,tegra20-efuse" }, - {}, -}; - -static int apb_dma_init(void) +static int tegra20_fuse_probe(struct tegra_fuse *fuse) { dma_cap_mask_t mask; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - apb_dma_chan = dma_request_channel(mask, NULL, NULL); - if (!apb_dma_chan) + + fuse->apbdma.chan = dma_request_channel(mask, NULL, NULL); + if (!fuse->apbdma.chan) return -EPROBE_DEFER; - apb_buffer = dma_alloc_coherent(NULL, sizeof(u32), &apb_buffer_phys, - GFP_KERNEL); - if (!apb_buffer) { - dma_release_channel(apb_dma_chan); + fuse->apbdma.virt = dma_alloc_coherent(fuse->dev, sizeof(u32), + &fuse->apbdma.phys, + GFP_KERNEL); + if (!fuse->apbdma.virt) { + dma_release_channel(fuse->apbdma.chan); return -ENOMEM; } - dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dma_sconfig.src_maxburst = 1; - dma_sconfig.dst_maxburst = 1; + fuse->apbdma.config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + fuse->apbdma.config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + fuse->apbdma.config.src_maxburst = 1; + fuse->apbdma.config.dst_maxburst = 1; - return 0; -} - -static int tegra20_fuse_probe(struct platform_device *pdev) -{ - struct resource *res; - int err; - - fuse_clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(fuse_clk)) { - dev_err(&pdev->dev, "missing clock"); - return PTR_ERR(fuse_clk); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - fuse_phys = res->start; - - err = apb_dma_init(); - if (err) - return err; - - if (tegra_fuse_create_sysfs(&pdev->dev, FUSE_SIZE, tegra20_fuse_readl)) - return -ENODEV; - - dev_dbg(&pdev->dev, "loaded\n"); + init_completion(&fuse->apbdma.wait); + mutex_init(&fuse->apbdma.lock); + fuse->read = tegra20_fuse_read; return 0; } -static struct platform_driver tegra20_fuse_driver = { - .probe = tegra20_fuse_probe, - .driver = { - .name = "tegra20_fuse", - .of_match_table = tegra20_fuse_of_match, - } +static const struct tegra_fuse_info tegra20_fuse_info = { + .read = tegra20_fuse_read, + .size = 0x1f8, + .spare = 0x100, }; -static int __init tegra20_fuse_init(void) -{ - return platform_driver_register(&tegra20_fuse_driver); -} -postcore_initcall(tegra20_fuse_init); - /* Early boot code. This code is called before the devices are created */ -u32 __init tegra20_fuse_early(const unsigned int offset) -{ - return readl_relaxed(fuse_base + FUSE_BEGIN + offset); -} - -bool __init tegra20_spare_fuse_early(int spare_bit) -{ - u32 offset = spare_bit * 4; - bool value; - - value = tegra20_fuse_early(offset + 0x100); - - return value; -} - static void __init tegra20_fuse_add_randomness(void) { u32 randomness[7]; @@ -195,22 +143,27 @@ static void __init tegra20_fuse_add_randomness(void) randomness[1] = tegra_read_straps(); randomness[2] = tegra_read_chipid(); randomness[3] = tegra_sku_info.cpu_process_id << 16; - randomness[3] |= tegra_sku_info.core_process_id; + randomness[3] |= tegra_sku_info.soc_process_id; randomness[4] = tegra_sku_info.cpu_speedo_id << 16; randomness[4] |= tegra_sku_info.soc_speedo_id; - randomness[5] = tegra20_fuse_early(FUSE_UID_LOW); - randomness[6] = tegra20_fuse_early(FUSE_UID_HIGH); + randomness[5] = tegra_fuse_read_early(FUSE_UID_LOW); + randomness[6] = tegra_fuse_read_early(FUSE_UID_HIGH); add_device_randomness(randomness, sizeof(randomness)); } -void __init tegra20_init_fuse_early(void) +static void __init tegra20_fuse_init(struct tegra_fuse *fuse) { - fuse_base = ioremap(TEGRA_FUSE_BASE, TEGRA_FUSE_SIZE); + fuse->read_early = tegra20_fuse_read_early; tegra_init_revision(); - tegra20_init_speedo_data(&tegra_sku_info); + fuse->soc->speedo_init(&tegra_sku_info); tegra20_fuse_add_randomness(); - - iounmap(fuse_base); } + +const struct tegra_fuse_soc tegra20_fuse_soc = { + .init = tegra20_fuse_init, + .speedo_init = tegra20_init_speedo_data, + .probe = tegra20_fuse_probe, + .info = &tegra20_fuse_info, +}; diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index 4d2f71bf6..882607bca 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -42,113 +42,33 @@ #define FUSE_HAS_REVISION_INFO BIT(0) -enum speedo_idx { - SPEEDO_TEGRA30 = 0, - SPEEDO_TEGRA114, - SPEEDO_TEGRA124, -}; - -struct tegra_fuse_info { - int size; - int spare_bit; - enum speedo_idx speedo_idx; -}; - -static void __iomem *fuse_base; -static struct clk *fuse_clk; -static const struct tegra_fuse_info *fuse_info; - -u32 tegra30_fuse_readl(const unsigned int offset) +#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \ + defined(CONFIG_ARCH_TEGRA_114_SOC) || \ + defined(CONFIG_ARCH_TEGRA_124_SOC) || \ + defined(CONFIG_ARCH_TEGRA_132_SOC) || \ + defined(CONFIG_ARCH_TEGRA_210_SOC) +static u32 tegra30_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset) { - u32 val; - - /* - * early in the boot, the fuse clock will be enabled by - * tegra_init_fuse() - */ - - if (fuse_clk) - clk_prepare_enable(fuse_clk); - - val = readl_relaxed(fuse_base + FUSE_BEGIN + offset); - - if (fuse_clk) - clk_disable_unprepare(fuse_clk); - - return val; + return readl_relaxed(fuse->base + FUSE_BEGIN + offset); } -static const struct tegra_fuse_info tegra30_info = { - .size = 0x2a4, - .spare_bit = 0x144, - .speedo_idx = SPEEDO_TEGRA30, -}; - -static const struct tegra_fuse_info tegra114_info = { - .size = 0x2a0, - .speedo_idx = SPEEDO_TEGRA114, -}; - -static const struct tegra_fuse_info tegra124_info = { - .size = 0x300, - .speedo_idx = SPEEDO_TEGRA124, -}; - -static const struct of_device_id tegra30_fuse_of_match[] = { - { .compatible = "nvidia,tegra30-efuse", .data = &tegra30_info }, - { .compatible = "nvidia,tegra114-efuse", .data = &tegra114_info }, - { .compatible = "nvidia,tegra124-efuse", .data = &tegra124_info }, - {}, -}; - -static int tegra30_fuse_probe(struct platform_device *pdev) +static u32 tegra30_fuse_read(struct tegra_fuse *fuse, unsigned int offset) { - const struct of_device_id *of_dev_id; - - of_dev_id = of_match_device(tegra30_fuse_of_match, &pdev->dev); - if (!of_dev_id) - return -ENODEV; + u32 value; + int err; - fuse_clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(fuse_clk)) { - dev_err(&pdev->dev, "missing clock"); - return PTR_ERR(fuse_clk); + err = clk_prepare_enable(fuse->clk); + if (err < 0) { + dev_err(fuse->dev, "failed to enable FUSE clock: %d\n", err); + return 0; } - platform_set_drvdata(pdev, NULL); - - if (tegra_fuse_create_sysfs(&pdev->dev, fuse_info->size, - tegra30_fuse_readl)) - return -ENODEV; + value = readl_relaxed(fuse->base + FUSE_BEGIN + offset); - dev_dbg(&pdev->dev, "loaded\n"); + clk_disable_unprepare(fuse->clk); - return 0; -} - -static struct platform_driver tegra30_fuse_driver = { - .probe = tegra30_fuse_probe, - .driver = { - .name = "tegra_fuse", - .of_match_table = tegra30_fuse_of_match, - } -}; - -static int __init tegra30_fuse_init(void) -{ - return platform_driver_register(&tegra30_fuse_driver); + return value; } -postcore_initcall(tegra30_fuse_init); - -/* Early boot code. This code is called before the devices are created */ - -typedef void (*speedo_f)(struct tegra_sku_info *sku_info); - -static speedo_f __initdata speedo_tbl[] = { - [SPEEDO_TEGRA30] = tegra30_init_speedo_data, - [SPEEDO_TEGRA114] = tegra114_init_speedo_data, - [SPEEDO_TEGRA124] = tegra124_init_speedo_data, -}; static void __init tegra30_fuse_add_randomness(void) { @@ -158,67 +78,83 @@ static void __init tegra30_fuse_add_randomness(void) randomness[1] = tegra_read_straps(); randomness[2] = tegra_read_chipid(); randomness[3] = tegra_sku_info.cpu_process_id << 16; - randomness[3] |= tegra_sku_info.core_process_id; + randomness[3] |= tegra_sku_info.soc_process_id; randomness[4] = tegra_sku_info.cpu_speedo_id << 16; randomness[4] |= tegra_sku_info.soc_speedo_id; - randomness[5] = tegra30_fuse_readl(FUSE_VENDOR_CODE); - randomness[6] = tegra30_fuse_readl(FUSE_FAB_CODE); - randomness[7] = tegra30_fuse_readl(FUSE_LOT_CODE_0); - randomness[8] = tegra30_fuse_readl(FUSE_LOT_CODE_1); - randomness[9] = tegra30_fuse_readl(FUSE_WAFER_ID); - randomness[10] = tegra30_fuse_readl(FUSE_X_COORDINATE); - randomness[11] = tegra30_fuse_readl(FUSE_Y_COORDINATE); + randomness[5] = tegra_fuse_read_early(FUSE_VENDOR_CODE); + randomness[6] = tegra_fuse_read_early(FUSE_FAB_CODE); + randomness[7] = tegra_fuse_read_early(FUSE_LOT_CODE_0); + randomness[8] = tegra_fuse_read_early(FUSE_LOT_CODE_1); + randomness[9] = tegra_fuse_read_early(FUSE_WAFER_ID); + randomness[10] = tegra_fuse_read_early(FUSE_X_COORDINATE); + randomness[11] = tegra_fuse_read_early(FUSE_Y_COORDINATE); add_device_randomness(randomness, sizeof(randomness)); } -static void __init legacy_fuse_init(void) +static void __init tegra30_fuse_init(struct tegra_fuse *fuse) { - switch (tegra_get_chip_id()) { - case TEGRA30: - fuse_info = &tegra30_info; - break; - case TEGRA114: - fuse_info = &tegra114_info; - break; - case TEGRA124: - case TEGRA132: - fuse_info = &tegra124_info; - break; - default: - return; - } + fuse->read_early = tegra30_fuse_read_early; + fuse->read = tegra30_fuse_read; - fuse_base = ioremap(TEGRA_FUSE_BASE, TEGRA_FUSE_SIZE); + tegra_init_revision(); + fuse->soc->speedo_init(&tegra_sku_info); + tegra30_fuse_add_randomness(); } +#endif -bool __init tegra30_spare_fuse(int spare_bit) -{ - u32 offset = fuse_info->spare_bit + spare_bit * 4; +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +static const struct tegra_fuse_info tegra30_fuse_info = { + .read = tegra30_fuse_read, + .size = 0x2a4, + .spare = 0x144, +}; - return tegra30_fuse_readl(offset) & 1; -} +const struct tegra_fuse_soc tegra30_fuse_soc = { + .init = tegra30_fuse_init, + .speedo_init = tegra30_init_speedo_data, + .info = &tegra30_fuse_info, +}; +#endif -void __init tegra30_init_fuse_early(void) -{ - struct device_node *np; - const struct of_device_id *of_match; - - np = of_find_matching_node_and_match(NULL, tegra30_fuse_of_match, - &of_match); - if (np) { - fuse_base = of_iomap(np, 0); - fuse_info = (struct tegra_fuse_info *)of_match->data; - } else - legacy_fuse_init(); - - if (!fuse_base) { - pr_warn("fuse DT node missing and unknown chip id: 0x%02x\n", - tegra_get_chip_id()); - return; - } +#ifdef CONFIG_ARCH_TEGRA_114_SOC +static const struct tegra_fuse_info tegra114_fuse_info = { + .read = tegra30_fuse_read, + .size = 0x2a0, + .spare = 0x180, +}; - tegra_init_revision(); - speedo_tbl[fuse_info->speedo_idx](&tegra_sku_info); - tegra30_fuse_add_randomness(); -} +const struct tegra_fuse_soc tegra114_fuse_soc = { + .init = tegra30_fuse_init, + .speedo_init = tegra114_init_speedo_data, + .info = &tegra114_fuse_info, +}; +#endif + +#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) +static const struct tegra_fuse_info tegra124_fuse_info = { + .read = tegra30_fuse_read, + .size = 0x300, + .spare = 0x200, +}; + +const struct tegra_fuse_soc tegra124_fuse_soc = { + .init = tegra30_fuse_init, + .speedo_init = tegra124_init_speedo_data, + .info = &tegra124_fuse_info, +}; +#endif + +#if defined(CONFIG_ARCH_TEGRA_210_SOC) +static const struct tegra_fuse_info tegra210_fuse_info = { + .read = tegra30_fuse_read, + .size = 0x300, + .spare = 0x280, +}; + +const struct tegra_fuse_soc tegra210_fuse_soc = { + .init = tegra30_fuse_init, + .speedo_init = tegra210_init_speedo_data, + .info = &tegra210_fuse_info, +}; +#endif diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h index 3a398bf35..10c2076d5 100644 --- a/drivers/soc/tegra/fuse/fuse.h +++ b/drivers/soc/tegra/fuse/fuse.h @@ -19,53 +19,90 @@ #ifndef __DRIVERS_MISC_TEGRA_FUSE_H #define __DRIVERS_MISC_TEGRA_FUSE_H -#define TEGRA_FUSE_BASE 0x7000f800 -#define TEGRA_FUSE_SIZE 0x400 +#include <linux/dmaengine.h> +#include <linux/types.h> -int tegra_fuse_create_sysfs(struct device *dev, int size, - u32 (*readl)(const unsigned int offset)); +struct tegra_fuse; + +struct tegra_fuse_info { + u32 (*read)(struct tegra_fuse *fuse, unsigned int offset); + unsigned int size; + unsigned int spare; +}; + +struct tegra_fuse_soc { + void (*init)(struct tegra_fuse *fuse); + void (*speedo_init)(struct tegra_sku_info *info); + int (*probe)(struct tegra_fuse *fuse); + + const struct tegra_fuse_info *info; +}; + +struct tegra_fuse { + struct device *dev; + void __iomem *base; + phys_addr_t phys; + struct clk *clk; + + u32 (*read_early)(struct tegra_fuse *fuse, unsigned int offset); + u32 (*read)(struct tegra_fuse *fuse, unsigned int offset); + const struct tegra_fuse_soc *soc; + + /* APBDMA on Tegra20 */ + struct { + struct mutex lock; + struct completion wait; + struct dma_chan *chan; + struct dma_slave_config config; + dma_addr_t phys; + u32 *virt; + } apbdma; +}; -bool tegra30_spare_fuse(int bit); -u32 tegra30_fuse_readl(const unsigned int offset); -void tegra30_init_fuse_early(void); void tegra_init_revision(void); void tegra_init_apbmisc(void); +bool __init tegra_fuse_read_spare(unsigned int spare); +u32 __init tegra_fuse_read_early(unsigned int offset); + #ifdef CONFIG_ARCH_TEGRA_2x_SOC void tegra20_init_speedo_data(struct tegra_sku_info *sku_info); -bool tegra20_spare_fuse_early(int spare_bit); -void tegra20_init_fuse_early(void); -u32 tegra20_fuse_early(const unsigned int offset); -#else -static inline void tegra20_init_speedo_data(struct tegra_sku_info *sku_info) {} -static inline bool tegra20_spare_fuse_early(int spare_bit) -{ - return false; -} -static inline void tegra20_init_fuse_early(void) {} -static inline u32 tegra20_fuse_early(const unsigned int offset) -{ - return 0; -} #endif - #ifdef CONFIG_ARCH_TEGRA_3x_SOC void tegra30_init_speedo_data(struct tegra_sku_info *sku_info); -#else -static inline void tegra30_init_speedo_data(struct tegra_sku_info *sku_info) {} #endif #ifdef CONFIG_ARCH_TEGRA_114_SOC void tegra114_init_speedo_data(struct tegra_sku_info *sku_info); -#else -static inline void tegra114_init_speedo_data(struct tegra_sku_info *sku_info) {} #endif -#ifdef CONFIG_ARCH_TEGRA_124_SOC +#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) void tegra124_init_speedo_data(struct tegra_sku_info *sku_info); -#else -static inline void tegra124_init_speedo_data(struct tegra_sku_info *sku_info) {} +#endif + +#ifdef CONFIG_ARCH_TEGRA_210_SOC +void tegra210_init_speedo_data(struct tegra_sku_info *sku_info); +#endif + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +extern const struct tegra_fuse_soc tegra20_fuse_soc; +#endif + +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +extern const struct tegra_fuse_soc tegra30_fuse_soc; +#endif + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +extern const struct tegra_fuse_soc tegra114_fuse_soc; +#endif + +#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) +extern const struct tegra_fuse_soc tegra124_fuse_soc; +#endif + +#ifdef CONFIG_ARCH_TEGRA_210_SOC +extern const struct tegra_fuse_soc tegra210_fuse_soc; #endif #endif diff --git a/drivers/soc/tegra/fuse/speedo-tegra114.c b/drivers/soc/tegra/fuse/speedo-tegra114.c index 2a6ca036f..1ba41ebbb 100644 --- a/drivers/soc/tegra/fuse/speedo-tegra114.c +++ b/drivers/soc/tegra/fuse/speedo-tegra114.c @@ -22,7 +22,7 @@ #include "fuse.h" -#define CORE_PROCESS_CORNERS 2 +#define SOC_PROCESS_CORNERS 2 #define CPU_PROCESS_CORNERS 2 enum { @@ -31,7 +31,7 @@ enum { THRESHOLD_INDEX_COUNT, }; -static const u32 __initconst core_process_speedos[][CORE_PROCESS_CORNERS] = { +static const u32 __initconst soc_process_speedos[][SOC_PROCESS_CORNERS] = { {1123, UINT_MAX}, {0, UINT_MAX}, }; @@ -74,8 +74,8 @@ static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, } if (rev == TEGRA_REVISION_A01) { - tmp = tegra30_fuse_readl(0x270) << 1; - tmp |= tegra30_fuse_readl(0x26c); + tmp = tegra_fuse_read_early(0x270) << 1; + tmp |= tegra_fuse_read_early(0x26c); if (!tmp) sku_info->cpu_speedo_id = 0; } @@ -84,27 +84,27 @@ static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, void __init tegra114_init_speedo_data(struct tegra_sku_info *sku_info) { u32 cpu_speedo_val; - u32 core_speedo_val; + u32 soc_speedo_val; int threshold; int i; BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != THRESHOLD_INDEX_COUNT); - BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != + BUILD_BUG_ON(ARRAY_SIZE(soc_process_speedos) != THRESHOLD_INDEX_COUNT); rev_sku_to_speedo_ids(sku_info, &threshold); - cpu_speedo_val = tegra30_fuse_readl(0x12c) + 1024; - core_speedo_val = tegra30_fuse_readl(0x134); + cpu_speedo_val = tegra_fuse_read_early(0x12c) + 1024; + soc_speedo_val = tegra_fuse_read_early(0x134); for (i = 0; i < CPU_PROCESS_CORNERS; i++) if (cpu_speedo_val < cpu_process_speedos[threshold][i]) break; sku_info->cpu_process_id = i; - for (i = 0; i < CORE_PROCESS_CORNERS; i++) - if (core_speedo_val < core_process_speedos[threshold][i]) + for (i = 0; i < SOC_PROCESS_CORNERS; i++) + if (soc_speedo_val < soc_process_speedos[threshold][i]) break; - sku_info->core_process_id = i; + sku_info->soc_process_id = i; } diff --git a/drivers/soc/tegra/fuse/speedo-tegra124.c b/drivers/soc/tegra/fuse/speedo-tegra124.c index 46362387d..a63a13410 100644 --- a/drivers/soc/tegra/fuse/speedo-tegra124.c +++ b/drivers/soc/tegra/fuse/speedo-tegra124.c @@ -24,7 +24,7 @@ #define CPU_PROCESS_CORNERS 2 #define GPU_PROCESS_CORNERS 2 -#define CORE_PROCESS_CORNERS 2 +#define SOC_PROCESS_CORNERS 2 #define FUSE_CPU_SPEEDO_0 0x14 #define FUSE_CPU_SPEEDO_1 0x2c @@ -53,7 +53,7 @@ static const u32 __initconst gpu_process_speedos[][GPU_PROCESS_CORNERS] = { {0, UINT_MAX}, }; -static const u32 __initconst core_process_speedos[][CORE_PROCESS_CORNERS] = { +static const u32 __initconst soc_process_speedos[][SOC_PROCESS_CORNERS] = { {2101, UINT_MAX}, {0, UINT_MAX}, }; @@ -119,19 +119,19 @@ void __init tegra124_init_speedo_data(struct tegra_sku_info *sku_info) THRESHOLD_INDEX_COUNT); BUILD_BUG_ON(ARRAY_SIZE(gpu_process_speedos) != THRESHOLD_INDEX_COUNT); - BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != + BUILD_BUG_ON(ARRAY_SIZE(soc_process_speedos) != THRESHOLD_INDEX_COUNT); - cpu_speedo_0_value = tegra30_fuse_readl(FUSE_CPU_SPEEDO_0); + cpu_speedo_0_value = tegra_fuse_read_early(FUSE_CPU_SPEEDO_0); /* GPU Speedo is stored in CPU_SPEEDO_2 */ - sku_info->gpu_speedo_value = tegra30_fuse_readl(FUSE_CPU_SPEEDO_2); + sku_info->gpu_speedo_value = tegra_fuse_read_early(FUSE_CPU_SPEEDO_2); - soc_speedo_0_value = tegra30_fuse_readl(FUSE_SOC_SPEEDO_0); + soc_speedo_0_value = tegra_fuse_read_early(FUSE_SOC_SPEEDO_0); - cpu_iddq_value = tegra30_fuse_readl(FUSE_CPU_IDDQ); - soc_iddq_value = tegra30_fuse_readl(FUSE_SOC_IDDQ); - gpu_iddq_value = tegra30_fuse_readl(FUSE_GPU_IDDQ); + cpu_iddq_value = tegra_fuse_read_early(FUSE_CPU_IDDQ); + soc_iddq_value = tegra_fuse_read_early(FUSE_SOC_IDDQ); + gpu_iddq_value = tegra_fuse_read_early(FUSE_GPU_IDDQ); sku_info->cpu_speedo_value = cpu_speedo_0_value; @@ -143,7 +143,7 @@ void __init tegra124_init_speedo_data(struct tegra_sku_info *sku_info) rev_sku_to_speedo_ids(sku_info, &threshold); - sku_info->cpu_iddq_value = tegra30_fuse_readl(FUSE_CPU_IDDQ); + sku_info->cpu_iddq_value = tegra_fuse_read_early(FUSE_CPU_IDDQ); for (i = 0; i < GPU_PROCESS_CORNERS; i++) if (sku_info->gpu_speedo_value < @@ -157,11 +157,11 @@ void __init tegra124_init_speedo_data(struct tegra_sku_info *sku_info) break; sku_info->cpu_process_id = i; - for (i = 0; i < CORE_PROCESS_CORNERS; i++) + for (i = 0; i < SOC_PROCESS_CORNERS; i++) if (soc_speedo_0_value < - core_process_speedos[threshold][i]) + soc_process_speedos[threshold][i]) break; - sku_info->core_process_id = i; + sku_info->soc_process_id = i; pr_debug("Tegra GPU Speedo ID=%d, Speedo Value=%d\n", sku_info->gpu_speedo_id, sku_info->gpu_speedo_value); diff --git a/drivers/soc/tegra/fuse/speedo-tegra20.c b/drivers/soc/tegra/fuse/speedo-tegra20.c index eff1b63f3..5f7818bf6 100644 --- a/drivers/soc/tegra/fuse/speedo-tegra20.c +++ b/drivers/soc/tegra/fuse/speedo-tegra20.c @@ -28,11 +28,11 @@ #define CPU_SPEEDO_REDUND_MSBIT 39 #define CPU_SPEEDO_REDUND_OFFS (CPU_SPEEDO_REDUND_MSBIT - CPU_SPEEDO_MSBIT) -#define CORE_SPEEDO_LSBIT 40 -#define CORE_SPEEDO_MSBIT 47 -#define CORE_SPEEDO_REDUND_LSBIT 48 -#define CORE_SPEEDO_REDUND_MSBIT 55 -#define CORE_SPEEDO_REDUND_OFFS (CORE_SPEEDO_REDUND_MSBIT - CORE_SPEEDO_MSBIT) +#define SOC_SPEEDO_LSBIT 40 +#define SOC_SPEEDO_MSBIT 47 +#define SOC_SPEEDO_REDUND_LSBIT 48 +#define SOC_SPEEDO_REDUND_MSBIT 55 +#define SOC_SPEEDO_REDUND_OFFS (SOC_SPEEDO_REDUND_MSBIT - SOC_SPEEDO_MSBIT) #define SPEEDO_MULT 4 @@ -56,7 +56,7 @@ static const u32 __initconst cpu_process_speedos[][PROCESS_CORNERS_NUM] = { {316, 331, 383, UINT_MAX}, }; -static const u32 __initconst core_process_speedos[][PROCESS_CORNERS_NUM] = { +static const u32 __initconst soc_process_speedos[][PROCESS_CORNERS_NUM] = { {165, 195, 224, UINT_MAX}, {165, 195, 224, UINT_MAX}, {165, 195, 224, UINT_MAX}, @@ -69,7 +69,7 @@ void __init tegra20_init_speedo_data(struct tegra_sku_info *sku_info) int i; BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != SPEEDO_ID_COUNT); - BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != SPEEDO_ID_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(soc_process_speedos) != SPEEDO_ID_COUNT); if (SPEEDO_ID_SELECT_0(sku_info->revision)) sku_info->soc_speedo_id = SPEEDO_ID_0; @@ -80,8 +80,8 @@ void __init tegra20_init_speedo_data(struct tegra_sku_info *sku_info) val = 0; for (i = CPU_SPEEDO_MSBIT; i >= CPU_SPEEDO_LSBIT; i--) { - reg = tegra20_spare_fuse_early(i) | - tegra20_spare_fuse_early(i + CPU_SPEEDO_REDUND_OFFS); + reg = tegra_fuse_read_spare(i) | + tegra_fuse_read_spare(i + CPU_SPEEDO_REDUND_OFFS); val = (val << 1) | (reg & 0x1); } val = val * SPEEDO_MULT; @@ -94,17 +94,17 @@ void __init tegra20_init_speedo_data(struct tegra_sku_info *sku_info) sku_info->cpu_process_id = i; val = 0; - for (i = CORE_SPEEDO_MSBIT; i >= CORE_SPEEDO_LSBIT; i--) { - reg = tegra20_spare_fuse_early(i) | - tegra20_spare_fuse_early(i + CORE_SPEEDO_REDUND_OFFS); + for (i = SOC_SPEEDO_MSBIT; i >= SOC_SPEEDO_LSBIT; i--) { + reg = tegra_fuse_read_spare(i) | + tegra_fuse_read_spare(i + SOC_SPEEDO_REDUND_OFFS); val = (val << 1) | (reg & 0x1); } val = val * SPEEDO_MULT; pr_debug("Core speedo value %u\n", val); for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) { - if (val <= core_process_speedos[sku_info->soc_speedo_id][i]) + if (val <= soc_process_speedos[sku_info->soc_speedo_id][i]) break; } - sku_info->core_process_id = i; + sku_info->soc_process_id = i; } diff --git a/drivers/soc/tegra/fuse/speedo-tegra210.c b/drivers/soc/tegra/fuse/speedo-tegra210.c new file mode 100644 index 000000000..5373f4c16 --- /dev/null +++ b/drivers/soc/tegra/fuse/speedo-tegra210.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/bug.h> + +#include <soc/tegra/fuse.h> + +#include "fuse.h" + +#define CPU_PROCESS_CORNERS 2 +#define GPU_PROCESS_CORNERS 2 +#define SOC_PROCESS_CORNERS 3 + +#define FUSE_CPU_SPEEDO_0 0x014 +#define FUSE_CPU_SPEEDO_1 0x02c +#define FUSE_CPU_SPEEDO_2 0x030 +#define FUSE_SOC_SPEEDO_0 0x034 +#define FUSE_SOC_SPEEDO_1 0x038 +#define FUSE_SOC_SPEEDO_2 0x03c +#define FUSE_CPU_IDDQ 0x018 +#define FUSE_SOC_IDDQ 0x040 +#define FUSE_GPU_IDDQ 0x128 +#define FUSE_FT_REV 0x028 + +enum { + THRESHOLD_INDEX_0, + THRESHOLD_INDEX_1, + THRESHOLD_INDEX_COUNT, +}; + +static const u32 __initconst cpu_process_speedos[][CPU_PROCESS_CORNERS] = { + { 2119, UINT_MAX }, + { 2119, UINT_MAX }, +}; + +static const u32 __initconst gpu_process_speedos[][GPU_PROCESS_CORNERS] = { + { UINT_MAX, UINT_MAX }, + { UINT_MAX, UINT_MAX }, +}; + +static const u32 __initconst soc_process_speedos[][SOC_PROCESS_CORNERS] = { + { 1950, 2100, UINT_MAX }, + { 1950, 2100, UINT_MAX }, +}; + +static u8 __init get_speedo_revision(void) +{ + return tegra_fuse_read_spare(4) << 2 | + tegra_fuse_read_spare(3) << 1 | + tegra_fuse_read_spare(2) << 0; +} + +static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, + u8 speedo_rev, int *threshold) +{ + int sku = sku_info->sku_id; + + /* Assign to default */ + sku_info->cpu_speedo_id = 0; + sku_info->soc_speedo_id = 0; + sku_info->gpu_speedo_id = 0; + *threshold = THRESHOLD_INDEX_0; + + switch (sku) { + case 0x00: /* Engineering SKU */ + case 0x01: /* Engineering SKU */ + case 0x07: + case 0x17: + case 0x27: + if (speedo_rev >= 2) + sku_info->gpu_speedo_id = 1; + break; + + case 0x13: + if (speedo_rev >= 2) + sku_info->gpu_speedo_id = 1; + + sku_info->cpu_speedo_id = 1; + break; + + default: + pr_err("Tegra210: unknown SKU %#04x\n", sku); + /* Using the default for the error case */ + break; + } +} + +static int get_process_id(int value, const u32 *speedos, unsigned int num) +{ + unsigned int i; + + for (i = 0; i < num; i++) + if (value < speedos[num]) + return i; + + return -EINVAL; +} + +void __init tegra210_init_speedo_data(struct tegra_sku_info *sku_info) +{ + int cpu_speedo[3], soc_speedo[3], cpu_iddq, gpu_iddq, soc_iddq; + unsigned int index; + u8 speedo_revision; + + BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != + THRESHOLD_INDEX_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(gpu_process_speedos) != + THRESHOLD_INDEX_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(soc_process_speedos) != + THRESHOLD_INDEX_COUNT); + + /* Read speedo/IDDQ fuses */ + cpu_speedo[0] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_0); + cpu_speedo[1] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_1); + cpu_speedo[2] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_2); + + soc_speedo[0] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_0); + soc_speedo[1] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_1); + soc_speedo[2] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_2); + + cpu_iddq = tegra_fuse_read_early(FUSE_CPU_IDDQ) * 4; + soc_iddq = tegra_fuse_read_early(FUSE_SOC_IDDQ) * 4; + gpu_iddq = tegra_fuse_read_early(FUSE_GPU_IDDQ) * 5; + + /* + * Determine CPU, GPU and SoC speedo values depending on speedo fusing + * revision. Note that GPU speedo value is fused in CPU_SPEEDO_2. + */ + speedo_revision = get_speedo_revision(); + pr_info("Speedo Revision %u\n", speedo_revision); + + if (speedo_revision >= 3) { + sku_info->cpu_speedo_value = cpu_speedo[0]; + sku_info->gpu_speedo_value = cpu_speedo[2]; + sku_info->soc_speedo_value = soc_speedo[0]; + } else if (speedo_revision == 2) { + sku_info->cpu_speedo_value = (-1938 + (1095 * cpu_speedo[0] / 100)) / 10; + sku_info->gpu_speedo_value = (-1662 + (1082 * cpu_speedo[2] / 100)) / 10; + sku_info->soc_speedo_value = ( -705 + (1037 * soc_speedo[0] / 100)) / 10; + } else { + sku_info->cpu_speedo_value = 2100; + sku_info->gpu_speedo_value = cpu_speedo[2] - 75; + sku_info->soc_speedo_value = 1900; + } + + if ((sku_info->cpu_speedo_value <= 0) || + (sku_info->gpu_speedo_value <= 0) || + (sku_info->soc_speedo_value <= 0)) { + WARN(1, "speedo value not fused\n"); + return; + } + + rev_sku_to_speedo_ids(sku_info, speedo_revision, &index); + + sku_info->gpu_process_id = get_process_id(sku_info->gpu_speedo_value, + gpu_process_speedos[index], + GPU_PROCESS_CORNERS); + + sku_info->cpu_process_id = get_process_id(sku_info->cpu_speedo_value, + cpu_process_speedos[index], + CPU_PROCESS_CORNERS); + + sku_info->soc_process_id = get_process_id(sku_info->soc_speedo_value, + soc_process_speedos[index], + SOC_PROCESS_CORNERS); + + pr_debug("Tegra GPU Speedo ID=%d, Speedo Value=%d\n", + sku_info->gpu_speedo_id, sku_info->gpu_speedo_value); +} diff --git a/drivers/soc/tegra/fuse/speedo-tegra30.c b/drivers/soc/tegra/fuse/speedo-tegra30.c index b17f0dcdf..9b010b3ef 100644 --- a/drivers/soc/tegra/fuse/speedo-tegra30.c +++ b/drivers/soc/tegra/fuse/speedo-tegra30.c @@ -22,7 +22,7 @@ #include "fuse.h" -#define CORE_PROCESS_CORNERS 1 +#define SOC_PROCESS_CORNERS 1 #define CPU_PROCESS_CORNERS 6 #define FUSE_SPEEDO_CALIB_0 0x14 @@ -54,7 +54,7 @@ enum { THRESHOLD_INDEX_COUNT, }; -static const u32 __initconst core_process_speedos[][CORE_PROCESS_CORNERS] = { +static const u32 __initconst soc_process_speedos[][SOC_PROCESS_CORNERS] = { {180}, {170}, {195}, @@ -93,25 +93,25 @@ static void __init fuse_speedo_calib(u32 *speedo_g, u32 *speedo_lp) int bit_minus1; int bit_minus2; - reg = tegra30_fuse_readl(FUSE_SPEEDO_CALIB_0); + reg = tegra_fuse_read_early(FUSE_SPEEDO_CALIB_0); *speedo_lp = (reg & 0xFFFF) * 4; *speedo_g = ((reg >> 16) & 0xFFFF) * 4; - ate_ver = tegra30_fuse_readl(FUSE_TEST_PROG_VER); + ate_ver = tegra_fuse_read_early(FUSE_TEST_PROG_VER); pr_debug("Tegra ATE prog ver %d.%d\n", ate_ver/10, ate_ver%10); if (ate_ver >= 26) { - bit_minus1 = tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS1); - bit_minus1 |= tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS1_R); - bit_minus2 = tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS2); - bit_minus2 |= tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS2_R); + bit_minus1 = tegra_fuse_read_spare(LP_SPEEDO_BIT_MINUS1); + bit_minus1 |= tegra_fuse_read_spare(LP_SPEEDO_BIT_MINUS1_R); + bit_minus2 = tegra_fuse_read_spare(LP_SPEEDO_BIT_MINUS2); + bit_minus2 |= tegra_fuse_read_spare(LP_SPEEDO_BIT_MINUS2_R); *speedo_lp |= (bit_minus1 << 1) | bit_minus2; - bit_minus1 = tegra30_spare_fuse(G_SPEEDO_BIT_MINUS1); - bit_minus1 |= tegra30_spare_fuse(G_SPEEDO_BIT_MINUS1_R); - bit_minus2 = tegra30_spare_fuse(G_SPEEDO_BIT_MINUS2); - bit_minus2 |= tegra30_spare_fuse(G_SPEEDO_BIT_MINUS2_R); + bit_minus1 = tegra_fuse_read_spare(G_SPEEDO_BIT_MINUS1); + bit_minus1 |= tegra_fuse_read_spare(G_SPEEDO_BIT_MINUS1_R); + bit_minus2 = tegra_fuse_read_spare(G_SPEEDO_BIT_MINUS2); + bit_minus2 |= tegra_fuse_read_spare(G_SPEEDO_BIT_MINUS2_R); *speedo_g |= (bit_minus1 << 1) | bit_minus2; } else { *speedo_lp |= 0x3; @@ -121,7 +121,7 @@ static void __init fuse_speedo_calib(u32 *speedo_g, u32 *speedo_lp) static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info) { - int package_id = tegra30_fuse_readl(FUSE_PACKAGE_INFO) & 0x0F; + int package_id = tegra_fuse_read_early(FUSE_PACKAGE_INFO) & 0x0F; switch (sku_info->revision) { case TEGRA_REVISION_A01: @@ -246,19 +246,19 @@ static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info) void __init tegra30_init_speedo_data(struct tegra_sku_info *sku_info) { u32 cpu_speedo_val; - u32 core_speedo_val; + u32 soc_speedo_val; int i; BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != THRESHOLD_INDEX_COUNT); - BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != + BUILD_BUG_ON(ARRAY_SIZE(soc_process_speedos) != THRESHOLD_INDEX_COUNT); rev_sku_to_speedo_ids(sku_info); - fuse_speedo_calib(&cpu_speedo_val, &core_speedo_val); + fuse_speedo_calib(&cpu_speedo_val, &soc_speedo_val); pr_debug("Tegra CPU speedo value %u\n", cpu_speedo_val); - pr_debug("Tegra Core speedo value %u\n", core_speedo_val); + pr_debug("Tegra Core speedo value %u\n", soc_speedo_val); for (i = 0; i < CPU_PROCESS_CORNERS; i++) { if (cpu_speedo_val < cpu_process_speedos[threshold_index][i]) @@ -273,16 +273,16 @@ void __init tegra30_init_speedo_data(struct tegra_sku_info *sku_info) sku_info->cpu_speedo_id = 1; } - for (i = 0; i < CORE_PROCESS_CORNERS; i++) { - if (core_speedo_val < core_process_speedos[threshold_index][i]) + for (i = 0; i < SOC_PROCESS_CORNERS; i++) { + if (soc_speedo_val < soc_process_speedos[threshold_index][i]) break; } - sku_info->core_process_id = i - 1; + sku_info->soc_process_id = i - 1; - if (sku_info->core_process_id == -1) { - pr_warn("Tegra CORE speedo value %3d out of range", - core_speedo_val); - sku_info->core_process_id = 0; + if (sku_info->soc_process_id == -1) { + pr_warn("Tegra SoC speedo value %3d out of range", + soc_speedo_val); + sku_info->soc_process_id = 0; sku_info->soc_speedo_id = 1; } } diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index 73fad05d8..5b18f6ffa 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -21,11 +21,10 @@ #include <linux/io.h> #include <soc/tegra/fuse.h> +#include <soc/tegra/common.h> #include "fuse.h" -#define APBMISC_BASE 0x70000800 -#define APBMISC_SIZE 0x64 #define FUSE_SKU_INFO 0x10 #define PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT 4 @@ -95,8 +94,8 @@ void __init tegra_init_revision(void) rev = TEGRA_REVISION_A02; break; case 3: - if (chip_id == TEGRA20 && (tegra20_spare_fuse_early(18) || - tegra20_spare_fuse_early(19))) + if (chip_id == TEGRA20 && (tegra_fuse_read_spare(18) || + tegra_fuse_read_spare(19))) rev = TEGRA_REVISION_A03p; else rev = TEGRA_REVISION_A03; @@ -110,27 +109,74 @@ void __init tegra_init_revision(void) tegra_sku_info.revision = rev; - if (chip_id == TEGRA20) - tegra_sku_info.sku_id = tegra20_fuse_early(FUSE_SKU_INFO); - else - tegra_sku_info.sku_id = tegra30_fuse_readl(FUSE_SKU_INFO); + tegra_sku_info.sku_id = tegra_fuse_read_early(FUSE_SKU_INFO); } void __init tegra_init_apbmisc(void) { + struct resource apbmisc, straps; struct device_node *np; np = of_find_matching_node(NULL, apbmisc_match); - apbmisc_base = of_iomap(np, 0); - if (!apbmisc_base) { - pr_warn("ioremap tegra apbmisc failed. using %08x instead\n", - APBMISC_BASE); - apbmisc_base = ioremap(APBMISC_BASE, APBMISC_SIZE); + 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 + * an APBMISC node. + * + * This is for backwards-compatibility with old device trees + * that didn't contain an APBMISC node. + */ + if (IS_ENABLED(CONFIG_ARM) && soc_is_tegra()) { + /* APBMISC registers (chip revision, ...) */ + apbmisc.start = 0x70000800; + apbmisc.end = 0x70000863; + apbmisc.flags = IORESOURCE_MEM; + + /* strapping options */ + if (tegra_get_chip_id() == TEGRA124) { + straps.start = 0x7000e864; + straps.end = 0x7000e867; + } else { + straps.start = 0x70000008; + straps.end = 0x7000000b; + } + + straps.flags = IORESOURCE_MEM; + + pr_warn("Using APBMISC region %pR\n", &apbmisc); + pr_warn("Using strapping options registers %pR\n", + &straps); + } else { + /* + * At this point we're not running on Tegra, so play + * nice with multi-platform kernels. + */ + return; + } + } else { + /* + * Extract information from the device tree if we've found a + * matching node. + */ + if (of_address_to_resource(np, 0, &apbmisc) < 0) { + pr_err("failed to get APBMISC registers\n"); + return; + } + + if (of_address_to_resource(np, 1, &straps) < 0) { + pr_err("failed to get strapping options registers\n"); + return; + } } - strapping_base = of_iomap(np, 1); + apbmisc_base = ioremap_nocache(apbmisc.start, resource_size(&apbmisc)); + if (!apbmisc_base) + pr_err("failed to map APBMISC registers\n"); + + strapping_base = ioremap_nocache(straps.start, resource_size(&straps)); if (!strapping_base) - pr_err("ioremap tegra strapping_base failed\n"); + pr_err("failed to map strapping options registers\n"); long_ram_code = of_property_read_bool(np, "nvidia,long-ram-code"); } diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index fa7036c4d..bc34cf748 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -17,6 +17,8 @@ * */ +#define pr_fmt(fmt) "tegra-pmc: " fmt + #include <linux/kernel.h> #include <linux/clk.h> #include <linux/clk/tegra.h> @@ -457,7 +459,6 @@ static int tegra_io_rail_prepare(int id, unsigned long *request, unsigned long *status, unsigned int *bit) { unsigned long rate, value; - struct clk *clk; *bit = id % 32; @@ -476,12 +477,7 @@ static int tegra_io_rail_prepare(int id, unsigned long *request, *request = IO_DPD2_REQ; } - clk = clk_get_sys(NULL, "pclk"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - rate = clk_get_rate(clk); - clk_put(clk); + rate = clk_get_rate(pmc->clk); tegra_pmc_writel(DPD_SAMPLE_ENABLE, DPD_SAMPLE); @@ -535,8 +531,10 @@ int tegra_io_rail_power_on(int id) tegra_pmc_writel(value, request); err = tegra_io_rail_poll(status, mask, 0, 250); - if (err < 0) + if (err < 0) { + pr_info("tegra_io_rail_poll() failed: %d\n", err); return err; + } tegra_io_rail_unprepare(); @@ -551,8 +549,10 @@ int tegra_io_rail_power_off(int id) int err; err = tegra_io_rail_prepare(id, &request, &status, &bit); - if (err < 0) + if (err < 0) { + pr_info("tegra_io_rail_prepare() failed: %d\n", err); return err; + } mask = 1 << bit; @@ -801,7 +801,6 @@ void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc) out: of_node_put(np); - return; } static int tegra_pmc_probe(struct platform_device *pdev) @@ -1002,7 +1001,56 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { .has_gpu_clamps = true, }; +static const char * const tegra210_powergates[] = { + [TEGRA_POWERGATE_CPU] = "crail", + [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_PCIE] = "pcie", + [TEGRA_POWERGATE_L2] = "l2", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [TEGRA_POWERGATE_SATA] = "sata", + [TEGRA_POWERGATE_CPU1] = "cpu1", + [TEGRA_POWERGATE_CPU2] = "cpu2", + [TEGRA_POWERGATE_CPU3] = "cpu3", + [TEGRA_POWERGATE_CELP] = "celp", + [TEGRA_POWERGATE_CPU0] = "cpu0", + [TEGRA_POWERGATE_C0NC] = "c0nc", + [TEGRA_POWERGATE_C1NC] = "c1nc", + [TEGRA_POWERGATE_SOR] = "sor", + [TEGRA_POWERGATE_DIS] = "dis", + [TEGRA_POWERGATE_DISB] = "disb", + [TEGRA_POWERGATE_XUSBA] = "xusba", + [TEGRA_POWERGATE_XUSBB] = "xusbb", + [TEGRA_POWERGATE_XUSBC] = "xusbc", + [TEGRA_POWERGATE_VIC] = "vic", + [TEGRA_POWERGATE_IRAM] = "iram", + [TEGRA_POWERGATE_NVDEC] = "nvdec", + [TEGRA_POWERGATE_NVJPG] = "nvjpg", + [TEGRA_POWERGATE_AUD] = "aud", + [TEGRA_POWERGATE_DFD] = "dfd", + [TEGRA_POWERGATE_VE2] = "ve2", +}; + +static const u8 tegra210_cpu_powergates[] = { + TEGRA_POWERGATE_CPU0, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + +static const struct tegra_pmc_soc tegra210_pmc_soc = { + .num_powergates = ARRAY_SIZE(tegra210_powergates), + .powergates = tegra210_powergates, + .num_cpu_powergates = ARRAY_SIZE(tegra210_cpu_powergates), + .cpu_powergates = tegra210_cpu_powergates, + .has_tsense_reset = true, + .has_gpu_clamps = true, +}; + static const struct of_device_id tegra_pmc_match[] = { + { .compatible = "nvidia,tegra210-pmc", .data = &tegra210_pmc_soc }, + { .compatible = "nvidia,tegra132-pmc", .data = &tegra124_pmc_soc }, { .compatible = "nvidia,tegra124-pmc", .data = &tegra124_pmc_soc }, { .compatible = "nvidia,tegra114-pmc", .data = &tegra114_pmc_soc }, { .compatible = "nvidia,tegra30-pmc", .data = &tegra30_pmc_soc }, @@ -1035,25 +1083,44 @@ static int __init tegra_pmc_early_init(void) bool invert; u32 value; - if (!soc_is_tegra()) - return 0; - np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match); if (!np) { - pr_warn("PMC device node not found, disabling powergating\n"); - - regs.start = 0x7000e400; - regs.end = 0x7000e7ff; - regs.flags = IORESOURCE_MEM; - - pr_warn("Using memory region %pR\n", ®s); + /* + * Fall back to legacy initialization for 32-bit ARM only. All + * 64-bit ARM device tree files for Tegra are required to have + * a PMC node. + * + * This is for backwards-compatibility with old device trees + * that didn't contain a PMC node. Note that in this case the + * SoC data can't be matched and therefore powergating is + * disabled. + */ + if (IS_ENABLED(CONFIG_ARM) && soc_is_tegra()) { + pr_warn("DT node not found, powergating disabled\n"); + + regs.start = 0x7000e400; + regs.end = 0x7000e7ff; + regs.flags = IORESOURCE_MEM; + + pr_warn("Using memory region %pR\n", ®s); + } else { + /* + * At this point we're not running on Tegra, so play + * nice with multi-platform kernels. + */ + return 0; + } } else { - pmc->soc = match->data; - } + /* + * 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 PMC registers\n"); + return -ENXIO; + } - if (of_address_to_resource(np, 0, ®s) < 0) { - pr_err("failed to get PMC registers\n"); - return -ENXIO; + pmc->soc = match->data; } pmc->base = ioremap_nocache(regs.start, resource_size(®s)); @@ -1064,6 +1131,10 @@ static int __init tegra_pmc_early_init(void) mutex_init(&pmc->powergates_lock); + /* + * Invert the interrupt polarity if a PMC device tree node exists and + * contains the nvidia,invert-interrupt property. + */ invert = of_property_read_bool(np, "nvidia,invert-interrupt"); value = tegra_pmc_readl(PMC_CNTRL); |