/*
* drivers/soc/tegra/pmc.c
*
* Copyright (c) 2010 Google, Inc
*
* Author:
* Colin Cross <ccross@google.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "tegra-pmc: " fmt
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/clk/tegra.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/reset.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <soc/tegra/common.h>
#include <soc/tegra/fuse.h>
#include <soc/tegra/pmc.h>
#define PMC_CNTRL 0x0
#define PMC_CNTRL_SYSCLK_POLARITY (1 << 10) /* sys clk polarity */
#define PMC_CNTRL_SYSCLK_OE (1 << 11) /* system clock enable */
#define PMC_CNTRL_SIDE_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */
#define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */
#define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
#define PMC_CNTRL_INTR_POLARITY (1 << 17) /* inverts INTR polarity */
#define DPD_SAMPLE 0x020
#define DPD_SAMPLE_ENABLE (1 << 0)
#define DPD_SAMPLE_DISABLE (0 << 0)
#define PWRGATE_TOGGLE 0x30
#define PWRGATE_TOGGLE_START (1 << 8)
#define REMOVE_CLAMPING 0x34
#define PWRGATE_STATUS 0x38
#define PMC_SCRATCH0 0x50
#define PMC_SCRATCH0_MODE_RECOVERY (1 << 31)
#define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30)
#define PMC_SCRATCH0_MODE_RCM (1 << 1)
#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \
PMC_SCRATCH0_MODE_BOOTLOADER | \
PMC_SCRATCH0_MODE_RCM)
#define PMC_CPUPWRGOOD_TIMER 0xc8
#define PMC_CPUPWROFF_TIMER 0xcc
#define PMC_SCRATCH41 0x140
#define PMC_SENSOR_CTRL 0x1b0
#define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2)
#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1)
#define IO_DPD_REQ 0x1b8
#define IO_DPD_REQ_CODE_IDLE (0 << 30)
#define IO_DPD_REQ_CODE_OFF (1 << 30)
#define IO_DPD_REQ_CODE_ON (2 << 30)
#define IO_DPD_REQ_CODE_MASK (3 << 30)
#define IO_DPD_STATUS 0x1bc
#define IO_DPD2_REQ 0x1c0
#define IO_DPD2_STATUS 0x1c4
#define SEL_DPD_TIM 0x1c8
#define PMC_SCRATCH54 0x258
#define PMC_SCRATCH54_DATA_SHIFT 8
#define PMC_SCRATCH54_ADDR_SHIFT 0
#define PMC_SCRATCH55 0x25c
#define PMC_SCRATCH55_RESET_TEGRA (1 << 31)
#define PMC_SCRATCH55_CNTRL_ID_SHIFT 27
#define PMC_SCRATCH55_PINMUX_SHIFT 24
#define PMC_SCRATCH55_16BITOP (1 << 15)
#define PMC_SCRATCH55_CHECKSUM_SHIFT 16
#define PMC_SCRATCH55_I2CSLV1_SHIFT 0
#define GPU_RG_CNTRL 0x2d4
struct tegra_pmc_soc {
unsigned int num_powergates;
const char *const *powergates;
unsigned int num_cpu_powergates;
const u8 *cpu_powergates;
bool has_tsense_reset;
bool has_gpu_clamps;
};
/**
* struct tegra_pmc - NVIDIA Tegra PMC
* @base: pointer to I/O remapped register region
* @clk: pointer to pclk clock
* @rate: currently configured rate of pclk
* @suspend_mode: lowest suspend mode available
* @cpu_good_time: CPU power good time (in microseconds)
* @cpu_off_time: CPU power off time (in microsecends)
* @core_osc_time: core power good OSC time (in microseconds)
* @core_pmu_time: core power good PMU time (in microseconds)
* @core_off_time: core power off time (in microseconds)
* @corereq_high: core power request is active-high
* @sysclkreq_high: system clock request is active-high
* @combined_req: combined power request for CPU & core
* @cpu_pwr_good_en: CPU power good signal is enabled
* @lp0_vec_phys: physical base address of the LP0 warm boot code
* @lp0_vec_size: size of the LP0 warm boot code
* @powergates_lock: mutex for power gate register access
*/
struct tegra_pmc {
struct device *dev;
void __iomem *base;
struct clk *clk;
const struct tegra_pmc_soc *soc;
unsigned long rate;
enum tegra_suspend_mode suspend_mode;
u32 cpu_good_time;
u32 cpu_off_time;
u32 core_osc_time;
u32 core_pmu_time;
u32 core_off_time;
bool corereq_high;
bool sysclkreq_high;
bool combined_req;
bool cpu_pwr_good_en;
u32 lp0_vec_phys;
u32 lp0_vec_size;
struct mutex powergates_lock;
};
static struct tegra_pmc *pmc = &(struct tegra_pmc) {
.base = NULL,
.suspend_mode = TEGRA_SUSPEND_NONE,
};
static u32 tegra_pmc_readl(unsigned long offset)
{
return readl(pmc->base + offset);
}
static void tegra_pmc_writel(u32 value, unsigned long offset)
{
writel(value, pmc->base + offset);
}
/**
* tegra_powergate_set() - set the state of a partition
* @id: partition ID
* @new_state: new state of the partition
*/
static int tegra_powergate_set(int id, bool new_state)
{
bool status;
mutex_lock(&pmc->powergates_lock);
status = tegra_pmc_readl(PWRGATE_STATUS) & (1 << id);
if (status == new_state) {
mutex_unlock(&pmc->powergates_lock);
return 0;
}
tegra_pmc_writel(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
mutex_unlock(&pmc->powergates_lock);
return 0;
}
/**
* tegra_powergate_power_on() - power on partition
* @id: partition ID
*/
int tegra_powergate_power_on(int id)
{
if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
return -EINVAL;
return tegra_powergate_set(id, true);
}
/**
* tegra_powergate_power_off() - power off partition
* @id: partition ID
*/
int tegra_powergate_power_off(int id)
{
if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
return -EINVAL;
return tegra_powergate_set(id, false);
}
EXPORT_SYMBOL(tegra_powergate_power_off);
/**
* tegra_powergate_is_powered() - check if