1/* 2 * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * 16 */ 17 18#include <linux/clk.h> 19#include <linux/device.h> 20#include <linux/kobject.h> 21#include <linux/module.h> 22#include <linux/platform_device.h> 23#include <linux/of.h> 24#include <linux/of_address.h> 25#include <linux/io.h> 26 27#include <soc/tegra/common.h> 28#include <soc/tegra/fuse.h> 29 30#include "fuse.h" 31 32struct tegra_sku_info tegra_sku_info; 33EXPORT_SYMBOL(tegra_sku_info); 34 35static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { 36 [TEGRA_REVISION_UNKNOWN] = "unknown", 37 [TEGRA_REVISION_A01] = "A01", 38 [TEGRA_REVISION_A02] = "A02", 39 [TEGRA_REVISION_A03] = "A03", 40 [TEGRA_REVISION_A03p] = "A03 prime", 41 [TEGRA_REVISION_A04] = "A04", 42}; 43 44static u8 fuse_readb(struct tegra_fuse *fuse, unsigned int offset) 45{ 46 u32 val; 47 48 val = fuse->read(fuse, round_down(offset, 4)); 49 val >>= (offset % 4) * 8; 50 val &= 0xff; 51 52 return val; 53} 54 55static ssize_t fuse_read(struct file *fd, struct kobject *kobj, 56 struct bin_attribute *attr, char *buf, 57 loff_t pos, size_t size) 58{ 59 struct device *dev = kobj_to_dev(kobj); 60 struct tegra_fuse *fuse = dev_get_drvdata(dev); 61 int i; 62 63 if (pos < 0 || pos >= attr->size) 64 return 0; 65 66 if (size > attr->size - pos) 67 size = attr->size - pos; 68 69 for (i = 0; i < size; i++) 70 buf[i] = fuse_readb(fuse, pos + i); 71 72 return i; 73} 74 75static struct bin_attribute fuse_bin_attr = { 76 .attr = { .name = "fuse", .mode = S_IRUGO, }, 77 .read = fuse_read, 78}; 79 80static int tegra_fuse_create_sysfs(struct device *dev, unsigned int size, 81 const struct tegra_fuse_info *info) 82{ 83 fuse_bin_attr.size = size; 84 85 return device_create_bin_file(dev, &fuse_bin_attr); 86} 87 88static const struct of_device_id car_match[] __initconst = { 89 { .compatible = "nvidia,tegra20-car", }, 90 { .compatible = "nvidia,tegra30-car", }, 91 { .compatible = "nvidia,tegra114-car", }, 92 { .compatible = "nvidia,tegra124-car", }, 93 { .compatible = "nvidia,tegra132-car", }, 94 { .compatible = "nvidia,tegra210-car", }, 95 {}, 96}; 97 98static struct tegra_fuse *fuse = &(struct tegra_fuse) { 99 .base = NULL, 100 .soc = NULL, 101}; 102 103static const struct of_device_id tegra_fuse_match[] = { 104#ifdef CONFIG_ARCH_TEGRA_210_SOC 105 { .compatible = "nvidia,tegra210-efuse", .data = &tegra210_fuse_soc }, 106#endif 107#ifdef CONFIG_ARCH_TEGRA_132_SOC 108 { .compatible = "nvidia,tegra132-efuse", .data = &tegra124_fuse_soc }, 109#endif 110#ifdef CONFIG_ARCH_TEGRA_124_SOC 111 { .compatible = "nvidia,tegra124-efuse", .data = &tegra124_fuse_soc }, 112#endif 113#ifdef CONFIG_ARCH_TEGRA_114_SOC 114 { .compatible = "nvidia,tegra114-efuse", .data = &tegra114_fuse_soc }, 115#endif 116#ifdef CONFIG_ARCH_TEGRA_3x_SOC 117 { .compatible = "nvidia,tegra30-efuse", .data = &tegra30_fuse_soc }, 118#endif 119#ifdef CONFIG_ARCH_TEGRA_2x_SOC 120 { .compatible = "nvidia,tegra20-efuse", .data = &tegra20_fuse_soc }, 121#endif 122 { /* sentinel */ } 123}; 124 125static int tegra_fuse_probe(struct platform_device *pdev) 126{ 127 void __iomem *base = fuse->base; 128 struct resource *res; 129 int err; 130 131 /* take over the memory region from the early initialization */ 132 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 133 fuse->base = devm_ioremap_resource(&pdev->dev, res); 134 if (IS_ERR(fuse->base)) 135 return PTR_ERR(fuse->base); 136 137 fuse->clk = devm_clk_get(&pdev->dev, "fuse"); 138 if (IS_ERR(fuse->clk)) { 139 dev_err(&pdev->dev, "failed to get FUSE clock: %ld", 140 PTR_ERR(fuse->clk)); 141 return PTR_ERR(fuse->clk); 142 } 143 144 platform_set_drvdata(pdev, fuse); 145 fuse->dev = &pdev->dev; 146 147 if (fuse->soc->probe) { 148 err = fuse->soc->probe(fuse); 149 if (err < 0) 150 return err; 151 } 152 153 if (tegra_fuse_create_sysfs(&pdev->dev, fuse->soc->info->size, 154 fuse->soc->info)) 155 return -ENODEV; 156 157 /* release the early I/O memory mapping */ 158 iounmap(base); 159 160 return 0; 161} 162 163static struct platform_driver tegra_fuse_driver = { 164 .driver = { 165 .name = "tegra-fuse", 166 .of_match_table = tegra_fuse_match, 167 .suppress_bind_attrs = true, 168 }, 169 .probe = tegra_fuse_probe, 170}; 171module_platform_driver(tegra_fuse_driver); 172 173bool __init tegra_fuse_read_spare(unsigned int spare) 174{ 175 unsigned int offset = fuse->soc->info->spare + spare * 4; 176 177 return fuse->read_early(fuse, offset) & 1; 178} 179 180u32 __init tegra_fuse_read_early(unsigned int offset) 181{ 182 return fuse->read_early(fuse, offset); 183} 184 185int tegra_fuse_readl(unsigned long offset, u32 *value) 186{ 187 if (!fuse->read) 188 return -EPROBE_DEFER; 189 190 *value = fuse->read(fuse, offset); 191 192 return 0; 193} 194EXPORT_SYMBOL(tegra_fuse_readl); 195 196static void tegra_enable_fuse_clk(void __iomem *base) 197{ 198 u32 reg; 199 200 reg = readl_relaxed(base + 0x48); 201 reg |= 1 << 28; 202 writel(reg, base + 0x48); 203 204 /* 205 * Enable FUSE clock. This needs to be hardcoded because the clock 206 * subsystem is not active during early boot. 207 */ 208 reg = readl(base + 0x14); 209 reg |= 1 << 7; 210 writel(reg, base + 0x14); 211} 212 213static int __init tegra_init_fuse(void) 214{ 215 const struct of_device_id *match; 216 struct device_node *np; 217 struct resource regs; 218 219 tegra_init_apbmisc(); 220 221 np = of_find_matching_node_and_match(NULL, tegra_fuse_match, &match); 222 if (!np) { 223 /* 224 * Fall back to legacy initialization for 32-bit ARM only. All 225 * 64-bit ARM device tree files for Tegra are required to have 226 * a FUSE node. 227 * 228 * This is for backwards-compatibility with old device trees 229 * that didn't contain a FUSE node. 230 */ 231 if (IS_ENABLED(CONFIG_ARM) && soc_is_tegra()) { 232 u8 chip = tegra_get_chip_id(); 233 234 regs.start = 0x7000f800; 235 regs.end = 0x7000fbff; 236 regs.flags = IORESOURCE_MEM; 237 238 switch (chip) { 239#ifdef CONFIG_ARCH_TEGRA_2x_SOC 240 case TEGRA20: 241 fuse->soc = &tegra20_fuse_soc; 242 break; 243#endif 244 245#ifdef CONFIG_ARCH_TEGRA_3x_SOC 246 case TEGRA30: 247 fuse->soc = &tegra30_fuse_soc; 248 break; 249#endif 250 251#ifdef CONFIG_ARCH_TEGRA_114_SOC 252 case TEGRA114: 253 fuse->soc = &tegra114_fuse_soc; 254 break; 255#endif 256 257#ifdef CONFIG_ARCH_TEGRA_124_SOC 258 case TEGRA124: 259 fuse->soc = &tegra124_fuse_soc; 260 break; 261#endif 262 263 default: 264 pr_warn("Unsupported SoC: %02x\n", chip); 265 break; 266 } 267 } else { 268 /* 269 * At this point we're not running on Tegra, so play 270 * nice with multi-platform kernels. 271 */ 272 return 0; 273 } 274 } else { 275 /* 276 * Extract information from the device tree if we've found a 277 * matching node. 278 */ 279 if (of_address_to_resource(np, 0, ®s) < 0) { 280 pr_err("failed to get FUSE register\n"); 281 return -ENXIO; 282 } 283 284 fuse->soc = match->data; 285 } 286 287 np = of_find_matching_node(NULL, car_match); 288 if (np) { 289 void __iomem *base = of_iomap(np, 0); 290 if (base) { 291 tegra_enable_fuse_clk(base); 292 iounmap(base); 293 } else { 294 pr_err("failed to map clock registers\n"); 295 return -ENXIO; 296 } 297 } 298 299 fuse->base = ioremap_nocache(regs.start, resource_size(®s)); 300 if (!fuse->base) { 301 pr_err("failed to map FUSE registers\n"); 302 return -ENXIO; 303 } 304 305 fuse->soc->init(fuse); 306 307 pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n", 308 tegra_revision_name[tegra_sku_info.revision], 309 tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id, 310 tegra_sku_info.soc_process_id); 311 pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n", 312 tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); 313 314 return 0; 315} 316early_initcall(tegra_init_fuse); 317