fuse-tegra.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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. #include <linux/device.h>
  18. #include <linux/kobject.h>
  19. #include <linux/kernel.h>
  20. #include <linux/platform_device.h>
  21. #include <linux/of.h>
  22. #include <linux/of_address.h>
  23. #include <linux/io.h>
  24. #include <soc/tegra/common.h>
  25. #include <soc/tegra/fuse.h>
  26. #include "fuse.h"
  27. static u32 (*fuse_readl)(const unsigned int offset);
  28. static int fuse_size;
  29. struct tegra_sku_info tegra_sku_info;
  30. static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
  31. [TEGRA_REVISION_UNKNOWN] = "unknown",
  32. [TEGRA_REVISION_A01] = "A01",
  33. [TEGRA_REVISION_A02] = "A02",
  34. [TEGRA_REVISION_A03] = "A03",
  35. [TEGRA_REVISION_A03p] = "A03 prime",
  36. [TEGRA_REVISION_A04] = "A04",
  37. };
  38. static u8 fuse_readb(const unsigned int offset)
  39. {
  40. u32 val;
  41. val = fuse_readl(round_down(offset, 4));
  42. val >>= (offset % 4) * 8;
  43. val &= 0xff;
  44. return val;
  45. }
  46. static ssize_t fuse_read(struct file *fd, struct kobject *kobj,
  47. struct bin_attribute *attr, char *buf,
  48. loff_t pos, size_t size)
  49. {
  50. int i;
  51. if (pos < 0 || pos >= fuse_size)
  52. return 0;
  53. if (size > fuse_size - pos)
  54. size = fuse_size - pos;
  55. for (i = 0; i < size; i++)
  56. buf[i] = fuse_readb(pos + i);
  57. return i;
  58. }
  59. static struct bin_attribute fuse_bin_attr = {
  60. .attr = { .name = "fuse", .mode = S_IRUGO, },
  61. .read = fuse_read,
  62. };
  63. static const struct of_device_id car_match[] __initconst = {
  64. { .compatible = "nvidia,tegra20-car", },
  65. { .compatible = "nvidia,tegra30-car", },
  66. { .compatible = "nvidia,tegra114-car", },
  67. { .compatible = "nvidia,tegra124-car", },
  68. {},
  69. };
  70. static void tegra_enable_fuse_clk(void __iomem *base)
  71. {
  72. u32 reg;
  73. reg = readl_relaxed(base + 0x48);
  74. reg |= 1 << 28;
  75. writel(reg, base + 0x48);
  76. /*
  77. * Enable FUSE clock. This needs to be hardcoded because the clock
  78. * subsystem is not active during early boot.
  79. */
  80. reg = readl(base + 0x14);
  81. reg |= 1 << 7;
  82. writel(reg, base + 0x14);
  83. }
  84. int tegra_fuse_readl(unsigned long offset, u32 *value)
  85. {
  86. if (!fuse_readl)
  87. return -EPROBE_DEFER;
  88. *value = fuse_readl(offset);
  89. return 0;
  90. }
  91. EXPORT_SYMBOL(tegra_fuse_readl);
  92. int tegra_fuse_create_sysfs(struct device *dev, int size,
  93. u32 (*readl)(const unsigned int offset))
  94. {
  95. if (fuse_size)
  96. return -ENODEV;
  97. fuse_bin_attr.size = size;
  98. fuse_bin_attr.read = fuse_read;
  99. fuse_size = size;
  100. fuse_readl = readl;
  101. return device_create_bin_file(dev, &fuse_bin_attr);
  102. }
  103. static int __init tegra_init_fuse(void)
  104. {
  105. struct device_node *np;
  106. void __iomem *car_base;
  107. if (!soc_is_tegra())
  108. return 0;
  109. tegra_init_apbmisc();
  110. np = of_find_matching_node(NULL, car_match);
  111. car_base = of_iomap(np, 0);
  112. if (car_base) {
  113. tegra_enable_fuse_clk(car_base);
  114. iounmap(car_base);
  115. } else {
  116. pr_err("Could not enable fuse clk. ioremap tegra car failed.\n");
  117. return -ENXIO;
  118. }
  119. if (tegra_get_chip_id() == TEGRA20)
  120. tegra20_init_fuse_early();
  121. else
  122. tegra30_init_fuse_early();
  123. pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n",
  124. tegra_revision_name[tegra_sku_info.revision],
  125. tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id,
  126. tegra_sku_info.core_process_id);
  127. pr_debug("Tegra CPU Speedo ID %d, Soc Speedo ID %d\n",
  128. tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id);
  129. return 0;
  130. }
  131. early_initcall(tegra_init_fuse);