From patchwork Wed May 28 12:54:35 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter De Schrijver X-Patchwork-Id: 4254961 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 21A1E9F32B for ; Wed, 28 May 2014 13:00:00 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 999EC20279 for ; Wed, 28 May 2014 12:59:57 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E73282026C for ; Wed, 28 May 2014 12:59:54 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WpdPb-0000gF-1P; Wed, 28 May 2014 12:56:55 +0000 Received: from hqemgate15.nvidia.com ([216.228.121.64]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WpdP7-0000Q5-FT for linux-arm-kernel@lists.infradead.org; Wed, 28 May 2014 12:56:29 +0000 Received: from hqnvupgp08.nvidia.com (Not Verified[216.228.121.13]) by hqemgate15.nvidia.com id ; Wed, 28 May 2014 05:56:01 -0700 Received: from hqemhub02.nvidia.com ([172.20.12.94]) by hqnvupgp08.nvidia.com (PGP Universal service); Wed, 28 May 2014 05:51:04 -0700 X-PGP-Universal: processed; by hqnvupgp08.nvidia.com on Wed, 28 May 2014 05:51:04 -0700 Received: from hqnvemgw02.nvidia.com (172.16.227.111) by hqemhub02.nvidia.com (172.20.150.31) with Microsoft SMTP Server id 8.3.342.0; Wed, 28 May 2014 05:56:05 -0700 Received: from thelma.nvidia.com (Not Verified[172.16.212.77]) by hqnvemgw02.nvidia.com with MailMarshal (v7,1,2,5326) id ; Wed, 28 May 2014 05:56:04 -0700 Received: from tbergstrom-lnx.nvidia.com (tbergstrom-lnx.nvidia.com [10.21.24.170]) by thelma.nvidia.com (8.13.8+Sun/8.8.8) with ESMTP id s4SCtj7o003278; Wed, 28 May 2014 05:56:00 -0700 (PDT) From: Peter De Schrijver To: Peter De Schrijver Subject: [PATCH v5 3/5] misc: fuse: Add efuse driver for Tegra Date: Wed, 28 May 2014 15:54:35 +0300 Message-ID: <1401281677-32110-4-git-send-email-pdeschrijver@nvidia.com> X-Mailer: git-send-email 1.7.7.rc0.72.g4b5ea.dirty In-Reply-To: <1401281677-32110-1-git-send-email-pdeschrijver@nvidia.com> References: <1401281677-32110-1-git-send-email-pdeschrijver@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140528_055625_602245_4D47B483 X-CRM114-Status: GOOD ( 23.76 ) X-Spam-Score: -0.7 (/) Cc: Russell King , Wolfram Sang , Linus Walleij , Stephen Warren , linux-kernel@vger.kernel.org, Thierry Reding , linux-tegra@vger.kernel.org, Andrew Morton , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.5 required=5.0 tests=BAYES_00,RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Implement fuse driver for Tegra20, Tegra30, Tegra114 and Tegra124. Signed-off-by: Peter De Schrijver --- Documentation/ABI/testing/sysfs-driver-tegra-fuse | 201 ++++++++++++++ drivers/misc/fuse/Makefile | 1 + drivers/misc/fuse/tegra/Makefile | 7 + drivers/misc/fuse/tegra/fuse-tegra.c | 250 +++++++++++++++++ drivers/misc/fuse/tegra/fuse-tegra20.c | 134 ++++++++++ drivers/misc/fuse/tegra/fuse-tegra30.c | 177 +++++++++++++ drivers/misc/fuse/tegra/fuse.h | 82 ++++++ drivers/misc/fuse/tegra/speedo-tegra114.c | 110 ++++++++ drivers/misc/fuse/tegra/speedo-tegra124.c | 180 +++++++++++++ drivers/misc/fuse/tegra/speedo-tegra20.c | 110 ++++++++ drivers/misc/fuse/tegra/speedo-tegra30.c | 294 +++++++++++++++++++++ include/linux/tegra-soc.h | 4 + 12 files changed, 1550 insertions(+), 0 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-tegra-fuse create mode 100644 drivers/misc/fuse/Makefile create mode 100644 drivers/misc/fuse/tegra/Makefile create mode 100644 drivers/misc/fuse/tegra/fuse-tegra.c create mode 100644 drivers/misc/fuse/tegra/fuse-tegra20.c create mode 100644 drivers/misc/fuse/tegra/fuse-tegra30.c create mode 100644 drivers/misc/fuse/tegra/fuse.h create mode 100644 drivers/misc/fuse/tegra/speedo-tegra114.c create mode 100644 drivers/misc/fuse/tegra/speedo-tegra124.c create mode 100644 drivers/misc/fuse/tegra/speedo-tegra20.c create mode 100644 drivers/misc/fuse/tegra/speedo-tegra30.c diff --git a/Documentation/ABI/testing/sysfs-driver-tegra-fuse b/Documentation/ABI/testing/sysfs-driver-tegra-fuse new file mode 100644 index 0000000..9d558d1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-tegra-fuse @@ -0,0 +1,201 @@ +What: /sys/devices/*//fuse +Date: February 2014 +Contact: Peter De Schrijver +Description: read-only access to the efuses on Tegra20, Tegra30, Tegra114 + and Tegra124 SoC's from NVIDIA. The efuses contain write once + data programmed at the factory. The data is layed out in 32bit + words in LSB first formnat. The number of valid bits depends + on the word and the SoC. The mapping is as follows: + + For Tegra20: + Word 0 - 1 : bit 0 + Word 2 : unused + Word 3 : bits 0 - 31 + Word 4 : bits 0 - 7 + Word 5 : bits 0 - 1 + Word 6 : bits 0 - 9 + Word 7 - 9 : bits 0 - 7 + Word 10 - 17 : unused + Word 18 : bit 0 + Word 19 : bits 0 - 3 + Word 20 - 23 : bits 0 - 5 + Word 24 - 39 : unused + Word 40 : bit 0 + Word 41 - 45 : bits 0 - 31 + Word 46 : bit 0 + Word 47 : bits 0 - 15 + Word 48 : bits 0 - 7 + Word 49 : bits 0 - 3 + Word 50 - 57 : bits 0 - 31 + Word 58 : bit 0 + Word 59 : bits 0 - 1 + Word 60 : bits 0 - 6 + Word 61 : unused + Word 62 - 63 : bits 0 - 1 + Word 64 - 125 : bit 0 + + For Tegra30: + Word 0 - 1 : bit 0 + Word 2 - 3 : unused + Word 4 : bits 0 - 7 + Word 5 : bits 0 - 31 + Word 6 : bits 0 - 15 + Word 7 - 9 : bits 0 - 7 + Word 10 : bits 0 - 6 + Word 11 - 16 : unused + Word 17 : bits 0 - 1 + Word 18 : bit 0 + Word 19 - 23 : bits 0 - 5 + Word 24 : bits 0 - 3 + Word 25 - 34 : unused + Word 35 : bits 0 - 31 + Word 36 : bits 0 - 6 + Word 37 : bits 0 - 13 + Word 38 : bits 0 - 31 + Word 39 : bits 0 - 6 + Word 40 : bit 0 + Word 41 - 45 : bits 0 - 31 + Word 46 : bit 0 + Word 47 : bits 0 - 15 + Word 48 : bits 0 - 7 + Word 49 - 57 : bits 0 - 31 + Word 58 : bit 0 + Word 59 : bits 0 - 1 + Word 60 : bits 0 - 31 + Word 61 : bits 0 - 7 + Word 62 : bits 0 - 1 + Word 63 - 64 : bits 0 - 3 + Word 65 : bits 0 - 5 + Word 66 : bits 0 - 31 + Word 67 : bits 0 - 27 + Word 68 : bits 0 - 5 + Word 69 - 70 : bits 0 - 8 + Word 71 : unused + Word 72 : bits 0 - 5 + Word 73 : bits 0 - 1 + Word 74 - 75 : unused + Word 76 - 80 : bits 0 - 31 + Word 81 - 168 : bit 0 + + For Tegra114: + Word 0 - 1 : bit 0 + Word 2 : bits 0 - 3 + Word 3 : unused + Word 4 : bits 0 - 7 + Word 5 : bits 0 - 10 + Word 6 : bits 0 - 12 + Word 7 - 9 : unused + Word 10 - 16 : bits 0 - 11 + Word 17 : unused + Word 18 : bit 0 + Word 19 : bits 0 - 3 + Word 20 - 23 : bits 0 - 7 + Word 24 : bits 0 - 3 + Word 25 - 32 : bits 0 - 31 + Word 33 - 34 : bits 0 - 25 + Word 35 : bits 0 - 31 + Word 36 : bits 0 - 10 + Word 37 : bits 0 - 13 + Word 38 : bits 0 - 25 + Word 39 : bits 0 - 6 + Word 40 : bit 0 + Word 41 - 45 : bits 0 - 31 + Word 46 : bit 0 + Word 47 : bits 0 - 15 + Word 48 : bits 0 - 8 + Word 49 : bit 0 + Word 50 - 57 : bits 0 - 31 + Word 58 : bit 0 + Word 59 : bits 0 - 1 + Word 60 : bits 0 - 31 + Word 61 : bits 0 - 7 + Word 62 : bits 0 - 1 + Word 63 - 64 : bits 0 - 3 + Word 65 : bits 0 - 5 + Word 66 : bits 0 - 31 + Word 67 : bits 0 - 27 + Word 68 : bits 0 - 5 + Word 69 - 70 : bits 0 - 8 + Word 71 : unused + Word 72 : bits 0 - 5 + Word 75 : bits 0 - 25 + Word 76 - 80 : bits 0 - 31 + Word 81 - 84 : unused + Word 85 - 88 : bits 0 - 25 + Word 89 : unused + Word 90 - 103 : bit 0 + + For Tegra124: + Word 0 - 1 : bit 0 + Word 2 : bits 0 - 3 + Word 3 : bit 0 + Word 4 : bits 0 - 7 + Word 5 : bits 0 - 11 + Word 6 : bits 0 - 12 + Word 7 - 9 : unused + Word 10 : bits 0 - 10 + Word 11 - 16 : bits 0 - 11 + Word 17 : unused + Word 18 : bit 0 + Word 19 : bits 0 - 3 + Word 20 - 23 : bits 0 - 7 + Word 24 : bits 0 - 3 + Word 25 - 32 : bits 0 - 31 + Word 33 - 34 : bits 0 - 25 + Word 35 : unused + Word 36 : bits 0 - 10 + Word 37 : bits 0 - 13 + Word 38 : bits 0 - 25 + Word 39 : bits 0 - 6 + Word 40 : bit 0 + Word 41 - 45 : bits 0 - 31 + Word 46 : bit 0 + Word 47 : bits 0 - 15 + Word 48 : bits 0 - 8 + Word 49 : bit 0 + Word 50 - 57 : bits 0 - 31 + Word 58 : bit 0 + Word 59 : unused + Word 60 : bits 0 - 31 + Word 61 : bits 0 - 7 + Word 62 : bits 0 - 1 + Word 63 - 64 : bits 0 - 3 + Word 65 : bits 0 - 5 + Word 66 : bits 0 - 31 + Word 67 : bits 0 - 27 + Word 68 : bits 0 - 5 + Word 69 - 70 : bits 0 - 8 + Word 71 : bit 0 + Word 72 : bits 0 - 5 + Word 73 : bits 0 - 1 + Word 74 : bits 0 - 11 + Word 75 : bits 0 - 25 + Word 76 - 80 : bits 0 - 31 + Word 81 : bit 0 + Word 82 : unused + Word 83 - 84 : bits 0 - 1 + Word 85 - 88 : bits 0 - 25 + Word 89 - 90 : bit 0 + Word 91 - 94 : unused + Word 95 : bit 0 + Word 96 : bits 0 - 25 + Word 97 : bits 0 - 1 + Word 98 : bit 0 + Word 99 : bits 0 - 9 + Word 100 : unused + Word 101 : bits 0 - 31 + Word 102 : bit 0 + Word 103 : unused + Word 104 - 105: bits 0 - 4 + Word 106 : bit 0 + Word 107 : bits 0 - 9 + Word 108 : bits 0 - 3 + Word 109 : bits 0 - 15 + Word 110 - 112: bit 0 + Word 113 : bits 0 - 7 + Word 114 : bits 0 - 2 + Word 115 - 191: unused + Word 192 - 255: bit 0 + +Users: any user space application which wants to read the efuses on + Tegra SoC's diff --git a/drivers/misc/fuse/Makefile b/drivers/misc/fuse/Makefile new file mode 100644 index 0000000..0679c4f --- /dev/null +++ b/drivers/misc/fuse/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/misc/fuse/tegra/Makefile b/drivers/misc/fuse/tegra/Makefile new file mode 100644 index 0000000..3a5b37f --- /dev/null +++ b/drivers/misc/fuse/tegra/Makefile @@ -0,0 +1,7 @@ +obj-y += fuse-tegra.o +obj-y += fuse-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += fuse-tegra20.o +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 diff --git a/drivers/misc/fuse/tegra/fuse-tegra.c b/drivers/misc/fuse/tegra/fuse-tegra.c new file mode 100644 index 0000000..ba32871 --- /dev/null +++ b/drivers/misc/fuse/tegra/fuse-tegra.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2013-2014, 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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuse.h" + +#define APBMISC_BASE 0x70000800 +#define APBMISC_SIZE 0x64 + +int tegra_chip_id; +enum tegra_revision tegra_revision; + +/* + * The BCT to use at boot is specified by board straps that can be read + * through a APB misc register and decoded. 2 bits, i.e. 4 possible BCTs. + */ + +#define TEGRA_RAM_ID_SHIFT 4 +#define TEGRA_RAM_ID_MASK 3 + +static u32 (*fuse_readl)(const unsigned int offset); +static int fuse_size; +static void __iomem *fuse_base; +static void __iomem *apbmisc_base; +static void __iomem *strapping_base; + +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 u8 fuse_readb(const unsigned int offset) +{ + u32 val; + + val = fuse_readl(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) +{ + int i; + + if (pos < 0 || pos >= fuse_size) + return 0; + + if (size > fuse_size - pos) + size = fuse_size - pos; + + for (i = 0; i < size; i++) + buf[i] = fuse_readb(pos + i); + + return i; +} + +static struct bin_attribute fuse_bin_attr = { + .attr = { .name = "fuse", .mode = S_IRUGO, }, + .read = fuse_read, +}; + +static const struct of_device_id tegra_fuse_match[] __initconst = { + { .compatible = "nvidia,tegra20-efuse", }, + { .compatible = "nvidia,tegra30-efuse", }, + { .compatible = "nvidia,tegra114-efuse", }, + { .compatible = "nvidia,tegra124-efuse", }, + {}, +}; + +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", }, + {}, +}; + +static const struct of_device_id apbmisc_match[] __initconst = { + { .compatible = "nvidia,tegra20-apbmisc", }, + {}, +}; + +static void tegra_get_revision(u32 id) +{ + u32 minor_rev = (id >> 16) & 0xf; + + switch (minor_rev) { + case 1: + tegra_revision = TEGRA_REVISION_A01; + break; + case 2: + tegra_revision = TEGRA_REVISION_A02; + break; + case 3: + if (tegra_chip_id == TEGRA20 && + (tegra20_spare_fuse_early(18, fuse_base) || + tegra20_spare_fuse_early(19, fuse_base))) + tegra_revision = TEGRA_REVISION_A03p; + else + tegra_revision = TEGRA_REVISION_A03; + break; + case 4: + tegra_revision = TEGRA_REVISION_A04; + break; + default: + tegra_revision = TEGRA_REVISION_UNKNOWN; + } +} + +static void tegra_enable_fuse_clk(void __iomem *base) +{ + u32 reg; + + reg = readl_relaxed(base + 0x48); + reg |= 1 << 28; + writel(reg, base + 0x48); + + /* + * 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); +} + +u32 tegra_read_straps(void) +{ + if (strapping_base) + return readl(strapping_base); + else + return 0; +} + +u32 tegra_read_chipid(void) +{ + return readl_relaxed(apbmisc_base + 4); +} + +int tegra_fuse_readl(u32 offset, u32 *val) +{ + if (!fuse_readl) + return -ENXIO; + + *val = fuse_readl(offset); + + return 0; +} + +int tegra_fuse_create_sysfs(struct device *dev, int size, + u32 (*readl)(const unsigned int offset), + struct tegra_sku_info *sku_info) +{ + int err; + + if (fuse_size) + return -ENODEV; + + fuse_bin_attr.size = size; + fuse_bin_attr.read = fuse_read; + + fuse_size = size; + fuse_readl = readl; + + err = device_create_bin_file(dev, &fuse_bin_attr); + if (err) + return err; + + dev_info(dev, + "Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n", + tegra_revision_name[sku_info->revision], + sku_info->sku_id, sku_info->cpu_process_id, + sku_info->core_process_id); + + return 0; +} + +void __init tegra_init_fuse(void) +{ + struct device_node *np; + u32 id; + void __iomem *car_base; + + 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); + } + + id = tegra_read_chipid(); + tegra_chip_id = (id >> 8) & 0xff; + + strapping_base = of_iomap(np, 0); + if (!strapping_base) { + pr_err("ioremap tegra strapping_base failed\n"); + return; + } + + np = of_find_matching_node(NULL, tegra_fuse_match); + fuse_base = of_iomap(np, 0); + if (!fuse_base) { + pr_err("ioremap tegra fuse failed\n"); + return; + } + + 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); + } else { + pr_err("Could not enable fuse clk. ioremap tegra car failed.\n"); + iounmap(fuse_base); + return; + } + + tegra_get_revision(id); +} diff --git a/drivers/misc/fuse/tegra/fuse-tegra20.c b/drivers/misc/fuse/tegra/fuse-tegra20.c new file mode 100644 index 0000000..852da92 --- /dev/null +++ b/drivers/misc/fuse/tegra/fuse-tegra20.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2013-2014, 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 . + * + * Based on drivers/misc/eeprom/sunxi_sid.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuse.h" + +#define FUSE_BEGIN 0x100 +#define FUSE_SIZE 0x1f8 +#define FUSE_SKU_INFO 0x10 +#define FUSE_UID_LOW 0x08 +#define FUSE_UID_HIGH 0x0c + +static phys_addr_t fuse_phys; +static struct clk *fuse_clk; +static struct tegra_sku_info sku_info; + +static u32 tegra20_fuse_readl(const unsigned int offset) +{ + int ret; + u32 val; + + clk_prepare_enable(fuse_clk); + + ret = tegra_apb_readl_using_dma(fuse_phys + FUSE_BEGIN + offset, &val); + + clk_disable_unprepare(fuse_clk); + + return (ret < 0) ? 0 : val; +} + +static void tegra20_fuse_add_randomness(void) +{ + u32 randomness[7]; + + randomness[0] = tegra20_fuse_readl(FUSE_SKU_INFO); + randomness[1] = tegra_read_straps(); + randomness[2] = tegra_read_chipid(); + randomness[3] = sku_info.cpu_process_id << 16; + randomness[3] |= sku_info.core_process_id; + randomness[4] = sku_info.cpu_speedo_id << 16 | sku_info.soc_speedo_id; + randomness[5] = tegra20_fuse_readl(FUSE_UID_LOW); + randomness[6] = tegra20_fuse_readl(FUSE_UID_HIGH); + + add_device_randomness(randomness, sizeof(randomness)); +} + +bool tegra20_spare_fuse(int spare_bit) +{ + u32 offset = spare_bit * 4 + 0x100; + + return tegra20_fuse_readl(offset) & 1; +} + +bool tegra20_spare_fuse_early(int spare_bit, void *fuse_base) +{ + u32 offset = spare_bit * 4 + 0x100; + + return readl_relaxed(fuse_base + FUSE_BEGIN + offset); +} + +static const struct of_device_id tegra20_fuse_of_match[] = { + { .compatible = "nvidia,tegra20-efuse" }, + {}, +}; + +static int tegra20_fuse_probe(struct platform_device *pdev) +{ + struct resource *res; + + 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; + + sku_info.revision = tegra_revision; + tegra20_init_speedo_data(&sku_info, &pdev->dev); + dev_dbg(&pdev->dev, "Soc Speedo ID %d", sku_info.soc_speedo_id); + + tegra20_fuse_add_randomness(); + + if (tegra_fuse_create_sysfs(&pdev->dev, FUSE_SIZE, tegra20_fuse_readl, + &sku_info)) + return -ENODEV; + + dev_dbg(&pdev->dev, "loaded\n"); + + return 0; +} + +static struct platform_driver tegra20_fuse_driver = { + .probe = tegra20_fuse_probe, + .driver = { + .name = "tegra20_fuse", + .owner = THIS_MODULE, + .of_match_table = tegra20_fuse_of_match, + } +}; + +static int __init tegra20_fuse_init(void) +{ + return platform_driver_register(&tegra20_fuse_driver); +} +postcore_initcall(tegra20_fuse_init); diff --git a/drivers/misc/fuse/tegra/fuse-tegra30.c b/drivers/misc/fuse/tegra/fuse-tegra30.c new file mode 100644 index 0000000..4aa6490 --- /dev/null +++ b/drivers/misc/fuse/tegra/fuse-tegra30.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2013-2014, 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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuse.h" + +#define FUSE_BEGIN 0x100 + +#define FUSE_SKU_INFO 0x10 + +/* Tegra30 and later */ +#define FUSE_VENDOR_CODE 0x100 +#define FUSE_FAB_CODE 0x104 +#define FUSE_LOT_CODE_0 0x108 +#define FUSE_LOT_CODE_1 0x10c +#define FUSE_WAFER_ID 0x110 +#define FUSE_X_COORDINATE 0x114 +#define FUSE_Y_COORDINATE 0x118 + +#define FUSE_HAS_REVISION_INFO BIT(0) + +struct tegra_fuse_info { + int size; + int spare_bit; + void (*init_speedo_data)(struct tegra_sku_info *sku_info, + struct device *dev); +}; + +static void __iomem *fuse_base; +static struct clk *fuse_clk; +static struct tegra_fuse_info *fuse_info; +static struct tegra_sku_info sku_info; + +u32 tegra30_fuse_readl(const unsigned int offset) +{ + u32 val; + + clk_prepare_enable(fuse_clk); + + val = readl_relaxed(fuse_base + FUSE_BEGIN + offset); + + clk_disable_unprepare(fuse_clk); + + return val; +} + +bool tegra30_spare_fuse(int spare_bit) +{ + u32 offset = fuse_info->spare_bit + spare_bit * 4; + + return tegra30_fuse_readl(offset) & 1; +} + +static void tegra30_fuse_add_randomness(void) +{ + u32 randomness[12]; + + randomness[0] = tegra30_fuse_readl(FUSE_SKU_INFO); + randomness[1] = tegra_read_straps(); + randomness[2] = tegra_read_chipid(); + randomness[3] = sku_info.cpu_process_id << 16; + randomness[3] |= sku_info.core_process_id; + randomness[4] = sku_info.cpu_speedo_id << 16; + randomness[4] |= 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); + + add_device_randomness(randomness, sizeof(randomness)); +} + +static struct tegra_fuse_info tegra30_info = { + .size = 0x2a4, + .spare_bit = 0x144, + .init_speedo_data = tegra30_init_speedo_data, +}; + +static struct tegra_fuse_info tegra114_info = { + .size = 0x2a0, + .init_speedo_data = tegra114_init_speedo_data, +}; + +static struct tegra_fuse_info tegra124_info = { + .size = 0x300, + .init_speedo_data = tegra124_init_speedo_data, +}; + +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) +{ + const struct of_device_id *of_dev_id; + struct resource *res; + + of_dev_id = of_match_device(tegra30_fuse_of_match, &pdev->dev); + if (!of_dev_id) + return -ENODEV; + fuse_info = (struct tegra_fuse_info *)of_dev_id->data; + + 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); + fuse_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fuse_base)) { + dev_err(&pdev->dev, "unable to map base address"); + return PTR_ERR(fuse_base); + } + + sku_info.revision = tegra_revision; + fuse_info->init_speedo_data(&sku_info, &pdev->dev); + dev_dbg(&pdev->dev, "CPU Speedo ID %d, Soc Speedo ID %d", + sku_info.cpu_speedo_id, sku_info.soc_speedo_id); + + tegra30_fuse_add_randomness(); + + platform_set_drvdata(pdev, NULL); + + if (tegra_fuse_create_sysfs(&pdev->dev, fuse_info->size, + tegra30_fuse_readl, &sku_info)) + return -ENODEV; + + dev_dbg(&pdev->dev, "loaded\n"); + + return 0; +} + +static struct platform_driver tegra30_fuse_driver = { + .probe = tegra30_fuse_probe, + .driver = { + .name = "tegra_fuse", + .owner = THIS_MODULE, + .of_match_table = tegra30_fuse_of_match, + } +}; + +static int __init tegra30_fuse_init(void) +{ + return platform_driver_register(&tegra30_fuse_driver); +} +postcore_initcall(tegra30_fuse_init); + diff --git a/drivers/misc/fuse/tegra/fuse.h b/drivers/misc/fuse/tegra/fuse.h new file mode 100644 index 0000000..0baf82b --- /dev/null +++ b/drivers/misc/fuse/tegra/fuse.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 Google, Inc. + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * Author: + * Colin Cross + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + */ + +#ifndef __DRIVERS_MISC_TEGRA_FUSE_H +#define __DRIVERS_MISC_TEGRA_FUSE_H + +struct tegra_sku_info { + int sku_id; + int cpu_process_id; + int cpu_speedo_id; + int cpu_speedo_value; + int cpu_iddq_value; + int core_process_id; + int soc_speedo_id; + int gpu_speedo_id; + int gpu_process_id; + int gpu_speedo_value; + enum tegra_revision revision; +}; + +int tegra_fuse_create_sysfs(struct device *dev, int size, + u32 (*readl)(const unsigned int offset), + struct tegra_sku_info *sku_info); + +bool tegra30_spare_fuse(int bit); +u32 tegra30_fuse_readl(const unsigned int offset); + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +void tegra20_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev); +bool tegra20_spare_fuse(int bit); +bool tegra20_spare_fuse_early(int spare_bit, void *fuse_base); +#else +static inline void tegra20_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev) {} +static inline bool tegra20_spare_fuse(int bit) { return false; } +static inline bool tegra20_spare_fuse_early(int spare_bit, void *fuse_base) +{ + return false; +} +#endif + +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +void tegra30_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev); +#else +static inline void tegra30_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev) {} +#endif + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +void tegra114_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev); +#else +static inline void tegra114_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev) {} +#endif + +#ifdef CONFIG_ARCH_TEGRA_124_SOC +void tegra124_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev); +#else +static inline void tegra124_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev) {} +#endif + +#endif diff --git a/drivers/misc/fuse/tegra/speedo-tegra114.c b/drivers/misc/fuse/tegra/speedo-tegra114.c new file mode 100644 index 0000000..7be8ba5 --- /dev/null +++ b/drivers/misc/fuse/tegra/speedo-tegra114.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2013-2014, 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 . + */ + +#include +#include +#include +#include + +#include "fuse.h" + +#define CORE_PROCESS_CORNERS_NUM 2 +#define CPU_PROCESS_CORNERS_NUM 2 + +enum { + THRESHOLD_INDEX_0, + THRESHOLD_INDEX_1, + THRESHOLD_INDEX_COUNT, +}; + +static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = { + {1123, UINT_MAX}, + {0, UINT_MAX}, +}; + +static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = { + {1695, UINT_MAX}, + {0, UINT_MAX}, +}; + +static void rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, + int *threshold, struct device *dev) +{ + u32 tmp; + u32 sku = sku_info->sku_id; + enum tegra_revision rev = sku_info->revision; + + switch (sku) { + case 0x00: + case 0x10: + case 0x05: + case 0x06: + sku_info->cpu_speedo_id = 1; + sku_info->soc_speedo_id = 0; + *threshold = THRESHOLD_INDEX_0; + break; + + case 0x03: + case 0x04: + sku_info->cpu_speedo_id = 2; + sku_info->soc_speedo_id = 1; + *threshold = THRESHOLD_INDEX_1; + break; + + default: + dev_err(dev, "Unknown SKU %d\n", sku); + sku_info->cpu_speedo_id = 0; + sku_info->soc_speedo_id = 0; + *threshold = THRESHOLD_INDEX_0; + break; + } + + if (rev == TEGRA_REVISION_A01) { + tmp = tegra30_fuse_readl(0x270) << 1; + tmp |= tegra30_fuse_readl(0x26c); + if (!tmp) + sku_info->cpu_speedo_id = 0; + } +} + +void tegra114_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev) +{ + u32 cpu_speedo_val; + u32 core_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) != + THRESHOLD_INDEX_COUNT); + + rev_sku_to_speedo_ids(sku_info, &threshold, dev); + + cpu_speedo_val = tegra30_fuse_readl(0x12c) + 1024; + core_speedo_val = tegra30_fuse_readl(0x134); + + for (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++) + if (cpu_speedo_val < cpu_process_speedos[threshold][i]) + break; + sku_info->cpu_process_id = i; + + for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++) + if (core_speedo_val < core_process_speedos[threshold][i]) + break; + sku_info->core_process_id = i; +} diff --git a/drivers/misc/fuse/tegra/speedo-tegra124.c b/drivers/misc/fuse/tegra/speedo-tegra124.c new file mode 100644 index 0000000..97bc6b6 --- /dev/null +++ b/drivers/misc/fuse/tegra/speedo-tegra124.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2013-2014, 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 . + */ + +#include +#include +#include +#include + +#include "fuse.h" + +#define CPU_PROCESS_CORNERS_NUM 2 +#define GPU_PROCESS_CORNERS_NUM 2 +#define CORE_PROCESS_CORNERS_NUM 2 + +#define FUSE_CPU_SPEEDO_0 0x14 +#define FUSE_CPU_SPEEDO_1 0x2c +#define FUSE_CPU_SPEEDO_2 0x30 +#define FUSE_SOC_SPEEDO_0 0x34 +#define FUSE_SOC_SPEEDO_1 0x38 +#define FUSE_SOC_SPEEDO_2 0x3c +#define FUSE_CPU_IDDQ 0x18 +#define FUSE_SOC_IDDQ 0x40 +#define FUSE_GPU_IDDQ 0x128 +#define FUSE_FT_REV 0x28 + +enum { + THRESHOLD_INDEX_0, + THRESHOLD_INDEX_1, + THRESHOLD_INDEX_COUNT, +}; + +static int cpu_speedo_0_value; +static int cpu_speedo_1_value; +static int soc_speedo_0_value; +static int soc_speedo_1_value; +static int soc_speedo_2_value; +static int cpu_iddq_value; +static int gpu_iddq_value; +static int soc_iddq_value; + +static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = { + {2190, UINT_MAX}, + {0, UINT_MAX}, +}; + +static const u32 gpu_process_speedos[][GPU_PROCESS_CORNERS_NUM] = { + {1965, UINT_MAX}, + {0, UINT_MAX}, +}; + +static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = { + {2101, UINT_MAX}, + {0, UINT_MAX}, +}; + +static void rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, + int *threshold, struct device *dev) +{ + 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: /* Eng sku */ + case 0x0F: + case 0x23: + /* Using the default */ + break; + case 0x83: + sku_info->cpu_speedo_id = 2; + break; + + case 0x1F: + case 0x87: + case 0x27: + sku_info->cpu_speedo_id = 2; + sku_info->soc_speedo_id = 0; + sku_info->gpu_speedo_id = 1; + *threshold = THRESHOLD_INDEX_0; + break; + case 0x81: + case 0x21: + case 0x07: + sku_info->cpu_speedo_id = 1; + sku_info->soc_speedo_id = 1; + sku_info->gpu_speedo_id = 1; + *threshold = THRESHOLD_INDEX_1; + break; + case 0x49: + case 0x4A: + case 0x48: + sku_info->cpu_speedo_id = 4; + sku_info->soc_speedo_id = 2; + sku_info->gpu_speedo_id = 3; + *threshold = THRESHOLD_INDEX_1; + break; + default: + dev_err(dev, "Unknown SKU %d\n", sku); + /* Using the default for the error case */ + break; + } +} + +void tegra124_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev) +{ + int i; + int threshold; + + 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(core_process_speedos) != + THRESHOLD_INDEX_COUNT); + + cpu_speedo_0_value = tegra30_fuse_readl(FUSE_CPU_SPEEDO_0); + cpu_speedo_1_value = tegra30_fuse_readl(FUSE_CPU_SPEEDO_1); + + /* GPU Speedo is stored in CPU_SPEEDO_2 */ + sku_info->gpu_speedo_value = tegra30_fuse_readl(FUSE_CPU_SPEEDO_2); + + soc_speedo_0_value = tegra30_fuse_readl(FUSE_SOC_SPEEDO_0); + soc_speedo_1_value = tegra30_fuse_readl(FUSE_SOC_SPEEDO_1); + soc_speedo_2_value = tegra30_fuse_readl(FUSE_SOC_SPEEDO_2); + + 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); + + sku_info->cpu_speedo_value = cpu_speedo_0_value; + + if (sku_info->cpu_speedo_value == 0) { + dev_warn(dev, "Warning: Speedo value not fused.\n"); + WARN_ON(1); + return; + } + + rev_sku_to_speedo_ids(sku_info, &threshold, dev); + + sku_info->cpu_iddq_value = tegra30_fuse_readl(FUSE_CPU_IDDQ); + + for (i = 0; i < GPU_PROCESS_CORNERS_NUM; i++) + if (sku_info->gpu_speedo_value < + gpu_process_speedos[threshold][i]) + break; + sku_info->gpu_process_id = i; + + for (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++) + if (sku_info->cpu_speedo_value < + cpu_process_speedos[threshold][i]) + break; + sku_info->cpu_process_id = i; + + for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++) + if (soc_speedo_0_value < + core_process_speedos[threshold][i]) + break; + sku_info->core_process_id = i; + + dev_dbg(dev, "GPU Speedo ID=%d, Speedo Value=%d\n", + sku_info->gpu_speedo_id, sku_info->gpu_speedo_value); +} diff --git a/drivers/misc/fuse/tegra/speedo-tegra20.c b/drivers/misc/fuse/tegra/speedo-tegra20.c new file mode 100644 index 0000000..af8e86a --- /dev/null +++ b/drivers/misc/fuse/tegra/speedo-tegra20.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012-2014, 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 . + */ + +#include +#include +#include +#include + +#include "fuse.h" + +#define CPU_SPEEDO_LSBIT 20 +#define CPU_SPEEDO_MSBIT 29 +#define CPU_SPEEDO_REDUND_LSBIT 30 +#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 SPEEDO_MULT 4 + +#define PROCESS_CORNERS_NUM 4 + +#define SPEEDO_ID_SELECT_0(rev) ((rev) <= 2) +#define SPEEDO_ID_SELECT_1(sku) \ + (((sku) != 20) && ((sku) != 23) && ((sku) != 24) && \ + ((sku) != 27) && ((sku) != 28)) + +enum { + SPEEDO_ID_0, + SPEEDO_ID_1, + SPEEDO_ID_2, + SPEEDO_ID_COUNT, +}; + +static const u32 cpu_process_speedos[][PROCESS_CORNERS_NUM] = { + {315, 366, 420, UINT_MAX}, + {303, 368, 419, UINT_MAX}, + {316, 331, 383, UINT_MAX}, +}; + +static const u32 core_process_speedos[][PROCESS_CORNERS_NUM] = { + {165, 195, 224, UINT_MAX}, + {165, 195, 224, UINT_MAX}, + {165, 195, 224, UINT_MAX}, +}; + +void tegra20_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev) +{ + u32 reg; + u32 val; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != SPEEDO_ID_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != SPEEDO_ID_COUNT); + + if (SPEEDO_ID_SELECT_0(sku_info->revision)) + sku_info->soc_speedo_id = SPEEDO_ID_0; + else if (SPEEDO_ID_SELECT_1(sku_info->sku_id)) + sku_info->soc_speedo_id = SPEEDO_ID_1; + else + sku_info->soc_speedo_id = SPEEDO_ID_2; + + val = 0; + for (i = CPU_SPEEDO_MSBIT; i >= CPU_SPEEDO_LSBIT; i--) { + reg = tegra20_spare_fuse(i) | + tegra20_spare_fuse(i + CPU_SPEEDO_REDUND_OFFS); + val = (val << 1) | (reg & 0x1); + } + val = val * SPEEDO_MULT; + dev_dbg(dev, "CPU speedo value %u\n", val); + + for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) { + if (val <= cpu_process_speedos[sku_info->soc_speedo_id][i]) + break; + } + sku_info->cpu_process_id = i; + + val = 0; + for (i = CORE_SPEEDO_MSBIT; i >= CORE_SPEEDO_LSBIT; i--) { + reg = tegra20_spare_fuse(i) | + tegra20_spare_fuse(i + CORE_SPEEDO_REDUND_OFFS); + val = (val << 1) | (reg & 0x1); + } + val = val * SPEEDO_MULT; + dev_dbg(dev, "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]) + break; + } + sku_info->core_process_id = i; +} diff --git a/drivers/misc/fuse/tegra/speedo-tegra30.c b/drivers/misc/fuse/tegra/speedo-tegra30.c new file mode 100644 index 0000000..11cf0cd --- /dev/null +++ b/drivers/misc/fuse/tegra/speedo-tegra30.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2012-2014, 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 . + */ + +#include +#include +#include +#include + +#include "fuse.h" + +#define CORE_PROCESS_CORNERS_NUM 1 +#define CPU_PROCESS_CORNERS_NUM 6 + +#define FUSE_SPEEDO_CALIB_0 0x14 +#define FUSE_PACKAGE_INFO 0XFC +#define FUSE_TEST_PROG_VER 0X28 + +#define G_SPEEDO_BIT_MINUS1 58 +#define G_SPEEDO_BIT_MINUS1_R 59 +#define G_SPEEDO_BIT_MINUS2 60 +#define G_SPEEDO_BIT_MINUS2_R 61 +#define LP_SPEEDO_BIT_MINUS1 62 +#define LP_SPEEDO_BIT_MINUS1_R 63 +#define LP_SPEEDO_BIT_MINUS2 64 +#define LP_SPEEDO_BIT_MINUS2_R 65 + +enum { + THRESHOLD_INDEX_0, + THRESHOLD_INDEX_1, + THRESHOLD_INDEX_2, + THRESHOLD_INDEX_3, + THRESHOLD_INDEX_4, + THRESHOLD_INDEX_5, + THRESHOLD_INDEX_6, + THRESHOLD_INDEX_7, + THRESHOLD_INDEX_8, + THRESHOLD_INDEX_9, + THRESHOLD_INDEX_10, + THRESHOLD_INDEX_11, + THRESHOLD_INDEX_COUNT, +}; + +static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = { + {180}, + {170}, + {195}, + {180}, + {168}, + {192}, + {180}, + {170}, + {195}, + {180}, + {180}, + {180}, +}; + +static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = { + {306, 338, 360, 376, UINT_MAX}, + {295, 336, 358, 375, UINT_MAX}, + {325, 325, 358, 375, UINT_MAX}, + {325, 325, 358, 375, UINT_MAX}, + {292, 324, 348, 364, UINT_MAX}, + {324, 324, 348, 364, UINT_MAX}, + {324, 324, 348, 364, UINT_MAX}, + {295, 336, 358, 375, UINT_MAX}, + {358, 358, 358, 358, 397, UINT_MAX}, + {364, 364, 364, 364, 397, UINT_MAX}, + {295, 336, 358, 375, 391, UINT_MAX}, + {295, 336, 358, 375, 391, UINT_MAX}, +}; + +static int threshold_index; +static int package_id; + +static void fuse_speedo_calib(u32 *speedo_g, u32 *speedo_lp, + struct device *dev) +{ + u32 reg; + int ate_ver; + int bit_minus1; + int bit_minus2; + + reg = tegra30_fuse_readl(FUSE_SPEEDO_CALIB_0); + + *speedo_lp = (reg & 0xFFFF) * 4; + *speedo_g = ((reg >> 16) & 0xFFFF) * 4; + + ate_ver = tegra30_fuse_readl(FUSE_TEST_PROG_VER); + dev_dbg(dev, "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); + *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); + *speedo_g |= (bit_minus1 << 1) | bit_minus2; + } else { + *speedo_lp |= 0x3; + *speedo_g |= 0x3; + } +} + +static void rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, + struct device *dev) +{ + switch (sku_info->revision) { + case TEGRA_REVISION_A01: + sku_info->cpu_speedo_id = 0; + sku_info->soc_speedo_id = 0; + threshold_index = THRESHOLD_INDEX_0; + break; + case TEGRA_REVISION_A02: + case TEGRA_REVISION_A03: + switch (sku_info->sku_id) { + case 0x87: + case 0x82: + sku_info->cpu_speedo_id = 1; + sku_info->soc_speedo_id = 1; + threshold_index = THRESHOLD_INDEX_1; + break; + case 0x81: + switch (package_id) { + case 1: + sku_info->cpu_speedo_id = 2; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_2; + break; + case 2: + sku_info->cpu_speedo_id = 4; + sku_info->soc_speedo_id = 1; + threshold_index = THRESHOLD_INDEX_7; + break; + default: + dev_err(dev, "Unknown pkg %d\n", package_id); + BUG(); + break; + } + break; + case 0x80: + switch (package_id) { + case 1: + sku_info->cpu_speedo_id = 5; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_8; + break; + case 2: + sku_info->cpu_speedo_id = 6; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_9; + break; + default: + dev_err(dev, "Unknown pkg %d\n", package_id); + BUG(); + break; + } + break; + case 0x83: + switch (package_id) { + case 1: + sku_info->cpu_speedo_id = 7; + sku_info->soc_speedo_id = 1; + threshold_index = THRESHOLD_INDEX_10; + break; + case 2: + sku_info->cpu_speedo_id = 3; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_3; + break; + default: + dev_err(dev, "Unknown pkg %d\n", package_id); + BUG(); + break; + } + break; + case 0x8F: + sku_info->cpu_speedo_id = 8; + sku_info->soc_speedo_id = 1; + threshold_index = THRESHOLD_INDEX_11; + break; + case 0x08: + sku_info->cpu_speedo_id = 1; + sku_info->soc_speedo_id = 1; + threshold_index = THRESHOLD_INDEX_4; + break; + case 0x02: + sku_info->cpu_speedo_id = 2; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_5; + break; + case 0x04: + sku_info->cpu_speedo_id = 3; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_6; + break; + case 0: + switch (package_id) { + case 1: + sku_info->cpu_speedo_id = 2; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_2; + break; + case 2: + sku_info->cpu_speedo_id = 3; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_3; + break; + default: + dev_err(dev, "Unknown pkg %d\n", package_id); + BUG(); + break; + } + break; + default: + dev_warn(dev, "Unknown SKU %d\n", sku_info->sku_id); + sku_info->cpu_speedo_id = 0; + sku_info->soc_speedo_id = 0; + threshold_index = THRESHOLD_INDEX_0; + break; + } + break; + default: + dev_warn(dev, "Unknown chip rev %d\n", sku_info->revision); + sku_info->cpu_speedo_id = 0; + sku_info->soc_speedo_id = 0; + threshold_index = THRESHOLD_INDEX_0; + break; + } +} + +void tegra30_init_speedo_data(struct tegra_sku_info *sku_info, + struct device *dev) +{ + u32 cpu_speedo_val; + u32 core_speedo_val; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != + THRESHOLD_INDEX_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != + THRESHOLD_INDEX_COUNT); + + package_id = tegra30_fuse_readl(FUSE_PACKAGE_INFO) & 0x0F; + + rev_sku_to_speedo_ids(sku_info, dev); + fuse_speedo_calib(&cpu_speedo_val, &core_speedo_val, dev); + dev_dbg(dev, "CPU speedo value %u\n", cpu_speedo_val); + dev_dbg(dev, "Core speedo value %u\n", core_speedo_val); + + for (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++) { + if (cpu_speedo_val < cpu_process_speedos[threshold_index][i]) + break; + } + sku_info->cpu_process_id = i - 1; + + if (sku_info->cpu_process_id == -1) { + dev_warn(dev, "CPU speedo value %3d out of range", + cpu_speedo_val); + sku_info->cpu_process_id = 0; + sku_info->cpu_speedo_id = 1; + } + + for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++) { + if (core_speedo_val < core_process_speedos[threshold_index][i]) + break; + } + sku_info->core_process_id = i - 1; + + if (sku_info->core_process_id == -1) { + dev_warn(dev, "CORE speedo value %3d out of range", + core_speedo_val); + sku_info->core_process_id = 0; + sku_info->soc_speedo_id = 1; + } +} diff --git a/include/linux/tegra-soc.h b/include/linux/tegra-soc.h index 60e6ac8..a22c55d 100644 --- a/include/linux/tegra-soc.h +++ b/include/linux/tegra-soc.h @@ -22,6 +22,9 @@ #define TEGRA114 0x35 #define TEGRA124 0x40 +#define TEGRA_FUSE_SKU_CALIB_0 0xf0 +#define TEGRA30_FUSE_SATA_CALIB 0x124 + #ifndef __ASSEMBLY__ enum tegra_revision { @@ -37,6 +40,7 @@ enum tegra_revision { u32 tegra_read_straps(void); u32 tegra_read_chipid(void); void tegra_init_fuse(void); +int tegra_fuse_readl(u32 offset, u32 *val); extern int tegra_chip_id; extern enum tegra_revision tegra_revision;