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/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
  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 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
  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_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
 164err_clamp:
 165        clk_disable_unprepare(clk);
 166err_clk:
 167        tegra_powergate_power_off(id);
 168err_power:
 169        return ret;
 170}
 171EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
 172
 173int 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
 181int __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
 203static const char * const *powergate_name;
 204
 205static 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
 215static 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
 232static 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
 245static int powergate_open(struct inode *inode, struct file *file)
 246{
 247        return single_open(file, powergate_show, inode->i_private);
 248}
 249
 250static const struct file_operations powergate_fops = {
 251        .open           = powergate_open,
 252        .read           = seq_read,
 253        .llseek         = seq_lseek,
 254        .release        = single_release,
 255};
 256
 257int __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