1783c8f4cSPeter De Schrijver /* 2783c8f4cSPeter De Schrijver * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved. 3783c8f4cSPeter De Schrijver * 4783c8f4cSPeter De Schrijver * This program is free software; you can redistribute it and/or modify it 5783c8f4cSPeter De Schrijver * under the terms and conditions of the GNU General Public License, 6783c8f4cSPeter De Schrijver * version 2, as published by the Free Software Foundation. 7783c8f4cSPeter De Schrijver * 8783c8f4cSPeter De Schrijver * This program is distributed in the hope it will be useful, but WITHOUT 9783c8f4cSPeter De Schrijver * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10783c8f4cSPeter De Schrijver * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11783c8f4cSPeter De Schrijver * more details. 12783c8f4cSPeter De Schrijver * 13783c8f4cSPeter De Schrijver * You should have received a copy of the GNU General Public License 14783c8f4cSPeter De Schrijver * along with this program. If not, see <http://www.gnu.org/licenses/>. 15783c8f4cSPeter De Schrijver * 16783c8f4cSPeter De Schrijver */ 17783c8f4cSPeter De Schrijver 18783c8f4cSPeter De Schrijver #include <linux/device.h> 19783c8f4cSPeter De Schrijver #include <linux/kobject.h> 20783c8f4cSPeter De Schrijver #include <linux/kernel.h> 21783c8f4cSPeter De Schrijver #include <linux/platform_device.h> 22783c8f4cSPeter De Schrijver #include <linux/of.h> 23783c8f4cSPeter De Schrijver #include <linux/of_address.h> 24783c8f4cSPeter De Schrijver #include <linux/io.h> 25783c8f4cSPeter De Schrijver 26*24fa5af8SThierry Reding #include <soc/tegra/common.h> 27783c8f4cSPeter De Schrijver #include <soc/tegra/fuse.h> 28783c8f4cSPeter De Schrijver 29783c8f4cSPeter De Schrijver #include "fuse.h" 30783c8f4cSPeter De Schrijver 31783c8f4cSPeter De Schrijver static u32 (*fuse_readl)(const unsigned int offset); 32783c8f4cSPeter De Schrijver static int fuse_size; 33783c8f4cSPeter De Schrijver struct tegra_sku_info tegra_sku_info; 34783c8f4cSPeter De Schrijver 35783c8f4cSPeter De Schrijver static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { 36783c8f4cSPeter De Schrijver [TEGRA_REVISION_UNKNOWN] = "unknown", 37783c8f4cSPeter De Schrijver [TEGRA_REVISION_A01] = "A01", 38783c8f4cSPeter De Schrijver [TEGRA_REVISION_A02] = "A02", 39783c8f4cSPeter De Schrijver [TEGRA_REVISION_A03] = "A03", 40783c8f4cSPeter De Schrijver [TEGRA_REVISION_A03p] = "A03 prime", 41783c8f4cSPeter De Schrijver [TEGRA_REVISION_A04] = "A04", 42783c8f4cSPeter De Schrijver }; 43783c8f4cSPeter De Schrijver 44783c8f4cSPeter De Schrijver static u8 fuse_readb(const unsigned int offset) 45783c8f4cSPeter De Schrijver { 46783c8f4cSPeter De Schrijver u32 val; 47783c8f4cSPeter De Schrijver 48783c8f4cSPeter De Schrijver val = fuse_readl(round_down(offset, 4)); 49783c8f4cSPeter De Schrijver val >>= (offset % 4) * 8; 50783c8f4cSPeter De Schrijver val &= 0xff; 51783c8f4cSPeter De Schrijver 52783c8f4cSPeter De Schrijver return val; 53783c8f4cSPeter De Schrijver } 54783c8f4cSPeter De Schrijver 55783c8f4cSPeter De Schrijver static ssize_t fuse_read(struct file *fd, struct kobject *kobj, 56783c8f4cSPeter De Schrijver struct bin_attribute *attr, char *buf, 57783c8f4cSPeter De Schrijver loff_t pos, size_t size) 58783c8f4cSPeter De Schrijver { 59783c8f4cSPeter De Schrijver int i; 60783c8f4cSPeter De Schrijver 61783c8f4cSPeter De Schrijver if (pos < 0 || pos >= fuse_size) 62783c8f4cSPeter De Schrijver return 0; 63783c8f4cSPeter De Schrijver 64783c8f4cSPeter De Schrijver if (size > fuse_size - pos) 65783c8f4cSPeter De Schrijver size = fuse_size - pos; 66783c8f4cSPeter De Schrijver 67783c8f4cSPeter De Schrijver for (i = 0; i < size; i++) 68783c8f4cSPeter De Schrijver buf[i] = fuse_readb(pos + i); 69783c8f4cSPeter De Schrijver 70783c8f4cSPeter De Schrijver return i; 71783c8f4cSPeter De Schrijver } 72783c8f4cSPeter De Schrijver 73783c8f4cSPeter De Schrijver static struct bin_attribute fuse_bin_attr = { 74783c8f4cSPeter De Schrijver .attr = { .name = "fuse", .mode = S_IRUGO, }, 75783c8f4cSPeter De Schrijver .read = fuse_read, 76783c8f4cSPeter De Schrijver }; 77783c8f4cSPeter De Schrijver 78783c8f4cSPeter De Schrijver static const struct of_device_id car_match[] __initconst = { 79783c8f4cSPeter De Schrijver { .compatible = "nvidia,tegra20-car", }, 80783c8f4cSPeter De Schrijver { .compatible = "nvidia,tegra30-car", }, 81783c8f4cSPeter De Schrijver { .compatible = "nvidia,tegra114-car", }, 82783c8f4cSPeter De Schrijver { .compatible = "nvidia,tegra124-car", }, 83783c8f4cSPeter De Schrijver {}, 84783c8f4cSPeter De Schrijver }; 85783c8f4cSPeter De Schrijver 86783c8f4cSPeter De Schrijver static void tegra_enable_fuse_clk(void __iomem *base) 87783c8f4cSPeter De Schrijver { 88783c8f4cSPeter De Schrijver u32 reg; 89783c8f4cSPeter De Schrijver 90783c8f4cSPeter De Schrijver reg = readl_relaxed(base + 0x48); 91783c8f4cSPeter De Schrijver reg |= 1 << 28; 92783c8f4cSPeter De Schrijver writel(reg, base + 0x48); 93783c8f4cSPeter De Schrijver 94783c8f4cSPeter De Schrijver /* 95783c8f4cSPeter De Schrijver * Enable FUSE clock. This needs to be hardcoded because the clock 96783c8f4cSPeter De Schrijver * subsystem is not active during early boot. 97783c8f4cSPeter De Schrijver */ 98783c8f4cSPeter De Schrijver reg = readl(base + 0x14); 99783c8f4cSPeter De Schrijver reg |= 1 << 7; 100783c8f4cSPeter De Schrijver writel(reg, base + 0x14); 101783c8f4cSPeter De Schrijver } 102783c8f4cSPeter De Schrijver 103783c8f4cSPeter De Schrijver int tegra_fuse_readl(unsigned long offset, u32 *value) 104783c8f4cSPeter De Schrijver { 105783c8f4cSPeter De Schrijver if (!fuse_readl) 106783c8f4cSPeter De Schrijver return -EPROBE_DEFER; 107783c8f4cSPeter De Schrijver 108783c8f4cSPeter De Schrijver *value = fuse_readl(offset); 109783c8f4cSPeter De Schrijver 110783c8f4cSPeter De Schrijver return 0; 111783c8f4cSPeter De Schrijver } 112783c8f4cSPeter De Schrijver EXPORT_SYMBOL(tegra_fuse_readl); 113783c8f4cSPeter De Schrijver 114783c8f4cSPeter De Schrijver int tegra_fuse_create_sysfs(struct device *dev, int size, 115783c8f4cSPeter De Schrijver u32 (*readl)(const unsigned int offset)) 116783c8f4cSPeter De Schrijver { 117783c8f4cSPeter De Schrijver if (fuse_size) 118783c8f4cSPeter De Schrijver return -ENODEV; 119783c8f4cSPeter De Schrijver 120783c8f4cSPeter De Schrijver fuse_bin_attr.size = size; 121783c8f4cSPeter De Schrijver fuse_bin_attr.read = fuse_read; 122783c8f4cSPeter De Schrijver 123783c8f4cSPeter De Schrijver fuse_size = size; 124783c8f4cSPeter De Schrijver fuse_readl = readl; 125783c8f4cSPeter De Schrijver 126783c8f4cSPeter De Schrijver return device_create_bin_file(dev, &fuse_bin_attr); 127783c8f4cSPeter De Schrijver } 128783c8f4cSPeter De Schrijver 129*24fa5af8SThierry Reding static int __init tegra_init_fuse(void) 130783c8f4cSPeter De Schrijver { 131783c8f4cSPeter De Schrijver struct device_node *np; 132783c8f4cSPeter De Schrijver void __iomem *car_base; 133783c8f4cSPeter De Schrijver 134*24fa5af8SThierry Reding if (!soc_is_tegra()) 135*24fa5af8SThierry Reding return 0; 136*24fa5af8SThierry Reding 137783c8f4cSPeter De Schrijver tegra_init_apbmisc(); 138783c8f4cSPeter De Schrijver 139783c8f4cSPeter De Schrijver np = of_find_matching_node(NULL, car_match); 140783c8f4cSPeter De Schrijver car_base = of_iomap(np, 0); 141783c8f4cSPeter De Schrijver if (car_base) { 142783c8f4cSPeter De Schrijver tegra_enable_fuse_clk(car_base); 143783c8f4cSPeter De Schrijver iounmap(car_base); 144783c8f4cSPeter De Schrijver } else { 145783c8f4cSPeter De Schrijver pr_err("Could not enable fuse clk. ioremap tegra car failed.\n"); 146*24fa5af8SThierry Reding return -ENXIO; 147783c8f4cSPeter De Schrijver } 148783c8f4cSPeter De Schrijver 149783c8f4cSPeter De Schrijver if (tegra_get_chip_id() == TEGRA20) 150783c8f4cSPeter De Schrijver tegra20_init_fuse_early(); 151783c8f4cSPeter De Schrijver else 152783c8f4cSPeter De Schrijver tegra30_init_fuse_early(); 153783c8f4cSPeter De Schrijver 154783c8f4cSPeter De Schrijver pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n", 155783c8f4cSPeter De Schrijver tegra_revision_name[tegra_sku_info.revision], 156783c8f4cSPeter De Schrijver tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id, 157783c8f4cSPeter De Schrijver tegra_sku_info.core_process_id); 158783c8f4cSPeter De Schrijver pr_debug("Tegra CPU Speedo ID %d, Soc Speedo ID %d\n", 159783c8f4cSPeter De Schrijver tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); 160*24fa5af8SThierry Reding 161*24fa5af8SThierry Reding return 0; 162783c8f4cSPeter De Schrijver } 163*24fa5af8SThierry Reding early_initcall(tegra_init_fuse); 164