Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  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
 43static int tegra_num_powerdomains;
 44static int tegra_num_cpu_domains;
 45static u8 *tegra_cpu_domains;
 46static u8 tegra30_cpu_domains[] = {
 47	TEGRA_POWERGATE_CPU0,
 48	TEGRA_POWERGATE_CPU1,
 49	TEGRA_POWERGATE_CPU2,
 50	TEGRA_POWERGATE_CPU3,
 51};
 52
 53static DEFINE_SPINLOCK(tegra_powergate_lock);
 54
 55static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
 56
 57static u32 pmc_read(unsigned long reg)
 58{
 59	return readl(pmc + reg);
 60}
 61
 62static void pmc_write(u32 val, unsigned long reg)
 63{
 64	writel(val, pmc + reg);
 65}
 66
 67static 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
 88int 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
 96int 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
104int 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
115int 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 */
139int 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
164err_clamp:
165	clk_disable(clk);
166err_clk:
167	tegra_powergate_power_off(id);
168err_power:
169	return ret;
170}
171
172int 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
180int __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
202static 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
212static 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
225static int powergate_open(struct inode *inode, struct file *file)
226{
227	return single_open(file, powergate_show, inode->i_private);
228}
229
230static const struct file_operations powergate_fops = {
231	.open		= powergate_open,
232	.read		= seq_read,
233	.llseek		= seq_lseek,
234	.release	= single_release,
235};
236
237int __init tegra_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#endif