/* * 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 <http://www.gnu.org/licenses/>. * */ #include <linux/device.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/of_device.h> #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/random.h> #include <soc/tegra/fuse.h> #include "fuse.h" #define FUSE_BEGIN 0x100 /* 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) 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) { 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; } 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) { 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; fuse_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(fuse_clk)) { dev_err(&pdev->dev, "missing clock"); return PTR_ERR(fuse_clk); } platform_set_drvdata(pdev, NULL); if (tegra_fuse_create_sysfs(&pdev->dev, fuse_info->size, tegra30_fuse_readl)) 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", .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); /* 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) { u32 randomness[12]; randomness[0] = tegra_sku_info.sku_id; 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[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); add_device_randomness(randomness, sizeof(randomness)); } static void __init legacy_fuse_init(void) { 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_base = ioremap(TEGRA_FUSE_BASE, TEGRA_FUSE_SIZE); } bool __init tegra30_spare_fuse(int spare_bit) { u32 offset = fuse_info->spare_bit + spare_bit * 4; return tegra30_fuse_readl(offset) & 1; } 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; } tegra_init_revision(); speedo_tbl[fuse_info->speedo_idx](&tegra_sku_info); tegra30_fuse_add_randomness(); }