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 2624fa5af8SThierry 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; 34f9fc3661SVince Hsu EXPORT_SYMBOL(tegra_sku_info); 35783c8f4cSPeter De Schrijver 36783c8f4cSPeter De Schrijver static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { 37783c8f4cSPeter De Schrijver [TEGRA_REVISION_UNKNOWN] = "unknown", 38783c8f4cSPeter De Schrijver [TEGRA_REVISION_A01] = "A01", 39783c8f4cSPeter De Schrijver [TEGRA_REVISION_A02] = "A02", 40783c8f4cSPeter De Schrijver [TEGRA_REVISION_A03] = "A03", 41783c8f4cSPeter De Schrijver [TEGRA_REVISION_A03p] = "A03 prime", 42783c8f4cSPeter De Schrijver [TEGRA_REVISION_A04] = "A04", 43783c8f4cSPeter De Schrijver }; 44783c8f4cSPeter De Schrijver 45783c8f4cSPeter De Schrijver static u8 fuse_readb(const unsigned int offset) 46783c8f4cSPeter De Schrijver { 47783c8f4cSPeter De Schrijver u32 val; 48783c8f4cSPeter De Schrijver 49783c8f4cSPeter De Schrijver val = fuse_readl(round_down(offset, 4)); 50783c8f4cSPeter De Schrijver val >>= (offset % 4) * 8; 51783c8f4cSPeter De Schrijver val &= 0xff; 52783c8f4cSPeter De Schrijver 53783c8f4cSPeter De Schrijver return val; 54783c8f4cSPeter De Schrijver } 55783c8f4cSPeter De Schrijver 56783c8f4cSPeter De Schrijver static ssize_t fuse_read(struct file *fd, struct kobject *kobj, 57783c8f4cSPeter De Schrijver struct bin_attribute *attr, char *buf, 58783c8f4cSPeter De Schrijver loff_t pos, size_t size) 59783c8f4cSPeter De Schrijver { 60783c8f4cSPeter De Schrijver int i; 61783c8f4cSPeter De Schrijver 62783c8f4cSPeter De Schrijver if (pos < 0 || pos >= fuse_size) 63783c8f4cSPeter De Schrijver return 0; 64783c8f4cSPeter De Schrijver 65783c8f4cSPeter De Schrijver if (size > fuse_size - pos) 66783c8f4cSPeter De Schrijver size = fuse_size - pos; 67783c8f4cSPeter De Schrijver 68783c8f4cSPeter De Schrijver for (i = 0; i < size; i++) 69783c8f4cSPeter De Schrijver buf[i] = fuse_readb(pos + i); 70783c8f4cSPeter De Schrijver 71783c8f4cSPeter De Schrijver return i; 72783c8f4cSPeter De Schrijver } 73783c8f4cSPeter De Schrijver 74783c8f4cSPeter De Schrijver static struct bin_attribute fuse_bin_attr = { 75783c8f4cSPeter De Schrijver .attr = { .name = "fuse", .mode = S_IRUGO, }, 76783c8f4cSPeter De Schrijver .read = fuse_read, 77783c8f4cSPeter De Schrijver }; 78783c8f4cSPeter De Schrijver 79783c8f4cSPeter De Schrijver static const struct of_device_id car_match[] __initconst = { 80783c8f4cSPeter De Schrijver { .compatible = "nvidia,tegra20-car", }, 81783c8f4cSPeter De Schrijver { .compatible = "nvidia,tegra30-car", }, 82783c8f4cSPeter De Schrijver { .compatible = "nvidia,tegra114-car", }, 83783c8f4cSPeter De Schrijver { .compatible = "nvidia,tegra124-car", }, 84*9b07eb05SThierry Reding { .compatible = "nvidia,tegra132-car", }, 85783c8f4cSPeter De Schrijver {}, 86783c8f4cSPeter De Schrijver }; 87783c8f4cSPeter De Schrijver 88783c8f4cSPeter De Schrijver static void tegra_enable_fuse_clk(void __iomem *base) 89783c8f4cSPeter De Schrijver { 90783c8f4cSPeter De Schrijver u32 reg; 91783c8f4cSPeter De Schrijver 92783c8f4cSPeter De Schrijver reg = readl_relaxed(base + 0x48); 93783c8f4cSPeter De Schrijver reg |= 1 << 28; 94783c8f4cSPeter De Schrijver writel(reg, base + 0x48); 95783c8f4cSPeter De Schrijver 96783c8f4cSPeter De Schrijver /* 97783c8f4cSPeter De Schrijver * Enable FUSE clock. This needs to be hardcoded because the clock 98783c8f4cSPeter De Schrijver * subsystem is not active during early boot. 99783c8f4cSPeter De Schrijver */ 100783c8f4cSPeter De Schrijver reg = readl(base + 0x14); 101783c8f4cSPeter De Schrijver reg |= 1 << 7; 102783c8f4cSPeter De Schrijver writel(reg, base + 0x14); 103783c8f4cSPeter De Schrijver } 104783c8f4cSPeter De Schrijver 105783c8f4cSPeter De Schrijver int tegra_fuse_readl(unsigned long offset, u32 *value) 106783c8f4cSPeter De Schrijver { 107783c8f4cSPeter De Schrijver if (!fuse_readl) 108783c8f4cSPeter De Schrijver return -EPROBE_DEFER; 109783c8f4cSPeter De Schrijver 110783c8f4cSPeter De Schrijver *value = fuse_readl(offset); 111783c8f4cSPeter De Schrijver 112783c8f4cSPeter De Schrijver return 0; 113783c8f4cSPeter De Schrijver } 114783c8f4cSPeter De Schrijver EXPORT_SYMBOL(tegra_fuse_readl); 115783c8f4cSPeter De Schrijver 116783c8f4cSPeter De Schrijver int tegra_fuse_create_sysfs(struct device *dev, int size, 117783c8f4cSPeter De Schrijver u32 (*readl)(const unsigned int offset)) 118783c8f4cSPeter De Schrijver { 119783c8f4cSPeter De Schrijver if (fuse_size) 120783c8f4cSPeter De Schrijver return -ENODEV; 121783c8f4cSPeter De Schrijver 122783c8f4cSPeter De Schrijver fuse_bin_attr.size = size; 123783c8f4cSPeter De Schrijver fuse_bin_attr.read = fuse_read; 124783c8f4cSPeter De Schrijver 125783c8f4cSPeter De Schrijver fuse_size = size; 126783c8f4cSPeter De Schrijver fuse_readl = readl; 127783c8f4cSPeter De Schrijver 128783c8f4cSPeter De Schrijver return device_create_bin_file(dev, &fuse_bin_attr); 129783c8f4cSPeter De Schrijver } 130783c8f4cSPeter De Schrijver 13124fa5af8SThierry Reding static int __init tegra_init_fuse(void) 132783c8f4cSPeter De Schrijver { 133783c8f4cSPeter De Schrijver struct device_node *np; 134783c8f4cSPeter De Schrijver void __iomem *car_base; 135783c8f4cSPeter De Schrijver 13624fa5af8SThierry Reding if (!soc_is_tegra()) 13724fa5af8SThierry Reding return 0; 13824fa5af8SThierry Reding 139783c8f4cSPeter De Schrijver tegra_init_apbmisc(); 140783c8f4cSPeter De Schrijver 141783c8f4cSPeter De Schrijver np = of_find_matching_node(NULL, car_match); 142783c8f4cSPeter De Schrijver car_base = of_iomap(np, 0); 143783c8f4cSPeter De Schrijver if (car_base) { 144783c8f4cSPeter De Schrijver tegra_enable_fuse_clk(car_base); 145783c8f4cSPeter De Schrijver iounmap(car_base); 146783c8f4cSPeter De Schrijver } else { 147783c8f4cSPeter De Schrijver pr_err("Could not enable fuse clk. ioremap tegra car failed.\n"); 14824fa5af8SThierry Reding return -ENXIO; 149783c8f4cSPeter De Schrijver } 150783c8f4cSPeter De Schrijver 151783c8f4cSPeter De Schrijver if (tegra_get_chip_id() == TEGRA20) 152783c8f4cSPeter De Schrijver tegra20_init_fuse_early(); 153783c8f4cSPeter De Schrijver else 154783c8f4cSPeter De Schrijver tegra30_init_fuse_early(); 155783c8f4cSPeter De Schrijver 156783c8f4cSPeter De Schrijver pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n", 157783c8f4cSPeter De Schrijver tegra_revision_name[tegra_sku_info.revision], 158783c8f4cSPeter De Schrijver tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id, 159783c8f4cSPeter De Schrijver tegra_sku_info.core_process_id); 160783c8f4cSPeter De Schrijver pr_debug("Tegra CPU Speedo ID %d, Soc Speedo ID %d\n", 161783c8f4cSPeter De Schrijver tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); 16224fa5af8SThierry Reding 16324fa5af8SThierry Reding return 0; 164783c8f4cSPeter De Schrijver } 16524fa5af8SThierry Reding early_initcall(tegra_init_fuse); 166