• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * drivers/powergate/tegra-powergate.c
3  *
4  * Copyright (c) 2010 Google, Inc
5  *
6  * Author:
7  *	Colin Cross <ccross@google.com>
8  *
9  * This software is licensed under the terms of the GNU General Public
10  * License version 2, as published by the Free Software Foundation, and
11  * may be copied, distributed, and modified under those terms.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  */
19 
20 #include <linux/kernel.h>
21 #include <linux/clk.h>
22 #include <linux/debugfs.h>
23 #include <linux/delay.h>
24 #include <linux/err.h>
25 #include <linux/export.h>
26 #include <linux/init.h>
27 #include <linux/io.h>
28 #include <linux/seq_file.h>
29 #include <linux/spinlock.h>
30 #include <linux/clk/tegra.h>
31 #include <linux/tegra-powergate.h>
32 
33 #include "fuse.h"
34 #include "iomap.h"
35 
36 #define PWRGATE_TOGGLE		0x30
37 #define  PWRGATE_TOGGLE_START	(1 << 8)
38 
39 #define REMOVE_CLAMPING		0x34
40 
41 #define PWRGATE_STATUS		0x38
42 
43 static int tegra_num_powerdomains;
44 static int tegra_num_cpu_domains;
45 static u8 *tegra_cpu_domains;
46 static u8 tegra30_cpu_domains[] = {
47 	TEGRA_POWERGATE_CPU0,
48 	TEGRA_POWERGATE_CPU1,
49 	TEGRA_POWERGATE_CPU2,
50 	TEGRA_POWERGATE_CPU3,
51 };
52 
53 static DEFINE_SPINLOCK(tegra_powergate_lock);
54 
55 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
56 
pmc_read(unsigned long reg)57 static u32 pmc_read(unsigned long reg)
58 {
59 	return readl(pmc + reg);
60 }
61 
pmc_write(u32 val,unsigned long reg)62 static void pmc_write(u32 val, unsigned long reg)
63 {
64 	writel(val, pmc + reg);
65 }
66 
tegra_powergate_set(int id,bool new_state)67 static int tegra_powergate_set(int id, bool new_state)
68 {
69 	bool status;
70 	unsigned long flags;
71 
72 	spin_lock_irqsave(&tegra_powergate_lock, flags);
73 
74 	status = pmc_read(PWRGATE_STATUS) & (1 << id);
75 
76 	if (status == new_state) {
77 		spin_unlock_irqrestore(&tegra_powergate_lock, flags);
78 		return 0;
79 	}
80 
81 	pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
82 
83 	spin_unlock_irqrestore(&tegra_powergate_lock, flags);
84 
85 	return 0;
86 }
87 
tegra_powergate_power_on(int id)88 int tegra_powergate_power_on(int id)
89 {
90 	if (id < 0 || id >= tegra_num_powerdomains)
91 		return -EINVAL;
92 
93 	return tegra_powergate_set(id, true);
94 }
95 
tegra_powergate_power_off(int id)96 int tegra_powergate_power_off(int id)
97 {
98 	if (id < 0 || id >= tegra_num_powerdomains)
99 		return -EINVAL;
100 
101 	return tegra_powergate_set(id, false);
102 }
103 
tegra_powergate_is_powered(int id)104 int tegra_powergate_is_powered(int id)
105 {
106 	u32 status;
107 
108 	if (id < 0 || id >= tegra_num_powerdomains)
109 		return -EINVAL;
110 
111 	status = pmc_read(PWRGATE_STATUS) & (1 << id);
112 	return !!status;
113 }
114 
tegra_powergate_remove_clamping(int id)115 int tegra_powergate_remove_clamping(int id)
116 {
117 	u32 mask;
118 
119 	if (id < 0 || id >= tegra_num_powerdomains)
120 		return -EINVAL;
121 
122 	/*
123 	 * Tegra 2 has a bug where PCIE and VDE clamping masks are
124 	 * swapped relatively to the partition ids
125 	 */
126 	if (id ==  TEGRA_POWERGATE_VDEC)
127 		mask = (1 << TEGRA_POWERGATE_PCIE);
128 	else if	(id == TEGRA_POWERGATE_PCIE)
129 		mask = (1 << TEGRA_POWERGATE_VDEC);
130 	else
131 		mask = (1 << id);
132 
133 	pmc_write(mask, REMOVE_CLAMPING);
134 
135 	return 0;
136 }
137 
138 /* Must be called with clk disabled, and returns with clk enabled */
tegra_powergate_sequence_power_up(int id,struct clk * clk)139 int tegra_powergate_sequence_power_up(int id, struct clk *clk)
140 {
141 	int ret;
142 
143 	tegra_periph_reset_assert(clk);
144 
145 	ret = tegra_powergate_power_on(id);
146 	if (ret)
147 		goto err_power;
148 
149 	ret = clk_prepare_enable(clk);
150 	if (ret)
151 		goto err_clk;
152 
153 	udelay(10);
154 
155 	ret = tegra_powergate_remove_clamping(id);
156 	if (ret)
157 		goto err_clamp;
158 
159 	udelay(10);
160 	tegra_periph_reset_deassert(clk);
161 
162 	return 0;
163 
164 err_clamp:
165 	clk_disable_unprepare(clk);
166 err_clk:
167 	tegra_powergate_power_off(id);
168 err_power:
169 	return ret;
170 }
171 EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
172 
tegra_cpu_powergate_id(int cpuid)173 int tegra_cpu_powergate_id(int cpuid)
174 {
175 	if (cpuid > 0 && cpuid < tegra_num_cpu_domains)
176 		return tegra_cpu_domains[cpuid];
177 
178 	return -EINVAL;
179 }
180 
tegra_powergate_init(void)181 int __init tegra_powergate_init(void)
182 {
183 	switch (tegra_chip_id) {
184 	case TEGRA20:
185 		tegra_num_powerdomains = 7;
186 		break;
187 	case TEGRA30:
188 		tegra_num_powerdomains = 14;
189 		tegra_num_cpu_domains = 4;
190 		tegra_cpu_domains = tegra30_cpu_domains;
191 		break;
192 	default:
193 		/* Unknown Tegra variant. Disable powergating */
194 		tegra_num_powerdomains = 0;
195 		break;
196 	}
197 
198 	return 0;
199 }
200 
201 #ifdef CONFIG_DEBUG_FS
202 
203 static const char * const *powergate_name;
204 
205 static const char * const powergate_name_t20[] = {
206 	[TEGRA_POWERGATE_CPU]	= "cpu",
207 	[TEGRA_POWERGATE_3D]	= "3d",
208 	[TEGRA_POWERGATE_VENC]	= "venc",
209 	[TEGRA_POWERGATE_VDEC]	= "vdec",
210 	[TEGRA_POWERGATE_PCIE]	= "pcie",
211 	[TEGRA_POWERGATE_L2]	= "l2",
212 	[TEGRA_POWERGATE_MPE]	= "mpe",
213 };
214 
215 static const char * const powergate_name_t30[] = {
216 	[TEGRA_POWERGATE_CPU]	= "cpu0",
217 	[TEGRA_POWERGATE_3D]	= "3d0",
218 	[TEGRA_POWERGATE_VENC]	= "venc",
219 	[TEGRA_POWERGATE_VDEC]	= "vdec",
220 	[TEGRA_POWERGATE_PCIE]	= "pcie",
221 	[TEGRA_POWERGATE_L2]	= "l2",
222 	[TEGRA_POWERGATE_MPE]	= "mpe",
223 	[TEGRA_POWERGATE_HEG]	= "heg",
224 	[TEGRA_POWERGATE_SATA]	= "sata",
225 	[TEGRA_POWERGATE_CPU1]	= "cpu1",
226 	[TEGRA_POWERGATE_CPU2]	= "cpu2",
227 	[TEGRA_POWERGATE_CPU3]	= "cpu3",
228 	[TEGRA_POWERGATE_CELP]	= "celp",
229 	[TEGRA_POWERGATE_3D1]	= "3d1",
230 };
231 
powergate_show(struct seq_file * s,void * data)232 static int powergate_show(struct seq_file *s, void *data)
233 {
234 	int i;
235 
236 	seq_printf(s, " powergate powered\n");
237 	seq_printf(s, "------------------\n");
238 
239 	for (i = 0; i < tegra_num_powerdomains; i++)
240 		seq_printf(s, " %9s %7s\n", powergate_name[i],
241 			tegra_powergate_is_powered(i) ? "yes" : "no");
242 	return 0;
243 }
244 
powergate_open(struct inode * inode,struct file * file)245 static int powergate_open(struct inode *inode, struct file *file)
246 {
247 	return single_open(file, powergate_show, inode->i_private);
248 }
249 
250 static const struct file_operations powergate_fops = {
251 	.open		= powergate_open,
252 	.read		= seq_read,
253 	.llseek		= seq_lseek,
254 	.release	= single_release,
255 };
256 
tegra_powergate_debugfs_init(void)257 int __init tegra_powergate_debugfs_init(void)
258 {
259 	struct dentry *d;
260 
261 	switch (tegra_chip_id) {
262 	case TEGRA20:
263 		powergate_name = powergate_name_t20;
264 		break;
265 	case TEGRA30:
266 		powergate_name = powergate_name_t30;
267 		break;
268 	}
269 
270 	if (powergate_name) {
271 		d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
272 			&powergate_fops);
273 		if (!d)
274 			return -ENOMEM;
275 	}
276 
277 	return 0;
278 }
279 
280 #endif
281