xref: /linux/drivers/soc/tegra/fuse/fuse-tegra.c (revision 783c8f4c84451bc444e314a71b447239c6ef6fd9)
1*783c8f4cSPeter De Schrijver /*
2*783c8f4cSPeter De Schrijver  * Copyright (c) 2013-2014, NVIDIA CORPORATION.  All rights reserved.
3*783c8f4cSPeter De Schrijver  *
4*783c8f4cSPeter De Schrijver  * This program is free software; you can redistribute it and/or modify it
5*783c8f4cSPeter De Schrijver  * under the terms and conditions of the GNU General Public License,
6*783c8f4cSPeter De Schrijver  * version 2, as published by the Free Software Foundation.
7*783c8f4cSPeter De Schrijver  *
8*783c8f4cSPeter De Schrijver  * This program is distributed in the hope it will be useful, but WITHOUT
9*783c8f4cSPeter De Schrijver  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10*783c8f4cSPeter De Schrijver  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11*783c8f4cSPeter De Schrijver  * more details.
12*783c8f4cSPeter De Schrijver  *
13*783c8f4cSPeter De Schrijver  * You should have received a copy of the GNU General Public License
14*783c8f4cSPeter De Schrijver  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15*783c8f4cSPeter De Schrijver  *
16*783c8f4cSPeter De Schrijver  */
17*783c8f4cSPeter De Schrijver 
18*783c8f4cSPeter De Schrijver #include <linux/device.h>
19*783c8f4cSPeter De Schrijver #include <linux/kobject.h>
20*783c8f4cSPeter De Schrijver #include <linux/kernel.h>
21*783c8f4cSPeter De Schrijver #include <linux/platform_device.h>
22*783c8f4cSPeter De Schrijver #include <linux/of.h>
23*783c8f4cSPeter De Schrijver #include <linux/of_address.h>
24*783c8f4cSPeter De Schrijver #include <linux/io.h>
25*783c8f4cSPeter De Schrijver 
26*783c8f4cSPeter De Schrijver #include <soc/tegra/fuse.h>
27*783c8f4cSPeter De Schrijver 
28*783c8f4cSPeter De Schrijver #include "fuse.h"
29*783c8f4cSPeter De Schrijver 
30*783c8f4cSPeter De Schrijver static u32 (*fuse_readl)(const unsigned int offset);
31*783c8f4cSPeter De Schrijver static int fuse_size;
32*783c8f4cSPeter De Schrijver struct tegra_sku_info tegra_sku_info;
33*783c8f4cSPeter De Schrijver 
34*783c8f4cSPeter De Schrijver static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
35*783c8f4cSPeter De Schrijver 	[TEGRA_REVISION_UNKNOWN] = "unknown",
36*783c8f4cSPeter De Schrijver 	[TEGRA_REVISION_A01]     = "A01",
37*783c8f4cSPeter De Schrijver 	[TEGRA_REVISION_A02]     = "A02",
38*783c8f4cSPeter De Schrijver 	[TEGRA_REVISION_A03]     = "A03",
39*783c8f4cSPeter De Schrijver 	[TEGRA_REVISION_A03p]    = "A03 prime",
40*783c8f4cSPeter De Schrijver 	[TEGRA_REVISION_A04]     = "A04",
41*783c8f4cSPeter De Schrijver };
42*783c8f4cSPeter De Schrijver 
43*783c8f4cSPeter De Schrijver static u8 fuse_readb(const unsigned int offset)
44*783c8f4cSPeter De Schrijver {
45*783c8f4cSPeter De Schrijver 	u32 val;
46*783c8f4cSPeter De Schrijver 
47*783c8f4cSPeter De Schrijver 	val = fuse_readl(round_down(offset, 4));
48*783c8f4cSPeter De Schrijver 	val >>= (offset % 4) * 8;
49*783c8f4cSPeter De Schrijver 	val &= 0xff;
50*783c8f4cSPeter De Schrijver 
51*783c8f4cSPeter De Schrijver 	return val;
52*783c8f4cSPeter De Schrijver }
53*783c8f4cSPeter De Schrijver 
54*783c8f4cSPeter De Schrijver static ssize_t fuse_read(struct file *fd, struct kobject *kobj,
55*783c8f4cSPeter De Schrijver 			struct bin_attribute *attr, char *buf,
56*783c8f4cSPeter De Schrijver 			loff_t pos, size_t size)
57*783c8f4cSPeter De Schrijver {
58*783c8f4cSPeter De Schrijver 	int i;
59*783c8f4cSPeter De Schrijver 
60*783c8f4cSPeter De Schrijver 	if (pos < 0 || pos >= fuse_size)
61*783c8f4cSPeter De Schrijver 		return 0;
62*783c8f4cSPeter De Schrijver 
63*783c8f4cSPeter De Schrijver 	if (size > fuse_size - pos)
64*783c8f4cSPeter De Schrijver 		size = fuse_size - pos;
65*783c8f4cSPeter De Schrijver 
66*783c8f4cSPeter De Schrijver 	for (i = 0; i < size; i++)
67*783c8f4cSPeter De Schrijver 		buf[i] = fuse_readb(pos + i);
68*783c8f4cSPeter De Schrijver 
69*783c8f4cSPeter De Schrijver 	return i;
70*783c8f4cSPeter De Schrijver }
71*783c8f4cSPeter De Schrijver 
72*783c8f4cSPeter De Schrijver static struct bin_attribute fuse_bin_attr = {
73*783c8f4cSPeter De Schrijver 	.attr = { .name = "fuse", .mode = S_IRUGO, },
74*783c8f4cSPeter De Schrijver 	.read = fuse_read,
75*783c8f4cSPeter De Schrijver };
76*783c8f4cSPeter De Schrijver 
77*783c8f4cSPeter De Schrijver static const struct of_device_id car_match[] __initconst = {
78*783c8f4cSPeter De Schrijver 	{ .compatible = "nvidia,tegra20-car", },
79*783c8f4cSPeter De Schrijver 	{ .compatible = "nvidia,tegra30-car", },
80*783c8f4cSPeter De Schrijver 	{ .compatible = "nvidia,tegra114-car", },
81*783c8f4cSPeter De Schrijver 	{ .compatible = "nvidia,tegra124-car", },
82*783c8f4cSPeter De Schrijver 	{},
83*783c8f4cSPeter De Schrijver };
84*783c8f4cSPeter De Schrijver 
85*783c8f4cSPeter De Schrijver static void tegra_enable_fuse_clk(void __iomem *base)
86*783c8f4cSPeter De Schrijver {
87*783c8f4cSPeter De Schrijver 	u32 reg;
88*783c8f4cSPeter De Schrijver 
89*783c8f4cSPeter De Schrijver 	reg = readl_relaxed(base + 0x48);
90*783c8f4cSPeter De Schrijver 	reg |= 1 << 28;
91*783c8f4cSPeter De Schrijver 	writel(reg, base + 0x48);
92*783c8f4cSPeter De Schrijver 
93*783c8f4cSPeter De Schrijver 	/*
94*783c8f4cSPeter De Schrijver 	 * Enable FUSE clock. This needs to be hardcoded because the clock
95*783c8f4cSPeter De Schrijver 	 * subsystem is not active during early boot.
96*783c8f4cSPeter De Schrijver 	 */
97*783c8f4cSPeter De Schrijver 	reg = readl(base + 0x14);
98*783c8f4cSPeter De Schrijver 	reg |= 1 << 7;
99*783c8f4cSPeter De Schrijver 	writel(reg, base + 0x14);
100*783c8f4cSPeter De Schrijver }
101*783c8f4cSPeter De Schrijver 
102*783c8f4cSPeter De Schrijver int tegra_fuse_readl(unsigned long offset, u32 *value)
103*783c8f4cSPeter De Schrijver {
104*783c8f4cSPeter De Schrijver 	if (!fuse_readl)
105*783c8f4cSPeter De Schrijver 		return -EPROBE_DEFER;
106*783c8f4cSPeter De Schrijver 
107*783c8f4cSPeter De Schrijver 	*value = fuse_readl(offset);
108*783c8f4cSPeter De Schrijver 
109*783c8f4cSPeter De Schrijver 	return 0;
110*783c8f4cSPeter De Schrijver }
111*783c8f4cSPeter De Schrijver EXPORT_SYMBOL(tegra_fuse_readl);
112*783c8f4cSPeter De Schrijver 
113*783c8f4cSPeter De Schrijver int tegra_fuse_create_sysfs(struct device *dev, int size,
114*783c8f4cSPeter De Schrijver 		     u32 (*readl)(const unsigned int offset))
115*783c8f4cSPeter De Schrijver {
116*783c8f4cSPeter De Schrijver 	if (fuse_size)
117*783c8f4cSPeter De Schrijver 		return -ENODEV;
118*783c8f4cSPeter De Schrijver 
119*783c8f4cSPeter De Schrijver 	fuse_bin_attr.size = size;
120*783c8f4cSPeter De Schrijver 	fuse_bin_attr.read = fuse_read;
121*783c8f4cSPeter De Schrijver 
122*783c8f4cSPeter De Schrijver 	fuse_size = size;
123*783c8f4cSPeter De Schrijver 	fuse_readl = readl;
124*783c8f4cSPeter De Schrijver 
125*783c8f4cSPeter De Schrijver 	return device_create_bin_file(dev, &fuse_bin_attr);
126*783c8f4cSPeter De Schrijver }
127*783c8f4cSPeter De Schrijver 
128*783c8f4cSPeter De Schrijver void __init tegra_init_fuse(void)
129*783c8f4cSPeter De Schrijver {
130*783c8f4cSPeter De Schrijver 	struct device_node *np;
131*783c8f4cSPeter De Schrijver 	void __iomem *car_base;
132*783c8f4cSPeter De Schrijver 
133*783c8f4cSPeter De Schrijver 	tegra_init_apbmisc();
134*783c8f4cSPeter De Schrijver 
135*783c8f4cSPeter De Schrijver 	np = of_find_matching_node(NULL, car_match);
136*783c8f4cSPeter De Schrijver 	car_base = of_iomap(np, 0);
137*783c8f4cSPeter De Schrijver 	if (car_base) {
138*783c8f4cSPeter De Schrijver 		tegra_enable_fuse_clk(car_base);
139*783c8f4cSPeter De Schrijver 		iounmap(car_base);
140*783c8f4cSPeter De Schrijver 	} else {
141*783c8f4cSPeter De Schrijver 		pr_err("Could not enable fuse clk. ioremap tegra car failed.\n");
142*783c8f4cSPeter De Schrijver 		return;
143*783c8f4cSPeter De Schrijver 	}
144*783c8f4cSPeter De Schrijver 
145*783c8f4cSPeter De Schrijver 	if (tegra_get_chip_id() == TEGRA20)
146*783c8f4cSPeter De Schrijver 		tegra20_init_fuse_early();
147*783c8f4cSPeter De Schrijver 	else
148*783c8f4cSPeter De Schrijver 		tegra30_init_fuse_early();
149*783c8f4cSPeter De Schrijver 
150*783c8f4cSPeter De Schrijver 	pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n",
151*783c8f4cSPeter De Schrijver 		tegra_revision_name[tegra_sku_info.revision],
152*783c8f4cSPeter De Schrijver 		tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id,
153*783c8f4cSPeter De Schrijver 		tegra_sku_info.core_process_id);
154*783c8f4cSPeter De Schrijver 	pr_debug("Tegra CPU Speedo ID %d, Soc Speedo ID %d\n",
155*783c8f4cSPeter De Schrijver 		tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id);
156*783c8f4cSPeter De Schrijver }
157