linux/arch/arm/mach-tegra/powergate.c
<<
>>
Prefs
   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#define PWRGATE_TOGGLE          0x30
  35#define  PWRGATE_TOGGLE_START   (1 << 8)
  36
  37#define REMOVE_CLAMPING         0x34
  38
  39#define PWRGATE_STATUS          0x38
  40
  41static DEFINE_SPINLOCK(tegra_powergate_lock);
  42
  43static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
  44
  45static u32 pmc_read(unsigned long reg)
  46{
  47        return readl(pmc + reg);
  48}
  49
  50static void pmc_write(u32 val, unsigned long reg)
  51{
  52        writel(val, pmc + reg);
  53}
  54
  55static int tegra_powergate_set(int id, bool new_state)
  56{
  57        bool status;
  58        unsigned long flags;
  59
  60        spin_lock_irqsave(&tegra_powergate_lock, flags);
  61
  62        status = pmc_read(PWRGATE_STATUS) & (1 << id);
  63
  64        if (status == new_state) {
  65                spin_unlock_irqrestore(&tegra_powergate_lock, flags);
  66                return -EINVAL;
  67        }
  68
  69        pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
  70
  71        spin_unlock_irqrestore(&tegra_powergate_lock, flags);
  72
  73        return 0;
  74}
  75
  76int tegra_powergate_power_on(int id)
  77{
  78        if (id < 0 || id >= TEGRA_NUM_POWERGATE)
  79                return -EINVAL;
  80
  81        return tegra_powergate_set(id, true);
  82}
  83
  84int tegra_powergate_power_off(int id)
  85{
  86        if (id < 0 || id >= TEGRA_NUM_POWERGATE)
  87                return -EINVAL;
  88
  89        return tegra_powergate_set(id, false);
  90}
  91
  92bool tegra_powergate_is_powered(int id)
  93{
  94        u32 status;
  95
  96        if (id < 0 || id >= TEGRA_NUM_POWERGATE)
  97                return -EINVAL;
  98
  99        status = pmc_read(PWRGATE_STATUS) & (1 << id);
 100        return !!status;
 101}
 102
 103int tegra_powergate_remove_clamping(int id)
 104{
 105        u32 mask;
 106
 107        if (id < 0 || id >= TEGRA_NUM_POWERGATE)
 108                return -EINVAL;
 109
 110        /*
 111         * Tegra 2 has a bug where PCIE and VDE clamping masks are
 112         * swapped relatively to the partition ids
 113         */
 114        if (id ==  TEGRA_POWERGATE_VDEC)
 115                mask = (1 << TEGRA_POWERGATE_PCIE);
 116        else if (id == TEGRA_POWERGATE_PCIE)
 117                mask = (1 << TEGRA_POWERGATE_VDEC);
 118        else
 119                mask = (1 << id);
 120
 121        pmc_write(mask, REMOVE_CLAMPING);
 122
 123        return 0;
 124}
 125
 126/* Must be called with clk disabled, and returns with clk enabled */
 127int tegra_powergate_sequence_power_up(int id, struct clk *clk)
 128{
 129        int ret;
 130
 131        tegra_periph_reset_assert(clk);
 132
 133        ret = tegra_powergate_power_on(id);
 134        if (ret)
 135                goto err_power;
 136
 137        ret = clk_enable(clk);
 138        if (ret)
 139                goto err_clk;
 140
 141        udelay(10);
 142
 143        ret = tegra_powergate_remove_clamping(id);
 144        if (ret)
 145                goto err_clamp;
 146
 147        udelay(10);
 148        tegra_periph_reset_deassert(clk);
 149
 150        return 0;
 151
 152err_clamp:
 153        clk_disable(clk);
 154err_clk:
 155        tegra_powergate_power_off(id);
 156err_power:
 157        return ret;
 158}
 159
 160#ifdef CONFIG_DEBUG_FS
 161
 162static const char * const powergate_name[] = {
 163        [TEGRA_POWERGATE_CPU]   = "cpu",
 164        [TEGRA_POWERGATE_3D]    = "3d",
 165        [TEGRA_POWERGATE_VENC]  = "venc",
 166        [TEGRA_POWERGATE_VDEC]  = "vdec",
 167        [TEGRA_POWERGATE_PCIE]  = "pcie",
 168        [TEGRA_POWERGATE_L2]    = "l2",
 169        [TEGRA_POWERGATE_MPE]   = "mpe",
 170};
 171
 172static int powergate_show(struct seq_file *s, void *data)
 173{
 174        int i;
 175
 176        seq_printf(s, " powergate powered\n");
 177        seq_printf(s, "------------------\n");
 178
 179        for (i = 0; i < TEGRA_NUM_POWERGATE; i++)
 180                seq_printf(s, " %9s %7s\n", powergate_name[i],
 181                        tegra_powergate_is_powered(i) ? "yes" : "no");
 182        return 0;
 183}
 184
 185static int powergate_open(struct inode *inode, struct file *file)
 186{
 187        return single_open(file, powergate_show, inode->i_private);
 188}
 189
 190static const struct file_operations powergate_fops = {
 191        .open           = powergate_open,
 192        .read           = seq_read,
 193        .llseek         = seq_lseek,
 194        .release        = single_release,
 195};
 196
 197static int __init powergate_debugfs_init(void)
 198{
 199        struct dentry *d;
 200        int err = -ENOMEM;
 201
 202        d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
 203                &powergate_fops);
 204        if (!d)
 205                return -ENOMEM;
 206
 207        return err;
 208}
 209
 210late_initcall(powergate_debugfs_init);
 211
 212#endif
 213