• 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/init.h>
26 #include <linux/io.h>
27 #include <linux/seq_file.h>
28 #include <linux/spinlock.h>
29 
30 #include <mach/clk.h>
31 #include <mach/iomap.h>
32 #include <mach/powergate.h>
33 
34 #include "fuse.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 -EINVAL;
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_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(clk);
166 err_clk:
167 	tegra_powergate_power_off(id);
168 err_power:
169 	return ret;
170 }
171 
tegra_cpu_powergate_id(int cpuid)172 int tegra_cpu_powergate_id(int cpuid)
173 {
174 	if (cpuid > 0 && cpuid < tegra_num_cpu_domains)
175 		return tegra_cpu_domains[cpuid];
176 
177 	return -EINVAL;
178 }
179 
tegra_powergate_init(void)180 int __init tegra_powergate_init(void)
181 {
182 	switch (tegra_chip_id) {
183 	case TEGRA20:
184 		tegra_num_powerdomains = 7;
185 		break;
186 	case TEGRA30:
187 		tegra_num_powerdomains = 14;
188 		tegra_num_cpu_domains = 4;
189 		tegra_cpu_domains = tegra30_cpu_domains;
190 		break;
191 	default:
192 		/* Unknown Tegra variant. Disable powergating */
193 		tegra_num_powerdomains = 0;
194 		break;
195 	}
196 
197 	return 0;
198 }
199 
200 #ifdef CONFIG_DEBUG_FS
201 
202 static const char * const powergate_name[] = {
203 	[TEGRA_POWERGATE_CPU]	= "cpu",
204 	[TEGRA_POWERGATE_3D]	= "3d",
205 	[TEGRA_POWERGATE_VENC]	= "venc",
206 	[TEGRA_POWERGATE_VDEC]	= "vdec",
207 	[TEGRA_POWERGATE_PCIE]	= "pcie",
208 	[TEGRA_POWERGATE_L2]	= "l2",
209 	[TEGRA_POWERGATE_MPE]	= "mpe",
210 };
211 
powergate_show(struct seq_file * s,void * data)212 static int powergate_show(struct seq_file *s, void *data)
213 {
214 	int i;
215 
216 	seq_printf(s, " powergate powered\n");
217 	seq_printf(s, "------------------\n");
218 
219 	for (i = 0; i < tegra_num_powerdomains; i++)
220 		seq_printf(s, " %9s %7s\n", powergate_name[i],
221 			tegra_powergate_is_powered(i) ? "yes" : "no");
222 	return 0;
223 }
224 
powergate_open(struct inode * inode,struct file * file)225 static int powergate_open(struct inode *inode, struct file *file)
226 {
227 	return single_open(file, powergate_show, inode->i_private);
228 }
229 
230 static const struct file_operations powergate_fops = {
231 	.open		= powergate_open,
232 	.read		= seq_read,
233 	.llseek		= seq_lseek,
234 	.release	= single_release,
235 };
236 
powergate_debugfs_init(void)237 static int __init powergate_debugfs_init(void)
238 {
239 	struct dentry *d;
240 	int err = -ENOMEM;
241 
242 	d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
243 		&powergate_fops);
244 	if (!d)
245 		return -ENOMEM;
246 
247 	return err;
248 }
249 
250 late_initcall(powergate_debugfs_init);
251 
252 #endif
253