blob: d81241c15acb375a39e9a29a31305c4f73c2829d [file] [log] [blame]
/*
* OMAP and TPS6236x specific initialization
*
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
* Vishwanath BS
* Nishanth Menon
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/i2c/twl.h>
#include "pm.h"
#include "vc.h"
#include "mux.h"
/* Voltage limits supported */
#define MIN_VOLTAGE_TPS62360_62_UV 770000
#define MAX_VOLTAGE_TPS62360_62_UV 1400000
#define MIN_VOLTAGE_TPS62361_UV 500000
#define MAX_VOLTAGE_TPS62361_UV 1770000
#define NOM_VOLTAGE_TPS62361_UV 1430000
#define MAX_VOLTAGE_RAMP_TPS6236X_UV 32000
/*
* This is the voltage delta between 2 values in voltage register.
* when switching voltage V1 to V2, TPS62361 can ramp up or down
* initially with step sizes of 20mV with a last step of 10mV.
* In the case of TPS6236[0|2], it is a constant 10mV steps
* we choose the 10mV step for linearity when SR is configured.
*/
#define STEP_SIZE_TPS6236X 10000
/* I2C access parameters */
#define I2C_TPS6236X_SLAVE_ADDR 0x60
#define DEF_SET_REG(VSEL0, VSEL1) (((VSEL1) << 1| (VSEL0) << 0) & 0x3)
#define REG_TPS6236X_SET_0 0x00
#define REG_TPS6236X_SET_1 0x01
#define REG_TPS6236X_SET_2 0x02
#define REG_TPS6236X_SET_3 0x03
#define REG_TPS6236X_CTRL 0x04
#define REG_TPS6236X_TEMP 0x05
#define REG_TPS6236X_RAMP_CTRL 0x06
#define REG_TPS6236X_CHIP_ID0 0x08
#define REG_TPS6236X_CHIP_ID1 0x09
#define MODE_TPS6236X_AUTO_PFM_PWM 0x00
#define MODE_TPS6236X_FORCE_PWM BIT(7)
/* We use Auto PFM/PWM mode currently seems to have the best trade off */
#define VOLTAGE_PFM_MODE_VAL MODE_TPS6236X_AUTO_PFM_PWM
#define REG_TPS6236X_RAMP_CTRL_RMP_MASK (0x7 << 5)
#define REG_TPS6236X_RAMP_CTRL_EN_DISC BIT(2)
#define REG_TPS6236X_RAMP_CTRL_RAMP_PFM BIT(1)
#define REG_TPS6236X_CTRL_PD_EN BIT(7)
#define REG_TPS6236X_CTRL_PD_VSEL0 BIT(6)
#define REG_TPS6236X_CTRL_PD_VSEL1 BIT(5)
/* TWL usage */
#define TWL6030_REG_SYSEN_CFG_GRP 0xB3
#define TWL6030_BIT_APE_GRP BIT(0)
/* Voltage params of the attached device (all in uV) */
static unsigned long voltage_min;
static unsigned long voltage_max;
/* Which register do we use by default? */
static int __initdata default_reg = -1;;
/* Do we need to setup internal pullups? */
static int __initdata pd_vsel0 = -1;
static int __initdata pd_vsel1 = -1;
static int __init _bd_setup(char *name,int gpio_vsel, int *pull, int *pd_vsel)
{
int pull_dir;
int r;
if (gpio_vsel == -1) {
if (*pull != -1) {
*pd_vsel = (*pull == OMAP_PIN_OFF_OUTPUT_HIGH);
*pull = *pd_vsel;
} else {
*pull = 0;
}
return 0;
}
/* if we have a pull gpio, with bad dir, pull low */
if (*pull == -1 || (*pull != OMAP_PIN_OFF_OUTPUT_HIGH &&
*pull != OMAP_PIN_OFF_OUTPUT_LOW))
*pull = OMAP_PIN_OFF_OUTPUT_LOW;
r = omap_mux_init_gpio(gpio_vsel, *pull);
if (r) {
pr_err("%s: unable to mux gpio%d=%d\n", __func__,
gpio_vsel, r);
goto out;
}
pull_dir = (*pull == OMAP_PIN_OFF_OUTPUT_HIGH);
*pull = pull_dir;
r = gpio_request(gpio_vsel, name);
if (r) {
pr_err("%s: unable to req gpio%d=%d\n", __func__,
gpio_vsel, r);
goto out;
}
r = gpio_direction_output(gpio_vsel, pull_dir);
if (r) {
pr_err("%s: unable to pull[%d] gpio%d=%d\n", __func__,
gpio_vsel, pull_dir, r);
gpio_free(gpio_vsel);
goto out;
}
out:
return r;
}
/* Convert the ramp voltage to ramp value. */
static u8 __init tps6236x_ramp_value(unsigned long uv)
{
if (!uv)
return 0;
if (uv > MAX_VOLTAGE_RAMP_TPS6236X_UV) {
pr_err("%s: uv%ld greater than max %d\n", __func__,
uv, MAX_VOLTAGE_RAMP_TPS6236X_UV);
uv = MAX_VOLTAGE_RAMP_TPS6236X_UV;
}
return fls(MAX_VOLTAGE_RAMP_TPS6236X_UV / uv) - 1;
}
static unsigned long tps6236x_vsel_to_uv(const u8 vsel)
{
return (voltage_min +
(STEP_SIZE_TPS6236X * (vsel & ~VOLTAGE_PFM_MODE_VAL)));
}
static u8 tps6236x_uv_to_vsel(unsigned long uv)
{
if (!uv)
return 0;
/* Round off requests to limits */
if (uv > voltage_max) {
pr_err("%s:Request for overvoltage[%ld] than supported[%ld]\n",
__func__, uv, voltage_max);
uv = voltage_max;
}
if (uv < voltage_min) {
pr_err("%s:Request for undervoltage[%ld] than supported[%ld]\n",
__func__, uv, voltage_min);
uv = voltage_min;
}
return DIV_ROUND_UP(uv - voltage_min, STEP_SIZE_TPS6236X) |
VOLTAGE_PFM_MODE_VAL;
}
static struct omap_voltdm_pmic omap4_mpu_pmic = {
.slew_rate = 8000,
.step_size = STEP_SIZE_TPS6236X,
.on_volt = 1375000,
.onlp_volt = 1375000,
.ret_volt = 830000,
.off_volt = 0,
.volt_setup_time = 0,
.vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET,
.vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN,
.vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX,
.vp_vddmin = OMAP4_VP_MPU_VLIMITTO_VDDMIN,
.vp_vddmax = OMAP4_VP_MPU_VLIMITTO_VDDMAX,
.vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US,
.i2c_slave_addr = I2C_TPS6236X_SLAVE_ADDR,
.volt_reg_addr = REG_TPS6236X_SET_0,
.cmd_reg_addr = REG_TPS6236X_SET_0,
.i2c_high_speed = true,
.i2c_scll_low = 0x28,
.i2c_scll_high = 0x2C,
.i2c_hscll_low = 0x0B,
.i2c_hscll_high = 0x00,
.vsel_to_uv = tps6236x_vsel_to_uv,
.uv_to_vsel = tps6236x_uv_to_vsel,
};
/**
* omap4_twl_tps62361_enable() - Enable tps chip
*
* This function enables TPS chip by associating SYSEN signal
* to APE resource group of TWL6030.
*
* Returns 0 on sucess, error is returned if I2C read/write fails.
*/
static int __init omap4_twl_tps62361_enable(struct voltagedomain *voltdm)
{
int ret = 0;
u8 val;
/* Dont trust the bootloader. start with nominal, pm will set later */
val = voltdm->pmic->uv_to_vsel(NOM_VOLTAGE_TPS62361_UV);
ret = omap_vc_bypass_send_i2c_msg(voltdm, voltdm->pmic->i2c_slave_addr,
default_reg, val);
/* Setup Ramp */
val = tps6236x_ramp_value(voltdm->pmic->slew_rate) <<
__ffs(REG_TPS6236X_RAMP_CTRL_RMP_MASK);
val &= REG_TPS6236X_RAMP_CTRL_RMP_MASK;
/* We would like to ramp the voltage asap */
val |= REG_TPS6236X_RAMP_CTRL_RAMP_PFM;
ret = omap_vc_bypass_send_i2c_msg(voltdm, voltdm->pmic->i2c_slave_addr,
REG_TPS6236X_RAMP_CTRL, val);
if (ret)
goto out;
/* Setup the internal pulls to select if needed */
if (pd_vsel0 != -1 || pd_vsel1 != -1) {
val = REG_TPS6236X_CTRL_PD_EN;
val |= (pd_vsel0) ? 0 : REG_TPS6236X_CTRL_PD_VSEL0;
val |= (pd_vsel1) ? 0 : REG_TPS6236X_CTRL_PD_VSEL1;
ret = omap_vc_bypass_send_i2c_msg(voltdm,
voltdm->pmic->i2c_slave_addr,
REG_TPS6236X_CTRL, val);
if (ret)
goto out;
}
/* Enable thermal shutdown - 0 is enable :) */
ret = omap_vc_bypass_send_i2c_msg(voltdm,
voltdm->pmic->i2c_slave_addr,
REG_TPS6236X_TEMP, 0x0);
if (ret)
goto out;
/* if we have to work with TWL */
#ifdef CONFIG_TWL4030_CORE
ret = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &val,
TWL6030_REG_SYSEN_CFG_GRP);
if (ret)
goto out;
val |= TWL6030_BIT_APE_GRP;
ret = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val,
TWL6030_REG_SYSEN_CFG_GRP);
#endif
out:
if (ret)
pr_err("%s: Error enabling TPS(%d)\n", __func__, ret);
return ret;
}
static __initdata struct omap_revisions tps_omap_revs[] = {
{ OMAP446X_CLASS, OMAP_REVISION_MASK_REV },
{ 0, 0 },
};
static __initdata struct omap_pmic_map omap_tps_map[] = {
{
.name = "mpu",
.omap_revs = tps_omap_revs,
.pmic_data = &omap4_mpu_pmic,
.special_action = omap4_twl_tps62361_enable,
},
/* Terminator */
{ .name = NULL,.pmic_data = NULL},
};
int __init omap_tps6236x_init(void)
{
struct omap_pmic_map *map;
/* Without registers, I wont proceed */
if (default_reg == -1)
return -EINVAL;
map = omap_tps_map;
/* setup all the pmic's voltage addresses to the default one */
while (map->name) {
map->pmic_data->volt_reg_addr = default_reg;
map->pmic_data->cmd_reg_addr = default_reg;
map++;
}
return omap_pmic_register_data(omap_tps_map);
}
/**
* omap_tps6236x_board_setup() - provide the board config for TPS connect
* @use_62361: Do we use TPS62361 variant?
* @gpio_vsel0: If using GPIO to control VSEL0, provide gpio number, else -1
* @gpio_vsel1: If using GPIO to control VSEL1, provide gpio number, else -1
* @pull0: If using GPIO, provide mux mode OMAP_PIN_OFF_OUTPUT_[HIGH|LOW]
* else provide any internal pull required, -1 if unused.
* @pull1: If using GPIO, provide mux mode OMAP_PIN_OFF_OUTPUT_[HIGH|LOW]
* else provide any internal pull required, -1 if unused.
*
* TPS6236x variants of PMIC can be hooked in numerous combinations on to the
* board. Some platforms can choose to hardwire and save on a GPIO for other
* uses, while others may hook a single line for GPIO control and may ground
* the other line. support these configurations.
*
* WARNING: for platforms using GPIO, be careful to provide MUX setting
* considering OFF mode configuration as well.
*/
int __init omap_tps6236x_board_setup(bool use_62361, int gpio_vsel0,
int gpio_vsel1, int pull0, int pull1)
{
int r;
r = _bd_setup("tps6236x_vsel0", gpio_vsel0, &pull0, &pd_vsel0);
if (r)
goto out;
r = _bd_setup("tps6236x_vsel1", gpio_vsel1, &pull1, &pd_vsel1);
if (r) {
if (gpio_vsel0 != -1)
gpio_free(gpio_vsel0);
goto out;
}
default_reg = ((pull1 & 0x1) << 1) | (pull0 & 0x1);
if (use_62361) {
voltage_min = MIN_VOLTAGE_TPS62361_UV;
voltage_max = MAX_VOLTAGE_TPS62361_UV;
} else {
voltage_min = MIN_VOLTAGE_TPS62360_62_UV;
voltage_max = MAX_VOLTAGE_TPS62360_62_UV;
}
out:
return r;
}