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 const u8 *tegra_cpu_domains;
  46
  47static const u8 tegra30_cpu_domains[] = {
  48        TEGRA_POWERGATE_CPU,
  49        TEGRA_POWERGATE_CPU1,
  50        TEGRA_POWERGATE_CPU2,
  51        TEGRA_POWERGATE_CPU3,
  52};
  53
  54static const u8 tegra114_cpu_domains[] = {
  55        TEGRA_POWERGATE_CPU0,
  56        TEGRA_POWERGATE_CPU1,
  57        TEGRA_POWERGATE_CPU2,
  58        TEGRA_POWERGATE_CPU3,
  59};
  60
  61static DEFINE_SPINLOCK(tegra_powergate_lock);
  62
  63static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
  64
  65static u32 pmc_read(unsigned long reg)
  66{
  67        return readl(pmc + reg);
  68}
  69
  70static void pmc_write(u32 val, unsigned long reg)
  71{
  72        writel(val, pmc + reg);
  73}
  74
  75static int tegra_powergate_set(int id, bool new_state)
  76{
  77        bool status;
  78        unsigned long flags;
  79
  80        spin_lock_irqsave(&tegra_powergate_lock, flags);
  81
  82        status = pmc_read(PWRGATE_STATUS) & (1 << id);
  83
  84        if (status == new_state) {
  85                spin_unlock_irqrestore(&tegra_powergate_lock, flags);
  86                return 0;
  87        }
  88
  89        pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
  90
  91        spin_unlock_irqrestore(&tegra_powergate_lock, flags);
  92
  93        return 0;
  94}
  95
  96int tegra_powergate_power_on(int id)
  97{
  98        if (id < 0 || id >= tegra_num_powerdomains)
  99                return -EINVAL;
 100
 101        return tegra_powergate_set(id, true);
 102}
 103
 104int tegra_powergate_power_off(int id)
 105{
 106        if (id < 0 || id >= tegra_num_powerdomains)
 107                return -EINVAL;
 108
 109        return tegra_powergate_set(id, false);
 110}
 111
 112int tegra_powergate_is_powered(int id)
 113{
 114        u32 status;
 115
 116        if (id < 0 || id >= tegra_num_powerdomains)
 117                return -EINVAL;
 118
 119        status = pmc_read(PWRGATE_STATUS) & (1 << id);
 120        return !!status;
 121}
 122
 123int tegra_powergate_remove_clamping(int id)
 124{
 125        u32 mask;
 126
 127        if (id < 0 || id >= tegra_num_powerdomains)
 128                return -EINVAL;
 129
 130        /*
 131         * Tegra 2 has a bug where PCIE and VDE clamping masks are
 132         * swapped relatively to the partition ids
 133         */
 134        if (id ==  TEGRA_POWERGATE_VDEC)
 135                mask = (1 << TEGRA_POWERGATE_PCIE);
 136        else if (id == TEGRA_POWERGATE_PCIE)
 137                mask = (1 << TEGRA_POWERGATE_VDEC);
 138        else
 139                mask = (1 << id);
 140
 141        pmc_write(mask, REMOVE_CLAMPING);
 142
 143        return 0;
 144}
 145
 146/* Must be called with clk disabled, and returns with clk enabled */
 147int tegra_powergate_sequence_power_up(int id, struct clk *clk)
 148{
 149        int ret;
 150
 151        tegra_periph_reset_assert(clk);
 152
 153        ret = tegra_powergate_power_on(id);
 154        if (ret)
 155                goto err_power;
 156
 157        ret = clk_prepare_enable(clk);
 158        if (ret)
 159                goto err_clk;
 160
 161        udelay(10);
 162
 163        ret = tegra_powergate_remove_clamping(id);
 164        if (ret)
 165                goto err_clamp;
 166
 167        udelay(10);
 168        tegra_periph_reset_deassert(clk);
 169
 170        return 0;
 171
 172err_clamp:
 173        clk_disable_unprepare(clk);
 174err_clk:
 175        tegra_powergate_power_off(id);
 176err_power:
 177        return ret;
 178}
 179EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
 180
 181int tegra_cpu_powergate_id(int cpuid)
 182{
 183        if (cpuid > 0 && cpuid < tegra_num_cpu_domains)
 184                return tegra_cpu_domains[cpuid];
 185
 186        return -EINVAL;
 187}
 188
 189int __init tegra_powergate_init(void)
 190{
 191        switch (tegra_chip_id) {
 192        case TEGRA20:
 193                tegra_num_powerdomains = 7;
 194                break;
 195        case TEGRA30:
 196                tegra_num_powerdomains = 14;
 197                tegra_num_cpu_domains = 4;
 198                tegra_cpu_domains = tegra30_cpu_domains;
 199                break;
 200        case TEGRA114:
 201                tegra_num_powerdomains = 23;
 202                tegra_num_cpu_domains = 4;
 203                tegra_cpu_domains = tegra114_cpu_domains;
 204                break;
 205        default:
 206                /* Unknown Tegra variant. Disable powergating */
 207                tegra_num_powerdomains = 0;
 208                break;
 209        }
 210
 211        return 0;
 212}
 213
 214#ifdef CONFIG_DEBUG_FS
 215
 216static const char * const *powergate_name;
 217
 218static const char * const powergate_name_t20[] = {
 219        [TEGRA_POWERGATE_CPU]   = "cpu",
 220        [TEGRA_POWERGATE_3D]    = "3d",
 221        [TEGRA_POWERGATE_VENC]  = "venc",
 222        [TEGRA_POWERGATE_VDEC]  = "vdec",
 223        [TEGRA_POWERGATE_PCIE]  = "pcie",
 224        [TEGRA_POWERGATE_L2]    = "l2",
 225        [TEGRA_POWERGATE_MPE]   = "mpe",
 226};
 227
 228static const char * const powergate_name_t30[] = {
 229        [TEGRA_POWERGATE_CPU]   = "cpu0",
 230        [TEGRA_POWERGATE_3D]    = "3d0",
 231        [TEGRA_POWERGATE_VENC]  = "venc",
 232        [TEGRA_POWERGATE_VDEC]  = "vdec",
 233        [TEGRA_POWERGATE_PCIE]  = "pcie",
 234        [TEGRA_POWERGATE_L2]    = "l2",
 235        [TEGRA_POWERGATE_MPE]   = "mpe",
 236        [TEGRA_POWERGATE_HEG]   = "heg",
 237        [TEGRA_POWERGATE_SATA]  = "sata",
 238        [TEGRA_POWERGATE_CPU1]  = "cpu1",
 239        [TEGRA_POWERGATE_CPU2]  = "cpu2",
 240        [TEGRA_POWERGATE_CPU3]  = "cpu3",
 241        [TEGRA_POWERGATE_CELP]  = "celp",
 242        [TEGRA_POWERGATE_3D1]   = "3d1",
 243};
 244
 245static const char * const powergate_name_t114[] = {
 246        [TEGRA_POWERGATE_CPU]   = "cpu0",
 247        [TEGRA_POWERGATE_3D]    = "3d",
 248        [TEGRA_POWERGATE_VENC]  = "venc",
 249        [TEGRA_POWERGATE_VDEC]  = "vdec",
 250        [TEGRA_POWERGATE_MPE]   = "mpe",
 251        [TEGRA_POWERGATE_HEG]   = "heg",
 252        [TEGRA_POWERGATE_CPU1]  = "cpu1",
 253        [TEGRA_POWERGATE_CPU2]  = "cpu2",
 254        [TEGRA_POWERGATE_CPU3]  = "cpu3",
 255        [TEGRA_POWERGATE_CELP]  = "celp",
 256        [TEGRA_POWERGATE_CPU0]  = "cpu0",
 257        [TEGRA_POWERGATE_C0NC]  = "c0nc",
 258        [TEGRA_POWERGATE_C1NC]  = "c1nc",
 259        [TEGRA_POWERGATE_DIS]   = "dis",
 260        [TEGRA_POWERGATE_DISB]  = "disb",
 261        [TEGRA_POWERGATE_XUSBA] = "xusba",
 262        [TEGRA_POWERGATE_XUSBB] = "xusbb",
 263        [TEGRA_POWERGATE_XUSBC] = "xusbc",
 264};
 265
 266static int powergate_show(struct seq_file *s, void *data)
 267{
 268        int i;
 269
 270        seq_printf(s, " powergate powered\n");
 271        seq_printf(s, "------------------\n");
 272
 273        for (i = 0; i < tegra_num_powerdomains; i++) {
 274                if (!powergate_name[i])
 275                        continue;
 276
 277                seq_printf(s, " %9s %7s\n", powergate_name[i],
 278                        tegra_powergate_is_powered(i) ? "yes" : "no");
 279        }
 280
 281        return 0;
 282}
 283
 284static int powergate_open(struct inode *inode, struct file *file)
 285{
 286        return single_open(file, powergate_show, inode->i_private);
 287}
 288
 289static const struct file_operations powergate_fops = {
 290        .open           = powergate_open,
 291        .read           = seq_read,
 292        .llseek         = seq_lseek,
 293        .release        = single_release,
 294};
 295
 296int __init tegra_powergate_debugfs_init(void)
 297{
 298        struct dentry *d;
 299
 300        switch (tegra_chip_id) {
 301        case TEGRA20:
 302                powergate_name = powergate_name_t20;
 303                break;
 304        case TEGRA30:
 305                powergate_name = powergate_name_t30;
 306                break;
 307        case TEGRA114:
 308                powergate_name = powergate_name_t114;
 309                break;
 310        }
 311
 312        if (powergate_name) {
 313                d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
 314                        &powergate_fops);
 315                if (!d)
 316                        return -ENOMEM;
 317        }
 318
 319        return 0;
 320}
 321
 322#endif
 323