Commit cd9a0b6b authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'next/pm' of git://git.linaro.org/people/arnd/arm-soc

* 'next/pm' of git://git.linaro.org/people/arnd/arm-soc: (66 commits)
  ARM: CSR: PM: use outer_resume to resume L2 cache
  ARM: CSR: call l2x0_of_init to init L2 cache of SiRFprimaII
  ARM: OMAP: voltage: voltage layer present, even when CONFIG_PM=n
  ARM: CSR: PM: add sleep entry for SiRFprimaII
  ARM: CSR: PM: save/restore irq status in suspend cycle
  ARM: CSR: PM: save/restore timer status in suspend cycle
  OMAP4: PM: TWL6030: add cmd register
  OMAP4: PM: TWL6030: fix ON/RET/OFF voltages
  OMAP4: PM: TWL6030: address 0V conversions
  OMAP4: PM: TWL6030: fix uv to voltage for >0x39
  OMAP4: PM: TWL6030: fix voltage conversion formula
  omap: voltage: add a stub header file for external/regulator use
  OMAP2+: VC: more registers are per-channel starting with OMAP5
  OMAP3+: voltage: update nominal voltage in voltdm_scale() not VC post-scale
  OMAP3+: voltage: rename omap_voltage_get_nom_volt -> voltdm_get_voltage
  OMAP3+: voltdm: final removal of omap_vdd_info
  OMAP3+: voltage: move/rename curr_volt from vdd_info into struct voltagedomain
  OMAP3+: voltage: rename scale and reset functions using voltdm_ prefix
  OMAP3+: VP: combine setting init voltage into common function
  OMAP3+: VP: remove unused omap_vp_get_curr_volt()
  ...

Fix up trivial conflict in arch/arm/mach-prima2/l2x0.c (code removal vs
edit)
parents ac5761a6 090ad104
......@@ -39,9 +39,12 @@ axi {
ranges = <0x40000000 0x40000000 0x80000000>;
l2-cache-controller@80040000 {
compatible = "arm,pl310-cache";
compatible = "arm,pl310-cache", "sirf,prima2-pl310-cache";
reg = <0x80040000 0x1000>;
interrupts = <59>;
arm,tag-latency = <1 1 1>;
arm,data-latency = <1 1 1>;
arm,filter-ranges = <0 0x40000000>;
};
intc: interrupt-controller@80020000 {
......@@ -67,6 +70,11 @@ reset-controller@88010000 {
compatible = "sirf,prima2-rstc";
reg = <0x88010000 0x1000>;
};
rsc-controller@88020000 {
compatible = "sirf,prima2-rsc";
reg = <0x88020000 0x1000>;
};
};
mem-iobg {
......@@ -274,7 +282,7 @@ tsc@b0110000 {
gpio: gpio-controller@b0120000 {
#gpio-cells = <2>;
#interrupt-cells = <2>;
compatible = "sirf,prima2-gpio";
compatible = "sirf,prima2-gpio-pinmux";
reg = <0xb0120000 0x10000>;
gpio-controller;
interrupt-controller;
......@@ -358,7 +366,7 @@ rom-interface@57a00000 {
};
rtc-iobg {
compatible = "sirf,prima2-rtciobg", "simple-bus";
compatible = "sirf,prima2-rtciobg", "sirf-prima2-rtciobg-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x80030000 0x10000>;
......
......@@ -89,14 +89,13 @@ obj-$(CONFIG_ARCH_OMAP4) += prcm.o cm2xxx_3xxx.o cminst44xx.o \
vp44xx_data.o
# OMAP voltage domains
ifeq ($(CONFIG_PM),y)
voltagedomain-common := voltage.o
obj-$(CONFIG_ARCH_OMAP2) += $(voltagedomain-common)
voltagedomain-common := voltage.o vc.o vp.o
obj-$(CONFIG_ARCH_OMAP2) += $(voltagedomain-common) \
voltagedomains2xxx_data.o
obj-$(CONFIG_ARCH_OMAP3) += $(voltagedomain-common) \
voltagedomains3xxx_data.o
obj-$(CONFIG_ARCH_OMAP4) += $(voltagedomain-common) \
voltagedomains44xx_data.o
endif
# OMAP powerdomain framework
powerdomain-common += powerdomain.o powerdomain-common.o
......
......@@ -37,6 +37,7 @@
#include "io.h"
#include <plat/omap-pm.h>
#include "voltage.h"
#include "powerdomain.h"
#include "clockdomain.h"
......@@ -341,18 +342,22 @@ void __init omap2_init_common_infrastructure(void)
u8 postsetup_state;
if (cpu_is_omap242x()) {
omap2xxx_voltagedomains_init();
omap242x_powerdomains_init();
omap242x_clockdomains_init();
omap2420_hwmod_init();
} else if (cpu_is_omap243x()) {
omap2xxx_voltagedomains_init();
omap243x_powerdomains_init();
omap243x_clockdomains_init();
omap2430_hwmod_init();
} else if (cpu_is_omap34xx()) {
omap3xxx_voltagedomains_init();
omap3xxx_powerdomains_init();
omap3xxx_clockdomains_init();
omap3xxx_hwmod_init();
} else if (cpu_is_omap44xx()) {
omap44xx_voltagedomains_init();
omap44xx_powerdomains_init();
omap44xx_clockdomains_init();
omap44xx_hwmod_init();
......
......@@ -2569,7 +2569,7 @@ static struct omap_hwmod omap34xx_sr1_hwmod = {
.name = "sr1_hwmod",
.class = &omap34xx_smartreflex_hwmod_class,
.main_clk = "sr1_fck",
.vdd_name = "mpu",
.vdd_name = "mpu_iva",
.prcm = {
.omap2 = {
.prcm_reg_id = 1,
......@@ -2588,7 +2588,7 @@ static struct omap_hwmod omap36xx_sr1_hwmod = {
.name = "sr1_hwmod",
.class = &omap36xx_smartreflex_hwmod_class,
.main_clk = "sr1_fck",
.vdd_name = "mpu",
.vdd_name = "mpu_iva",
.prcm = {
.omap2 = {
.prcm_reg_id = 1,
......
......@@ -42,8 +42,11 @@
#define OMAP4_SRI2C_SLAVE_ADDR 0x12
#define OMAP4_VDD_MPU_SR_VOLT_REG 0x55
#define OMAP4_VDD_MPU_SR_CMD_REG 0x56
#define OMAP4_VDD_IVA_SR_VOLT_REG 0x5B
#define OMAP4_VDD_IVA_SR_CMD_REG 0x5C
#define OMAP4_VDD_CORE_SR_VOLT_REG 0x61
#define OMAP4_VDD_CORE_SR_CMD_REG 0x62
#define OMAP4_VP_CONFIG_ERROROFFSET 0x00
#define OMAP4_VP_VSTEPMIN_VSTEPMIN 0x01
......@@ -95,6 +98,8 @@ static unsigned long twl6030_vsel_to_uv(const u8 vsel)
is_offset_valid = true;
}
if (!vsel)
return 0;
/*
* There is no specific formula for voltage to vsel
* conversion above 1.3V. There are special hardcoded
......@@ -106,9 +111,9 @@ static unsigned long twl6030_vsel_to_uv(const u8 vsel)
return 1350000;
if (smps_offset & 0x8)
return ((((vsel - 1) * 125) + 7000)) * 100;
return ((((vsel - 1) * 1266) + 70900)) * 10;
else
return ((((vsel - 1) * 125) + 6000)) * 100;
return ((((vsel - 1) * 1266) + 60770)) * 10;
}
static u8 twl6030_uv_to_vsel(unsigned long uv)
......@@ -127,6 +132,8 @@ static u8 twl6030_uv_to_vsel(unsigned long uv)
is_offset_valid = true;
}
if (!uv)
return 0x00;
/*
* There is no specific formula for voltage to vsel
* conversion above 1.3V. There are special hardcoded
......@@ -134,16 +141,21 @@ static u8 twl6030_uv_to_vsel(unsigned long uv)
* hardcoding only for 1.35 V which is used for 1GH OPP for
* OMAP4430.
*/
if (uv > twl6030_vsel_to_uv(0x39)) {
if (uv == 1350000)
return 0x3A;
pr_err("%s:OUT OF RANGE! non mapped vsel for %ld Vs max %ld\n",
__func__, uv, twl6030_vsel_to_uv(0x39));
return 0x3A;
}
if (smps_offset & 0x8)
return DIV_ROUND_UP(uv - 700000, 12500) + 1;
return DIV_ROUND_UP(uv - 709000, 12660) + 1;
else
return DIV_ROUND_UP(uv - 600000, 12500) + 1;
return DIV_ROUND_UP(uv - 607700, 12660) + 1;
}
static struct omap_volt_pmic_info omap3_mpu_volt_info = {
static struct omap_voltdm_pmic omap3_mpu_pmic = {
.slew_rate = 4000,
.step_size = 12500,
.on_volt = 1200000,
......@@ -158,12 +170,13 @@ static struct omap_volt_pmic_info omap3_mpu_volt_info = {
.vp_vddmax = OMAP3430_VP1_VLIMITTO_VDDMAX,
.vp_timeout_us = OMAP3_VP_VLIMITTO_TIMEOUT_US,
.i2c_slave_addr = OMAP3_SRI2C_SLAVE_ADDR,
.pmic_reg = OMAP3_VDD_MPU_SR_CONTROL_REG,
.volt_reg_addr = OMAP3_VDD_MPU_SR_CONTROL_REG,
.i2c_high_speed = true,
.vsel_to_uv = twl4030_vsel_to_uv,
.uv_to_vsel = twl4030_uv_to_vsel,
};
static struct omap_volt_pmic_info omap3_core_volt_info = {
static struct omap_voltdm_pmic omap3_core_pmic = {
.slew_rate = 4000,
.step_size = 12500,
.on_volt = 1200000,
......@@ -178,18 +191,19 @@ static struct omap_volt_pmic_info omap3_core_volt_info = {
.vp_vddmax = OMAP3430_VP2_VLIMITTO_VDDMAX,
.vp_timeout_us = OMAP3_VP_VLIMITTO_TIMEOUT_US,
.i2c_slave_addr = OMAP3_SRI2C_SLAVE_ADDR,
.pmic_reg = OMAP3_VDD_CORE_SR_CONTROL_REG,
.volt_reg_addr = OMAP3_VDD_CORE_SR_CONTROL_REG,
.i2c_high_speed = true,
.vsel_to_uv = twl4030_vsel_to_uv,
.uv_to_vsel = twl4030_uv_to_vsel,
};
static struct omap_volt_pmic_info omap4_mpu_volt_info = {
static struct omap_voltdm_pmic omap4_mpu_pmic = {
.slew_rate = 4000,
.step_size = 12500,
.on_volt = 1350000,
.onlp_volt = 1350000,
.ret_volt = 837500,
.off_volt = 600000,
.step_size = 12660,
.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,
......@@ -198,18 +212,20 @@ static struct omap_volt_pmic_info omap4_mpu_volt_info = {
.vp_vddmax = OMAP4_VP_MPU_VLIMITTO_VDDMAX,
.vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US,
.i2c_slave_addr = OMAP4_SRI2C_SLAVE_ADDR,
.pmic_reg = OMAP4_VDD_MPU_SR_VOLT_REG,
.volt_reg_addr = OMAP4_VDD_MPU_SR_VOLT_REG,
.cmd_reg_addr = OMAP4_VDD_MPU_SR_CMD_REG,
.i2c_high_speed = true,
.vsel_to_uv = twl6030_vsel_to_uv,
.uv_to_vsel = twl6030_uv_to_vsel,
};
static struct omap_volt_pmic_info omap4_iva_volt_info = {
static struct omap_voltdm_pmic omap4_iva_pmic = {
.slew_rate = 4000,
.step_size = 12500,
.on_volt = 1100000,
.onlp_volt = 1100000,
.ret_volt = 837500,
.off_volt = 600000,
.step_size = 12660,
.on_volt = 1188000,
.onlp_volt = 1188000,
.ret_volt = 830000,
.off_volt = 0,
.volt_setup_time = 0,
.vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET,
.vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN,
......@@ -218,18 +234,20 @@ static struct omap_volt_pmic_info omap4_iva_volt_info = {
.vp_vddmax = OMAP4_VP_IVA_VLIMITTO_VDDMAX,
.vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US,
.i2c_slave_addr = OMAP4_SRI2C_SLAVE_ADDR,
.pmic_reg = OMAP4_VDD_IVA_SR_VOLT_REG,
.volt_reg_addr = OMAP4_VDD_IVA_SR_VOLT_REG,
.cmd_reg_addr = OMAP4_VDD_IVA_SR_CMD_REG,
.i2c_high_speed = true,
.vsel_to_uv = twl6030_vsel_to_uv,
.uv_to_vsel = twl6030_uv_to_vsel,
};
static struct omap_volt_pmic_info omap4_core_volt_info = {
static struct omap_voltdm_pmic omap4_core_pmic = {
.slew_rate = 4000,
.step_size = 12500,
.on_volt = 1100000,
.onlp_volt = 1100000,
.ret_volt = 837500,
.off_volt = 600000,
.step_size = 12660,
.on_volt = 1200000,
.onlp_volt = 1200000,
.ret_volt = 830000,
.off_volt = 0,
.volt_setup_time = 0,
.vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET,
.vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN,
......@@ -238,7 +256,8 @@ static struct omap_volt_pmic_info omap4_core_volt_info = {
.vp_vddmax = OMAP4_VP_CORE_VLIMITTO_VDDMAX,
.vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US,
.i2c_slave_addr = OMAP4_SRI2C_SLAVE_ADDR,
.pmic_reg = OMAP4_VDD_CORE_SR_VOLT_REG,
.volt_reg_addr = OMAP4_VDD_CORE_SR_VOLT_REG,
.cmd_reg_addr = OMAP4_VDD_CORE_SR_CMD_REG,
.vsel_to_uv = twl6030_vsel_to_uv,
.uv_to_vsel = twl6030_uv_to_vsel,
};
......@@ -250,14 +269,14 @@ int __init omap4_twl_init(void)
if (!cpu_is_omap44xx())
return -ENODEV;
voltdm = omap_voltage_domain_lookup("mpu");
omap_voltage_register_pmic(voltdm, &omap4_mpu_volt_info);
voltdm = voltdm_lookup("mpu");
omap_voltage_register_pmic(voltdm, &omap4_mpu_pmic);
voltdm = omap_voltage_domain_lookup("iva");
omap_voltage_register_pmic(voltdm, &omap4_iva_volt_info);
voltdm = voltdm_lookup("iva");
omap_voltage_register_pmic(voltdm, &omap4_iva_pmic);
voltdm = omap_voltage_domain_lookup("core");
omap_voltage_register_pmic(voltdm, &omap4_core_volt_info);
voltdm = voltdm_lookup("core");
omap_voltage_register_pmic(voltdm, &omap4_core_pmic);
return 0;
}
......@@ -270,10 +289,10 @@ int __init omap3_twl_init(void)
return -ENODEV;
if (cpu_is_omap3630()) {
omap3_mpu_volt_info.vp_vddmin = OMAP3630_VP1_VLIMITTO_VDDMIN;
omap3_mpu_volt_info.vp_vddmax = OMAP3630_VP1_VLIMITTO_VDDMAX;
omap3_core_volt_info.vp_vddmin = OMAP3630_VP2_VLIMITTO_VDDMIN;
omap3_core_volt_info.vp_vddmax = OMAP3630_VP2_VLIMITTO_VDDMAX;
omap3_mpu_pmic.vp_vddmin = OMAP3630_VP1_VLIMITTO_VDDMIN;
omap3_mpu_pmic.vp_vddmax = OMAP3630_VP1_VLIMITTO_VDDMAX;
omap3_core_pmic.vp_vddmin = OMAP3630_VP2_VLIMITTO_VDDMIN;
omap3_core_pmic.vp_vddmax = OMAP3630_VP2_VLIMITTO_VDDMAX;
}
/*
......@@ -288,11 +307,11 @@ int __init omap3_twl_init(void)
if (!twl_sr_enable_autoinit)
omap3_twl_set_sr_bit(true);
voltdm = omap_voltage_domain_lookup("mpu");
omap_voltage_register_pmic(voltdm, &omap3_mpu_volt_info);
voltdm = voltdm_lookup("mpu_iva");
omap_voltage_register_pmic(voltdm, &omap3_mpu_pmic);
voltdm = omap_voltage_domain_lookup("core");
omap_voltage_register_pmic(voltdm, &omap3_core_volt_info);
voltdm = voltdm_lookup("core");
omap_voltage_register_pmic(voltdm, &omap3_core_pmic);
return 0;
}
......
......@@ -181,7 +181,7 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name,
goto exit;
}
voltdm = omap_voltage_domain_lookup(vdd_name);
voltdm = voltdm_lookup(vdd_name);
if (IS_ERR(voltdm)) {
pr_err("%s: unable to get vdd pointer for vdd_%s\n",
__func__, vdd_name);
......@@ -211,7 +211,7 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name,
goto exit;
}
omap_voltage_scale_vdd(voltdm, bootup_volt);
voltdm_scale(voltdm, bootup_volt);
return 0;
exit:
......@@ -224,7 +224,7 @@ static void __init omap3_init_voltages(void)
if (!cpu_is_omap34xx())
return;
omap2_set_init_voltage("mpu", "dpll1_ck", mpu_dev);
omap2_set_init_voltage("mpu_iva", "dpll1_ck", mpu_dev);
omap2_set_init_voltage("core", "l3_ick", l3_dev);
}
......
......@@ -77,6 +77,7 @@ static struct powerdomain *_pwrdm_lookup(const char *name)
static int _pwrdm_register(struct powerdomain *pwrdm)
{
int i;
struct voltagedomain *voltdm;
if (!pwrdm || !pwrdm->name)
return -EINVAL;
......@@ -91,6 +92,16 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
if (_pwrdm_lookup(pwrdm->name))
return -EEXIST;
voltdm = voltdm_lookup(pwrdm->voltdm.name);
if (!voltdm) {
pr_err("powerdomain: %s: voltagedomain %s does not exist\n",
pwrdm->name, pwrdm->voltdm.name);
return -EINVAL;
}
pwrdm->voltdm.ptr = voltdm;
INIT_LIST_HEAD(&pwrdm->voltdm_node);
voltdm_add_pwrdm(voltdm, pwrdm);
list_add(&pwrdm->node, &pwrdm_list);
/* Initialize the powerdomain's state counter */
......@@ -426,6 +437,18 @@ int pwrdm_for_each_clkdm(struct powerdomain *pwrdm,
return ret;
}
/**
* pwrdm_get_voltdm - return a ptr to the voltdm that this pwrdm resides in
* @pwrdm: struct powerdomain *
*
* Return a pointer to the struct voltageomain that the specified powerdomain
* @pwrdm exists in.
*/
struct voltagedomain *pwrdm_get_voltdm(struct powerdomain *pwrdm)
{
return pwrdm->voltdm.ptr;
}
/**
* pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain
* @pwrdm: struct powerdomain *
......
......@@ -24,6 +24,8 @@
#include <plat/cpu.h>
#include "voltage.h"
/* Powerdomain basic power states */
#define PWRDM_POWER_OFF 0x0
#define PWRDM_POWER_RET 0x1
......@@ -78,6 +80,7 @@ struct powerdomain;
/**
* struct powerdomain - OMAP powerdomain
* @name: Powerdomain name
* @voltdm: voltagedomain containing this powerdomain
* @prcm_offs: the address offset from CM_BASE/PRM_BASE
* @prcm_partition: (OMAP4 only) the PRCM partition ID containing @prcm_offs
* @pwrsts: Possible powerdomain power states
......@@ -88,6 +91,7 @@ struct powerdomain;
* @pwrsts_mem_on: Possible memory bank pwrstates when pwrdm in ON
* @pwrdm_clkdms: Clockdomains in this powerdomain
* @node: list_head linking all powerdomains
* @voltdm_node: list_head linking all powerdomains in a voltagedomain
* @state:
* @state_counter:
* @timer:
......@@ -97,6 +101,10 @@ struct powerdomain;
*/
struct powerdomain {
const char *name;
union {
const char *name;
struct voltagedomain *ptr;
} voltdm;
const s16 prcm_offs;
const u8 pwrsts;
const u8 pwrsts_logic_ret;
......@@ -107,6 +115,7 @@ struct powerdomain {
const u8 prcm_partition;
struct clockdomain *pwrdm_clkdms[PWRDM_MAX_CLKDMS];
struct list_head node;
struct list_head voltdm_node;
int state;
unsigned state_counter[PWRDM_MAX_PWRSTS];
unsigned ret_logic_off_counter;
......@@ -176,6 +185,7 @@ int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm);
int pwrdm_for_each_clkdm(struct powerdomain *pwrdm,
int (*fn)(struct powerdomain *pwrdm,
struct clockdomain *clkdm));
struct voltagedomain *pwrdm_get_voltdm(struct powerdomain *pwrdm);
int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm);
......
/*
* OMAP2 and OMAP3 powerdomain control
*
* Copyright (C) 2009-2010 Texas Instruments, Inc.
* Copyright (C) 2009-2011 Texas Instruments, Inc.
* Copyright (C) 2007-2009 Nokia Corporation
*
* Derived from mach-omap2/powerdomain.c written by Paul Walmsley
......
......@@ -54,10 +54,12 @@ struct powerdomain gfx_omap2_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
.voltdm = { .name = "core" },
};
struct powerdomain wkup_omap2_pwrdm = {
.name = "wkup_pwrdm",
.prcm_offs = WKUP_MOD,
.pwrsts = PWRSTS_ON,
.voltdm = { .name = "wakeup" },
};
......@@ -37,6 +37,7 @@ static struct powerdomain dsp_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON,
},
.voltdm = { .name = "core" },
};
static struct powerdomain mpu_24xx_pwrdm = {
......@@ -51,6 +52,7 @@ static struct powerdomain mpu_24xx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON,
},
.voltdm = { .name = "core" },
};
static struct powerdomain core_24xx_pwrdm = {
......@@ -68,6 +70,7 @@ static struct powerdomain core_24xx_pwrdm = {
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
[2] = PWRSTS_OFF_RET_ON, /* MEM3ONSTATE */
},
.voltdm = { .name = "core" },
};
......@@ -89,6 +92,7 @@ static struct powerdomain mdm_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
.voltdm = { .name = "core" },
};
/*
......
......@@ -51,6 +51,7 @@ static struct powerdomain iva2_pwrdm = {
[2] = PWRSTS_OFF_ON,
[3] = PWRSTS_ON,
},
.voltdm = { .name = "mpu_iva" },
};
static struct powerdomain mpu_3xxx_pwrdm = {
......@@ -66,6 +67,7 @@ static struct powerdomain mpu_3xxx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_OFF_ON,
},
.voltdm = { .name = "mpu_iva" },
};
/*
......@@ -92,6 +94,7 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
},
.voltdm = { .name = "core" },
};
static struct powerdomain core_3xxx_es3_1_pwrdm = {
......@@ -113,6 +116,7 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
},
.voltdm = { .name = "core" },
};
static struct powerdomain dss_pwrdm = {
......@@ -127,6 +131,7 @@ static struct powerdomain dss_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
.voltdm = { .name = "core" },
};
/*
......@@ -147,6 +152,7 @@ static struct powerdomain sgx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
.voltdm = { .name = "core" },
};
static struct powerdomain cam_pwrdm = {
......@@ -161,6 +167,7 @@ static struct powerdomain cam_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
.voltdm = { .name = "core" },
};
static struct powerdomain per_pwrdm = {
......@@ -175,11 +182,13 @@ static struct powerdomain per_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
.voltdm = { .name = "core" },
};
static struct powerdomain emu_pwrdm = {
.name = "emu_pwrdm",
.prcm_offs = OMAP3430_EMU_MOD,
.voltdm = { .name = "core" },
};
static struct powerdomain neon_pwrdm = {
......@@ -187,6 +196,7 @@ static struct powerdomain neon_pwrdm = {
.prcm_offs = OMAP3430_NEON_MOD,
.pwrsts = PWRSTS_OFF_RET_ON,
.pwrsts_logic_ret = PWRSTS_RET,
.voltdm = { .name = "mpu_iva" },
};
static struct powerdomain usbhost_pwrdm = {
......@@ -208,31 +218,37 @@ static struct powerdomain usbhost_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
.voltdm = { .name = "core" },
};
static struct powerdomain dpll1_pwrdm = {
.name = "dpll1_pwrdm",
.prcm_offs = MPU_MOD,
.voltdm = { .name = "mpu_iva" },
};
static struct powerdomain dpll2_pwrdm = {
.name = "dpll2_pwrdm",
.prcm_offs = OMAP3430_IVA2_MOD,
.voltdm = { .name = "mpu_iva" },
};
static struct powerdomain dpll3_pwrdm = {
.name = "dpll3_pwrdm",
.prcm_offs = PLL_MOD,
.voltdm = { .name = "core" },
};
static struct powerdomain dpll4_pwrdm = {
.name = "dpll4_pwrdm",
.prcm_offs = PLL_MOD,
.voltdm = { .name = "core" },
};
static struct powerdomain dpll5_pwrdm = {
.name = "dpll5_pwrdm",
.prcm_offs = PLL_MOD,
.voltdm = { .name = "core" },
};
/* As powerdomains are added or removed above, this list must also be changed */
......
......@@ -33,6 +33,7 @@
/* core_44xx_pwrdm: CORE power domain */
static struct powerdomain core_44xx_pwrdm = {
.name = "core_pwrdm",
.voltdm = { .name = "core" },
.prcm_offs = OMAP4430_PRM_CORE_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_RET_ON,
......@@ -58,6 +59,7 @@ static struct powerdomain core_44xx_pwrdm = {
/* gfx_44xx_pwrdm: 3D accelerator power domain */
static struct powerdomain gfx_44xx_pwrdm = {
.name = "gfx_pwrdm",
.voltdm = { .name = "core" },
.prcm_offs = OMAP4430_PRM_GFX_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_OFF_ON,
......@@ -74,6 +76,7 @@ static struct powerdomain gfx_44xx_pwrdm = {
/* abe_44xx_pwrdm: Audio back end power domain */
static struct powerdomain abe_44xx_pwrdm = {
.name = "abe_pwrdm",
.voltdm = { .name = "iva" },
.prcm_offs = OMAP4430_PRM_ABE_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_OFF_RET_ON,
......@@ -93,6 +96,7 @@ static struct powerdomain abe_44xx_pwrdm = {
/* dss_44xx_pwrdm: Display subsystem power domain */
static struct powerdomain dss_44xx_pwrdm = {
.name = "dss_pwrdm",
.voltdm = { .name = "core" },
.prcm_offs = OMAP4430_PRM_DSS_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_OFF_RET_ON,
......@@ -110,6 +114,7 @@ static struct powerdomain dss_44xx_pwrdm = {
/* tesla_44xx_pwrdm: Tesla processor power domain */
static struct powerdomain tesla_44xx_pwrdm = {
.name = "tesla_pwrdm",
.voltdm = { .name = "iva" },
.prcm_offs = OMAP4430_PRM_TESLA_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_OFF_RET_ON,
......@@ -131,6 +136,7 @@ static struct powerdomain tesla_44xx_pwrdm = {
/* wkup_44xx_pwrdm: Wake-up power domain */
static struct powerdomain wkup_44xx_pwrdm = {
.name = "wkup_pwrdm",
.voltdm = { .name = "wakeup" },
.prcm_offs = OMAP4430_PRM_WKUP_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_ON,
......@@ -146,6 +152,7 @@ static struct powerdomain wkup_44xx_pwrdm = {
/* cpu0_44xx_pwrdm: MPU0 processor and Neon coprocessor power domain */
static struct powerdomain cpu0_44xx_pwrdm = {
.name = "cpu0_pwrdm",
.voltdm = { .name = "mpu" },
.prcm_offs = OMAP4430_PRCM_MPU_CPU0_INST,
.prcm_partition = OMAP4430_PRCM_MPU_PARTITION,
.pwrsts = PWRSTS_OFF_RET_ON,
......@@ -162,6 +169,7 @@ static struct powerdomain cpu0_44xx_pwrdm = {
/* cpu1_44xx_pwrdm: MPU1 processor and Neon coprocessor power domain */
static struct powerdomain cpu1_44xx_pwrdm = {
.name = "cpu1_pwrdm",
.voltdm = { .name = "mpu" },
.prcm_offs = OMAP4430_PRCM_MPU_CPU1_INST,
.prcm_partition = OMAP4430_PRCM_MPU_PARTITION,
.pwrsts = PWRSTS_OFF_RET_ON,
......@@ -178,6 +186,7 @@ static struct powerdomain cpu1_44xx_pwrdm = {
/* emu_44xx_pwrdm: Emulation power domain */
static struct powerdomain emu_44xx_pwrdm = {
.name = "emu_pwrdm",
.voltdm = { .name = "wakeup" },
.prcm_offs = OMAP4430_PRM_EMU_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_OFF_ON,
......@@ -193,6 +202,7 @@ static struct powerdomain emu_44xx_pwrdm = {
/* mpu_44xx_pwrdm: Modena processor and the Neon coprocessor power domain */
static struct powerdomain mpu_44xx_pwrdm = {
.name = "mpu_pwrdm",
.voltdm = { .name = "mpu" },
.prcm_offs = OMAP4430_PRM_MPU_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_RET_ON,
......@@ -213,6 +223,7 @@ static struct powerdomain mpu_44xx_pwrdm = {
/* ivahd_44xx_pwrdm: IVA-HD power domain */
static struct powerdomain ivahd_44xx_pwrdm = {
.name = "ivahd_pwrdm",
.voltdm = { .name = "iva" },
.prcm_offs = OMAP4430_PRM_IVAHD_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_OFF_RET_ON,
......@@ -236,6 +247,7 @@ static struct powerdomain ivahd_44xx_pwrdm = {
/* cam_44xx_pwrdm: Camera subsystem power domain */
static struct powerdomain cam_44xx_pwrdm = {
.name = "cam_pwrdm",
.voltdm = { .name = "core" },
.prcm_offs = OMAP4430_PRM_CAM_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_OFF_ON,
......@@ -252,6 +264,7 @@ static struct powerdomain cam_44xx_pwrdm = {
/* l3init_44xx_pwrdm: L3 initators pheripherals power domain */
static struct powerdomain l3init_44xx_pwrdm = {
.name = "l3init_pwrdm",
.voltdm = { .name = "core" },
.prcm_offs = OMAP4430_PRM_L3INIT_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_RET_ON,
......@@ -269,6 +282,7 @@ static struct powerdomain l3init_44xx_pwrdm = {
/* l4per_44xx_pwrdm: Target peripherals power domain */
static struct powerdomain l4per_44xx_pwrdm = {
.name = "l4per_pwrdm",
.voltdm = { .name = "core" },
.prcm_offs = OMAP4430_PRM_L4PER_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_RET_ON,
......@@ -291,6 +305,7 @@ static struct powerdomain l4per_44xx_pwrdm = {
*/
static struct powerdomain always_on_core_44xx_pwrdm = {
.name = "always_on_core_pwrdm",
.voltdm = { .name = "core" },
.prcm_offs = OMAP4430_PRM_ALWAYS_ON_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_ON,
......@@ -299,6 +314,7 @@ static struct powerdomain always_on_core_44xx_pwrdm = {
/* cefuse_44xx_pwrdm: Customer efuse controller power domain */
static struct powerdomain cefuse_44xx_pwrdm = {
.name = "cefuse_pwrdm",
.voltdm = { .name = "core" },
.prcm_offs = OMAP4430_PRM_CEFUSE_INST,
.prcm_partition = OMAP4430_PRM_PARTITION,
.pwrsts = PWRSTS_OFF_ON,
......
......@@ -20,6 +20,8 @@
#include <plat/cpu.h>
#include <plat/prcm.h>
#include "vp.h"
#include "prm2xxx_3xxx.h"
#include "cm2xxx_3xxx.h"
#include "prm-regbits-24xx.h"
......@@ -156,3 +158,57 @@ int omap2_prm_deassert_hardreset(s16 prm_mod, u8 rst_shift, u8 st_shift)
return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
}
/* PRM VP */
/*
* struct omap3_vp - OMAP3 VP register access description.
* @tranxdone_status: VP_TRANXDONE_ST bitmask in PRM_IRQSTATUS_MPU reg
*/
struct omap3_vp {
u32 tranxdone_status;
};
static struct omap3_vp omap3_vp[] = {
[OMAP3_VP_VDD_MPU_ID] = {
.tranxdone_status = OMAP3430_VP1_TRANXDONE_ST_MASK,
},
[OMAP3_VP_VDD_CORE_ID] = {
.tranxdone_status = OMAP3430_VP2_TRANXDONE_ST_MASK,
},
};
#define MAX_VP_ID ARRAY_SIZE(omap3_vp);
u32 omap3_prm_vp_check_txdone(u8 vp_id)
{
struct omap3_vp *vp = &omap3_vp[vp_id];
u32 irqstatus;
irqstatus = omap2_prm_read_mod_reg(OCP_MOD,
OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
return irqstatus & vp->tranxdone_status;
}
void omap3_prm_vp_clear_txdone(u8 vp_id)
{
struct omap3_vp *vp = &omap3_vp[vp_id];
omap2_prm_write_mod_reg(vp->tranxdone_status,
OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
}
u32 omap3_prm_vcvp_read(u8 offset)
{
return omap2_prm_read_mod_reg(OMAP3430_GR_MOD, offset);
}
void omap3_prm_vcvp_write(u32 val, u8 offset)
{
omap2_prm_write_mod_reg(val, OMAP3430_GR_MOD, offset);
}
u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset)
{
return omap2_prm_rmw_mod_reg_bits(mask, bits, OMAP3430_GR_MOD, offset);
}
......@@ -303,7 +303,19 @@ extern int omap2_prm_is_hardreset_asserted(s16 prm_mod, u8 shift);
extern int omap2_prm_assert_hardreset(s16 prm_mod, u8 shift);
extern int omap2_prm_deassert_hardreset(s16 prm_mod, u8 rst_shift, u8 st_shift);
/* OMAP3-specific VP functions */
u32 omap3_prm_vp_check_txdone(u8 vp_id);
void omap3_prm_vp_clear_txdone(u8 vp_id);
/*
* OMAP3 access functions for voltage controller (VC) and
* voltage proccessor (VP) in the PRM.
*/
extern u32 omap3_prm_vcvp_read(u8 offset);
extern void omap3_prm_vcvp_write(u32 val, u8 offset);
extern u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset);
#endif /* CONFIG_ARCH_OMAP4 */
#endif
/*
......
......@@ -21,8 +21,11 @@
#include <plat/cpu.h>
#include <plat/prcm.h>
#include "vp.h"
#include "prm44xx.h"
#include "prm-regbits-44xx.h"
#include "prcm44xx.h"
#include "prminst44xx.h"
/* PRM low-level functions */
......@@ -50,3 +53,71 @@ u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 reg)
return v;
}
/* PRM VP */
/*
* struct omap4_vp - OMAP4 VP register access description.
* @irqstatus_mpu: offset to IRQSTATUS_MPU register for VP
* @tranxdone_status: VP_TRANXDONE_ST bitmask in PRM_IRQSTATUS_MPU reg
*/
struct omap4_vp {
u32 irqstatus_mpu;
u32 tranxdone_status;
};
static struct omap4_vp omap4_vp[] = {
[OMAP4_VP_VDD_MPU_ID] = {
.irqstatus_mpu = OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET,
.tranxdone_status = OMAP4430_VP_MPU_TRANXDONE_ST_MASK,
},
[OMAP4_VP_VDD_IVA_ID] = {
.irqstatus_mpu = OMAP4_PRM_IRQSTATUS_MPU_OFFSET,
.tranxdone_status = OMAP4430_VP_IVA_TRANXDONE_ST_MASK,
},
[OMAP4_VP_VDD_CORE_ID] = {
.irqstatus_mpu = OMAP4_PRM_IRQSTATUS_MPU_OFFSET,
.tranxdone_status = OMAP4430_VP_CORE_TRANXDONE_ST_MASK,
},
};
u32 omap4_prm_vp_check_txdone(u8 vp_id)
{
struct omap4_vp *vp = &omap4_vp[vp_id];
u32 irqstatus;
irqstatus = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
OMAP4430_PRM_OCP_SOCKET_INST,
vp->irqstatus_mpu);
return irqstatus & vp->tranxdone_status;
}
void omap4_prm_vp_clear_txdone(u8 vp_id)
{
struct omap4_vp *vp = &omap4_vp[vp_id];
omap4_prminst_write_inst_reg(vp->tranxdone_status,
OMAP4430_PRM_PARTITION,
OMAP4430_PRM_OCP_SOCKET_INST,
vp->irqstatus_mpu);
};
u32 omap4_prm_vcvp_read(u8 offset)
{
return omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
OMAP4430_PRM_DEVICE_INST, offset);
}
void omap4_prm_vcvp_write(u32 val, u8 offset)
{
omap4_prminst_write_inst_reg(val, OMAP4430_PRM_PARTITION,
OMAP4430_PRM_DEVICE_INST, offset);
}
u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset)
{
return omap4_prminst_rmw_inst_reg_bits(mask, bits,
OMAP4430_PRM_PARTITION,
OMAP4430_PRM_DEVICE_INST,
offset);
}
......@@ -751,6 +751,18 @@ extern u32 omap4_prm_read_inst_reg(s16 inst, u16 idx);
extern void omap4_prm_write_inst_reg(u32 val, s16 inst, u16 idx);
extern u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
/* OMAP4-specific VP functions */
u32 omap4_prm_vp_check_txdone(u8 vp_id);
void omap4_prm_vp_clear_txdone(u8 vp_id);
/*
* OMAP4 access functions for voltage controller (VC) and
* voltage proccessor (VP) in the PRM.
*/
extern u32 omap4_prm_vcvp_read(u8 offset);
extern void omap4_prm_vcvp_write(u32 val, u8 offset);
extern u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset);
# endif
#endif
......@@ -15,7 +15,7 @@
static int sr_class3_enable(struct voltagedomain *voltdm)
{
unsigned long volt = omap_voltage_get_nom_volt(voltdm);
unsigned long volt = voltdm_get_voltage(voltdm);
if (!volt) {
pr_warning("%s: Curr voltage unknown. Cannot enable sr_%s\n",
......@@ -32,7 +32,7 @@ static int sr_class3_disable(struct voltagedomain *voltdm, int is_volt_reset)
omap_vp_disable(voltdm);
sr_disable(voltdm);
if (is_volt_reset)
omap_voltage_reset(voltdm);
voltdm_reset(voltdm);
return 0;
}
......
......@@ -62,6 +62,7 @@ static LIST_HEAD(sr_list);
static struct omap_sr_class_data *sr_class;
static struct omap_sr_pmic_data *sr_pmic_data;
static struct dentry *sr_dbg_dir;
static inline void sr_write_reg(struct omap_sr *sr, unsigned offset, u32 value)
{
......@@ -826,9 +827,10 @@ static int __init omap_sr_probe(struct platform_device *pdev)
struct omap_sr *sr_info = kzalloc(sizeof(struct omap_sr), GFP_KERNEL);
struct omap_sr_data *pdata = pdev->dev.platform_data;
struct resource *mem, *irq;
struct dentry *vdd_dbg_dir, *nvalue_dir;
struct dentry *nvalue_dir;
struct omap_volt_data *volt_data;
int i, ret = 0;
char *name;
if (!sr_info) {
dev_err(&pdev->dev, "%s: unable to allocate sr_info\n",
......@@ -899,18 +901,25 @@ static int __init omap_sr_probe(struct platform_device *pdev)
}
dev_info(&pdev->dev, "%s: SmartReflex driver initialized\n", __func__);
/*
* If the voltage domain debugfs directory is not created, do
* not try to create rest of the debugfs entries.
*/
vdd_dbg_dir = omap_voltage_get_dbgdir(sr_info->voltdm);
if (!vdd_dbg_dir) {
ret = -EINVAL;
if (!sr_dbg_dir) {
sr_dbg_dir = debugfs_create_dir("smartreflex", NULL);
if (!sr_dbg_dir) {
ret = PTR_ERR(sr_dbg_dir);
pr_err("%s:sr debugfs dir creation failed(%d)\n",
__func__, ret);
goto err_iounmap;
}
}
sr_info->dbg_dir = debugfs_create_dir("smartreflex", vdd_dbg_dir);
name = kasprintf(GFP_KERNEL, "sr_%s", sr_info->voltdm->name);
if (!name) {
dev_err(&pdev->dev, "%s: Unable to alloc debugfs name\n",
__func__);
ret = -ENOMEM;
goto err_iounmap;
}
sr_info->dbg_dir = debugfs_create_dir(name, sr_dbg_dir);
kfree(name);
if (IS_ERR(sr_info->dbg_dir)) {
dev_err(&pdev->dev, "%s: Unable to create debugfs directory\n",
__func__);
......
......@@ -102,7 +102,7 @@ static int sr_dev_init(struct omap_hwmod *oh, void *user)
sr_data->senn_mod = 0x1;
sr_data->senp_mod = 0x1;
sr_data->voltdm = omap_voltage_domain_lookup(oh->vdd_name);
sr_data->voltdm = voltdm_lookup(oh->vdd_name);
if (IS_ERR(sr_data->voltdm)) {
pr_err("%s: Unable to get voltage domain pointer for VDD %s\n",
__func__, oh->vdd_name);
......
/*
* OMAP Voltage Controller (VC) interface
*
* Copyright (C) 2011 Texas Instruments, Inc.
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <plat/cpu.h>
#include "voltage.h"
#include "vc.h"
#include "prm-regbits-34xx.h"
#include "prm-regbits-44xx.h"
#include "prm44xx.h"
/**
* struct omap_vc_channel_cfg - describe the cfg_channel bitfield
* @sa: bit for slave address
* @rav: bit for voltage configuration register
* @rac: bit for command configuration register
* @racen: enable bit for RAC
* @cmd: bit for command value set selection
*
* Channel configuration bits, common for OMAP3+
* OMAP3 register: PRM_VC_CH_CONF
* OMAP4 register: PRM_VC_CFG_CHANNEL
* OMAP5 register: PRM_VC_SMPS_<voltdm>_CONFIG
*/
struct omap_vc_channel_cfg {
u8 sa;
u8 rav;
u8 rac;
u8 racen;
u8 cmd;
};
static struct omap_vc_channel_cfg vc_default_channel_cfg = {
.sa = BIT(0),
.rav = BIT(1),
.rac = BIT(2),
.racen = BIT(3),
.cmd = BIT(4),
};
/*
* On OMAP3+, all VC channels have the above default bitfield
* configuration, except the OMAP4 MPU channel. This appears
* to be a freak accident as every other VC channel has the
* default configuration, thus creating a mutant channel config.
*/
static struct omap_vc_channel_cfg vc_mutant_channel_cfg = {
.sa = BIT(0),
.rav = BIT(2),
.rac = BIT(3),
.racen = BIT(4),
.cmd = BIT(1),
};
static struct omap_vc_channel_cfg *vc_cfg_bits;
#define CFG_CHANNEL_MASK 0x1f
/**
* omap_vc_config_channel - configure VC channel to PMIC mappings
* @voltdm: pointer to voltagdomain defining the desired VC channel
*
* Configures the VC channel to PMIC mappings for the following
* PMIC settings
* - i2c slave address (SA)
* - voltage configuration address (RAV)
* - command configuration address (RAC) and enable bit (RACEN)
* - command values for ON, ONLP, RET and OFF (CMD)
*
* This function currently only allows flexible configuration of the
* non-default channel. Starting with OMAP4, there are more than 2
* channels, with one defined as the default (on OMAP4, it's MPU.)
* Only the non-default channel can be configured.
*/
static int omap_vc_config_channel(struct voltagedomain *voltdm)
{
struct omap_vc_channel *vc = voltdm->vc;
/*
* For default channel, the only configurable bit is RACEN.
* All others must stay at zero (see function comment above.)
*/
if (vc->flags & OMAP_VC_CHANNEL_DEFAULT)
vc->cfg_channel &= vc_cfg_bits->racen;
voltdm->rmw(CFG_CHANNEL_MASK << vc->cfg_channel_sa_shift,
vc->cfg_channel << vc->cfg_channel_sa_shift,
vc->cfg_channel_reg);
return 0;
}
/* Voltage scale and accessory APIs */
int omap_vc_pre_scale(struct voltagedomain *voltdm,
unsigned long target_volt,
u8 *target_vsel, u8 *current_vsel)
{
struct omap_vc_channel *vc = voltdm->vc;
u32 vc_cmdval;
/* Check if sufficient pmic info is available for this vdd */
if (!voltdm->pmic) {
pr_err("%s: Insufficient pmic info to scale the vdd_%s\n",
__func__, voltdm->name);
return -EINVAL;
}
if (!voltdm->pmic->uv_to_vsel) {
pr_err("%s: PMIC function to convert voltage in uV to"
"vsel not registered. Hence unable to scale voltage"
"for vdd_%s\n", __func__, voltdm->name);
return -ENODATA;
}
if (!voltdm->read || !voltdm->write) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
__func__, voltdm->name);
return -EINVAL;
}
*target_vsel = voltdm->pmic->uv_to_vsel(target_volt);
*current_vsel = voltdm->pmic->uv_to_vsel(voltdm->nominal_volt);
/* Setting the ON voltage to the new target voltage */
vc_cmdval = voltdm->read(vc->cmdval_reg);
vc_cmdval &= ~vc->common->cmd_on_mask;
vc_cmdval |= (*target_vsel << vc->common->cmd_on_shift);
voltdm->write(vc_cmdval, vc->cmdval_reg);
omap_vp_update_errorgain(voltdm, target_volt);
return 0;
}
void omap_vc_post_scale(struct voltagedomain *voltdm,
unsigned long target_volt,
u8 target_vsel, u8 current_vsel)
{
u32 smps_steps = 0, smps_delay = 0;
smps_steps = abs(target_vsel - current_vsel);
/* SMPS slew rate / step size. 2us added as buffer. */
smps_delay = ((smps_steps * voltdm->pmic->step_size) /
voltdm->pmic->slew_rate) + 2;
udelay(smps_delay);
}
/* vc_bypass_scale - VC bypass method of voltage scaling */
int omap_vc_bypass_scale(struct voltagedomain *voltdm,
unsigned long target_volt)
{
struct omap_vc_channel *vc = voltdm->vc;
u32 loop_cnt = 0, retries_cnt = 0;
u32 vc_valid, vc_bypass_val_reg, vc_bypass_value;
u8 target_vsel, current_vsel;
int ret;
ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, &current_vsel);
if (ret)
return ret;
vc_valid = vc->common->valid;
vc_bypass_val_reg = vc->common->bypass_val_reg;
vc_bypass_value = (target_vsel << vc->common->data_shift) |
(vc->volt_reg_addr << vc->common->regaddr_shift) |
(vc->i2c_slave_addr << vc->common->slaveaddr_shift);
voltdm->write(vc_bypass_value, vc_bypass_val_reg);
voltdm->write(vc_bypass_value | vc_valid, vc_bypass_val_reg);
vc_bypass_value = voltdm->read(vc_bypass_val_reg);
/*
* Loop till the bypass command is acknowledged from the SMPS.
* NOTE: This is legacy code. The loop count and retry count needs
* to be revisited.
*/
while (!(vc_bypass_value & vc_valid)) {
loop_cnt++;
if (retries_cnt > 10) {
pr_warning("%s: Retry count exceeded\n", __func__);
return -ETIMEDOUT;
}
if (loop_cnt > 50) {
retries_cnt++;
loop_cnt = 0;
udelay(10);
}
vc_bypass_value = voltdm->read(vc_bypass_val_reg);
}
omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel);
return 0;
}
static void __init omap3_vfsm_init(struct voltagedomain *voltdm)
{
/*
* Voltage Manager FSM parameters init
* XXX This data should be passed in from the board file
*/
voltdm->write(OMAP3_CLKSETUP, OMAP3_PRM_CLKSETUP_OFFSET);
voltdm->write(OMAP3_VOLTOFFSET, OMAP3_PRM_VOLTOFFSET_OFFSET);
voltdm->write(OMAP3_VOLTSETUP2, OMAP3_PRM_VOLTSETUP2_OFFSET);
}
static void __init omap3_vc_init_channel(struct voltagedomain *voltdm)
{
static bool is_initialized;
if (is_initialized)
return;
omap3_vfsm_init(voltdm);
is_initialized = true;
}
/* OMAP4 specific voltage init functions */
static void __init omap4_vc_init_channel(struct voltagedomain *voltdm)
{
static bool is_initialized;
u32 vc_val;
if (is_initialized)
return;
/* XXX These are magic numbers and do not belong! */
vc_val = (0x60 << OMAP4430_SCLL_SHIFT | 0x26 << OMAP4430_SCLH_SHIFT);
voltdm->write(vc_val, OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET);
is_initialized = true;
}
/**
* omap_vc_i2c_init - initialize I2C interface to PMIC
* @voltdm: voltage domain containing VC data
*
* Use PMIC supplied seetings for I2C high-speed mode and
* master code (if set) and program the VC I2C configuration
* register.
*
* The VC I2C configuration is common to all VC channels,
* so this function only configures I2C for the first VC
* channel registers. All other VC channels will use the
* same configuration.
*/
static void __init omap_vc_i2c_init(struct voltagedomain *voltdm)
{
struct omap_vc_channel *vc = voltdm->vc;
static bool initialized;
static bool i2c_high_speed;
u8 mcode;
if (initialized) {
if (voltdm->pmic->i2c_high_speed != i2c_high_speed)
pr_warn("%s: I2C config for all channels must match.",
__func__);
return;
}
i2c_high_speed = voltdm->pmic->i2c_high_speed;
if (i2c_high_speed)
voltdm->rmw(vc->common->i2c_cfg_hsen_mask,
vc->common->i2c_cfg_hsen_mask,
vc->common->i2c_cfg_reg);
mcode = voltdm->pmic->i2c_mcode;
if (mcode)
voltdm->rmw(vc->common->i2c_mcode_mask,
mcode << __ffs(vc->common->i2c_mcode_mask),
vc->common->i2c_cfg_reg);
initialized = true;
}
void __init omap_vc_init_channel(struct voltagedomain *voltdm)
{
struct omap_vc_channel *vc = voltdm->vc;
u8 on_vsel, onlp_vsel, ret_vsel, off_vsel;
u32 val;
if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) {
pr_err("%s: PMIC info requried to configure vc for"
"vdd_%s not populated.Hence cannot initialize vc\n",
__func__, voltdm->name);
return;
}
if (!voltdm->read || !voltdm->write) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
__func__, voltdm->name);
return;
}
vc->cfg_channel = 0;
if (vc->flags & OMAP_VC_CHANNEL_CFG_MUTANT)
vc_cfg_bits = &vc_mutant_channel_cfg;
else
vc_cfg_bits = &vc_default_channel_cfg;
/* get PMIC/board specific settings */
vc->i2c_slave_addr = voltdm->pmic->i2c_slave_addr;
vc->volt_reg_addr = voltdm->pmic->volt_reg_addr;
vc->cmd_reg_addr = voltdm->pmic->cmd_reg_addr;
vc->setup_time = voltdm->pmic->volt_setup_time;
/* Configure the i2c slave address for this VC */
voltdm->rmw(vc->smps_sa_mask,
vc->i2c_slave_addr << __ffs(vc->smps_sa_mask),
vc->smps_sa_reg);
vc->cfg_channel |= vc_cfg_bits->sa;
/*
* Configure the PMIC register addresses.
*/
voltdm->rmw(vc->smps_volra_mask,
vc->volt_reg_addr << __ffs(vc->smps_volra_mask),
vc->smps_volra_reg);
vc->cfg_channel |= vc_cfg_bits->rav;
if (vc->cmd_reg_addr) {
voltdm->rmw(vc->smps_cmdra_mask,
vc->cmd_reg_addr << __ffs(vc->smps_cmdra_mask),
vc->smps_cmdra_reg);
vc->cfg_channel |= vc_cfg_bits->rac | vc_cfg_bits->racen;
}
/* Set up the on, inactive, retention and off voltage */
on_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->on_volt);
onlp_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->onlp_volt);
ret_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->ret_volt);
off_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->off_volt);
val = ((on_vsel << vc->common->cmd_on_shift) |
(onlp_vsel << vc->common->cmd_onlp_shift) |
(ret_vsel << vc->common->cmd_ret_shift) |
(off_vsel << vc->common->cmd_off_shift));
voltdm->write(val, vc->cmdval_reg);
vc->cfg_channel |= vc_cfg_bits->cmd;
/* Channel configuration */
omap_vc_config_channel(voltdm);
/* Configure the setup times */
voltdm->rmw(voltdm->vfsm->voltsetup_mask,
vc->setup_time << __ffs(voltdm->vfsm->voltsetup_mask),
voltdm->vfsm->voltsetup_reg);
omap_vc_i2c_init(voltdm);
if (cpu_is_omap34xx())
omap3_vc_init_channel(voltdm);
else if (cpu_is_omap44xx())
omap4_vc_init_channel(voltdm);
}
......@@ -19,12 +19,12 @@
#include <linux/kernel.h>
struct voltagedomain;
/**
* struct omap_vc_common_data - per-VC register/bitfield data
* struct omap_vc_common - per-VC register/bitfield data
* @cmd_on_mask: ON bitmask in PRM_VC_CMD_VAL* register
* @valid: VALID bitmask in PRM_VC_BYPASS_VAL register
* @smps_sa_reg: Offset of PRM_VC_SMPS_SA reg from PRM start
* @smps_volra_reg: Offset of PRM_VC_SMPS_VOL_RA reg from PRM start
* @bypass_val_reg: Offset of PRM_VC_BYPASS_VAL reg from PRM start
* @data_shift: DATA field shift in PRM_VC_BYPASS_VAL register
* @slaveaddr_shift: SLAVEADDR field shift in PRM_VC_BYPASS_VAL register
......@@ -33,15 +33,16 @@
* @cmd_onlp_shift: ONLP field shift in PRM_VC_CMD_VAL_* register
* @cmd_ret_shift: RET field shift in PRM_VC_CMD_VAL_* register
* @cmd_off_shift: OFF field shift in PRM_VC_CMD_VAL_* register
* @i2c_cfg_reg: I2C configuration register offset
* @i2c_cfg_hsen_mask: high-speed mode bit field mask in I2C config register
* @i2c_mcode_mask: MCODE field mask for I2C config register
*
* XXX One of cmd_on_mask and cmd_on_shift are not needed
* XXX VALID should probably be a shift, not a mask
*/
struct omap_vc_common_data {
struct omap_vc_common {
u32 cmd_on_mask;
u32 valid;
u8 smps_sa_reg;
u8 smps_volra_reg;
u8 bypass_val_reg;
u8 data_shift;
u8 slaveaddr_shift;
......@@ -50,34 +51,75 @@ struct omap_vc_common_data {
u8 cmd_onlp_shift;
u8 cmd_ret_shift;
u8 cmd_off_shift;
u8 i2c_cfg_reg;
u8 i2c_cfg_hsen_mask;
u8 i2c_mcode_mask;
};
/* omap_vc_channel.flags values */
#define OMAP_VC_CHANNEL_DEFAULT BIT(0)
#define OMAP_VC_CHANNEL_CFG_MUTANT BIT(1)
/**
* struct omap_vc_instance_data - VC per-instance data
* @vc_common: pointer to VC common data for this platform
* @smps_sa_mask: SA* bitmask in the PRM_VC_SMPS_SA register
* @smps_volra_mask: VOLRA* bitmask in the PRM_VC_VOL_RA register
* @smps_sa_shift: SA* field shift in the PRM_VC_SMPS_SA register
* @smps_volra_shift: VOLRA* field shift in the PRM_VC_VOL_RA register
* struct omap_vc_channel - VC per-instance data
* @i2c_slave_addr: I2C slave address of PMIC for this VC channel
* @volt_reg_addr: voltage configuration register address
* @cmd_reg_addr: command configuration register address
* @setup_time: setup time (in sys_clk cycles) of regulator for this channel
* @cfg_channel: current value of VC channel configuration register
* @i2c_high_speed: whether or not to use I2C high-speed mode
*
* XXX It is not necessary to have both a *_mask and a *_shift -
* remove one
* @common: pointer to VC common data for this platform
* @smps_sa_mask: i2c slave address bitmask in the PRM_VC_SMPS_SA register
* @smps_volra_mask: VOLRA* bitmask in the PRM_VC_VOL_RA register
* @smps_cmdra_mask: CMDRA* bitmask in the PRM_VC_CMD_RA register
* @cmdval_reg: register for on/ret/off voltage level values for this channel
* @smps_sa_reg: Offset of PRM_VC_SMPS_SA reg from PRM start
* @smps_volra_reg: Offset of PRM_VC_SMPS_VOL_RA reg from PRM start
* @smps_cmdra_reg: Offset of PRM_VC_SMPS_CMD_RA reg from PRM start
* @cfg_channel_reg: VC channel configuration register
* @cfg_channel_sa_shift: bit shift for slave address cfg_channel register
* @flags: VC channel-specific flags (optional)
*/
struct omap_vc_instance_data {
const struct omap_vc_common_data *vc_common;
struct omap_vc_channel {
/* channel state */
u16 i2c_slave_addr;
u16 volt_reg_addr;
u16 cmd_reg_addr;
u16 setup_time;
u8 cfg_channel;
bool i2c_high_speed;
/* register access data */
const struct omap_vc_common *common;
u32 smps_sa_mask;
u32 smps_volra_mask;
u32 smps_cmdra_mask;
u8 cmdval_reg;
u8 smps_sa_shift;
u8 smps_volra_shift;
u8 smps_sa_reg;
u8 smps_volra_reg;
u8 smps_cmdra_reg;
u8 cfg_channel_reg;
u8 cfg_channel_sa_shift;
u8 flags;
};
extern struct omap_vc_instance_data omap3_vc1_data;
extern struct omap_vc_instance_data omap3_vc2_data;
extern struct omap_vc_channel omap3_vc_mpu;
extern struct omap_vc_channel omap3_vc_core;
extern struct omap_vc_channel omap4_vc_mpu;
extern struct omap_vc_channel omap4_vc_iva;
extern struct omap_vc_channel omap4_vc_core;
extern struct omap_vc_instance_data omap4_vc_mpu_data;
extern struct omap_vc_instance_data omap4_vc_iva_data;
extern struct omap_vc_instance_data omap4_vc_core_data;
void omap_vc_init_channel(struct voltagedomain *voltdm);
int omap_vc_pre_scale(struct voltagedomain *voltdm,
unsigned long target_volt,
u8 *target_vsel, u8 *current_vsel);
void omap_vc_post_scale(struct voltagedomain *voltdm,
unsigned long target_volt,
u8 target_vsel, u8 current_vsel);
int omap_vc_bypass_scale(struct voltagedomain *voltdm,
unsigned long target_volt);
#endif
......@@ -29,9 +29,7 @@
* VC data common to 34xx/36xx chips
* XXX This stuff presumably belongs in the vc3xxx.c or vc.c file.
*/
static struct omap_vc_common_data omap3_vc_common = {
.smps_sa_reg = OMAP3_PRM_VC_SMPS_SA_OFFSET,
.smps_volra_reg = OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET,
static struct omap_vc_common omap3_vc_common = {
.bypass_val_reg = OMAP3_PRM_VC_BYPASS_VAL_OFFSET,
.data_shift = OMAP3430_DATA_SHIFT,
.slaveaddr_shift = OMAP3430_SLAVEADDR_SHIFT,
......@@ -42,22 +40,33 @@ static struct omap_vc_common_data omap3_vc_common = {
.cmd_onlp_shift = OMAP3430_VC_CMD_ONLP_SHIFT,
.cmd_ret_shift = OMAP3430_VC_CMD_RET_SHIFT,
.cmd_off_shift = OMAP3430_VC_CMD_OFF_SHIFT,
.i2c_cfg_hsen_mask = OMAP3430_HSEN_MASK,
.i2c_cfg_reg = OMAP3_PRM_VC_I2C_CFG_OFFSET,
.i2c_mcode_mask = OMAP3430_MCODE_MASK,
};
struct omap_vc_instance_data omap3_vc1_data = {
.vc_common = &omap3_vc_common,
struct omap_vc_channel omap3_vc_mpu = {
.common = &omap3_vc_common,
.smps_sa_reg = OMAP3_PRM_VC_SMPS_SA_OFFSET,
.smps_volra_reg = OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET,
.smps_cmdra_reg = OMAP3_PRM_VC_SMPS_CMD_RA_OFFSET,
.cfg_channel_reg = OMAP3_PRM_VC_CH_CONF_OFFSET,
.cmdval_reg = OMAP3_PRM_VC_CMD_VAL_0_OFFSET,
.smps_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA0_SHIFT,
.smps_sa_mask = OMAP3430_PRM_VC_SMPS_SA_SA0_MASK,
.smps_volra_shift = OMAP3430_VOLRA0_SHIFT,
.smps_volra_mask = OMAP3430_VOLRA0_MASK,
.smps_cmdra_mask = OMAP3430_CMDRA0_MASK,
.cfg_channel_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA0_SHIFT,
};
struct omap_vc_instance_data omap3_vc2_data = {
.vc_common = &omap3_vc_common,
struct omap_vc_channel omap3_vc_core = {
.common = &omap3_vc_common,
.smps_sa_reg = OMAP3_PRM_VC_SMPS_SA_OFFSET,
.smps_volra_reg = OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET,
.smps_cmdra_reg = OMAP3_PRM_VC_SMPS_CMD_RA_OFFSET,
.cfg_channel_reg = OMAP3_PRM_VC_CH_CONF_OFFSET,
.cmdval_reg = OMAP3_PRM_VC_CMD_VAL_1_OFFSET,
.smps_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA1_SHIFT,
.smps_sa_mask = OMAP3430_PRM_VC_SMPS_SA_SA1_MASK,
.smps_volra_shift = OMAP3430_VOLRA1_SHIFT,
.smps_volra_mask = OMAP3430_VOLRA1_MASK,
.smps_cmdra_mask = OMAP3430_CMDRA1_MASK,
.cfg_channel_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA1_SHIFT,
};
......@@ -30,9 +30,7 @@
* VC data common to 44xx chips
* XXX This stuff presumably belongs in the vc3xxx.c or vc.c file.
*/
static const struct omap_vc_common_data omap4_vc_common = {
.smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET,
.smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET,
static const struct omap_vc_common omap4_vc_common = {
.bypass_val_reg = OMAP4_PRM_VC_VAL_BYPASS_OFFSET,
.data_shift = OMAP4430_DATA_SHIFT,
.slaveaddr_shift = OMAP4430_SLAVEADDR_SHIFT,
......@@ -43,33 +41,49 @@ static const struct omap_vc_common_data omap4_vc_common = {
.cmd_onlp_shift = OMAP4430_ONLP_SHIFT,
.cmd_ret_shift = OMAP4430_RET_SHIFT,
.cmd_off_shift = OMAP4430_OFF_SHIFT,
.i2c_cfg_reg = OMAP4_PRM_VC_CFG_I2C_MODE_OFFSET,
.i2c_cfg_hsen_mask = OMAP4430_HSMODEEN_MASK,
.i2c_mcode_mask = OMAP4430_HSMCODE_MASK,
};
/* VC instance data for each controllable voltage line */
struct omap_vc_instance_data omap4_vc_mpu_data = {
.vc_common = &omap4_vc_common,
struct omap_vc_channel omap4_vc_mpu = {
.flags = OMAP_VC_CHANNEL_DEFAULT | OMAP_VC_CHANNEL_CFG_MUTANT,
.common = &omap4_vc_common,
.smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET,
.smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET,
.smps_cmdra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_CMD_OFFSET,
.cfg_channel_reg = OMAP4_PRM_VC_CFG_CHANNEL_OFFSET,
.cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_MPU_L_OFFSET,
.smps_sa_shift = OMAP4430_SA_VDD_MPU_L_PRM_VC_SMPS_SA_SHIFT,
.smps_sa_mask = OMAP4430_SA_VDD_MPU_L_PRM_VC_SMPS_SA_MASK,
.smps_volra_shift = OMAP4430_VOLRA_VDD_MPU_L_SHIFT,
.smps_volra_mask = OMAP4430_VOLRA_VDD_MPU_L_MASK,
.smps_cmdra_mask = OMAP4430_CMDRA_VDD_MPU_L_MASK,
.cfg_channel_sa_shift = OMAP4430_SA_VDD_MPU_L_SHIFT,
};
struct omap_vc_instance_data omap4_vc_iva_data = {
.vc_common = &omap4_vc_common,
struct omap_vc_channel omap4_vc_iva = {
.common = &omap4_vc_common,
.smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET,
.smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET,
.smps_cmdra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_CMD_OFFSET,
.cfg_channel_reg = OMAP4_PRM_VC_CFG_CHANNEL_OFFSET,
.cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_IVA_L_OFFSET,
.smps_sa_shift = OMAP4430_SA_VDD_IVA_L_PRM_VC_SMPS_SA_SHIFT,
.smps_sa_mask = OMAP4430_SA_VDD_IVA_L_PRM_VC_SMPS_SA_MASK,
.smps_volra_shift = OMAP4430_VOLRA_VDD_IVA_L_SHIFT,
.smps_volra_mask = OMAP4430_VOLRA_VDD_IVA_L_MASK,
.smps_cmdra_mask = OMAP4430_CMDRA_VDD_IVA_L_MASK,
.cfg_channel_sa_shift = OMAP4430_SA_VDD_IVA_L_SHIFT,
};
struct omap_vc_instance_data omap4_vc_core_data = {
.vc_common = &omap4_vc_common,
struct omap_vc_channel omap4_vc_core = {
.common = &omap4_vc_common,
.smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET,
.smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET,
.smps_cmdra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_CMD_OFFSET,
.cfg_channel_reg = OMAP4_PRM_VC_CFG_CHANNEL_OFFSET,
.cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_CORE_L_OFFSET,
.smps_sa_shift = OMAP4430_SA_VDD_CORE_L_0_6_SHIFT,
.smps_sa_mask = OMAP4430_SA_VDD_CORE_L_0_6_MASK,
.smps_volra_shift = OMAP4430_VOLRA_VDD_CORE_L_SHIFT,
.smps_volra_mask = OMAP4430_VOLRA_VDD_CORE_L_MASK,
.smps_cmdra_mask = OMAP4430_CMDRA_VDD_CORE_L_MASK,
.cfg_channel_sa_shift = OMAP4430_SA_VDD_CORE_L_SHIFT,
};
......@@ -21,10 +21,10 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <plat/common.h>
......@@ -36,839 +36,88 @@
#include "control.h"
#include "voltage.h"
#include "powerdomain.h"
#include "vc.h"
#include "vp.h"
#define VOLTAGE_DIR_SIZE 16
static struct omap_vdd_info **vdd_info;
/*
* Number of scalable voltage domains.
*/
static int nr_scalable_vdd;
/* XXX document */
static s16 prm_mod_offs;
static s16 prm_irqst_ocp_mod_offs;
static struct dentry *voltage_dir;
/* Init function pointers */
static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd,
unsigned long target_volt);
static u32 omap3_voltage_read_reg(u16 mod, u8 offset)
{
return omap2_prm_read_mod_reg(mod, offset);
}
static void omap3_voltage_write_reg(u32 val, u16 mod, u8 offset)
{
omap2_prm_write_mod_reg(val, mod, offset);
}
static u32 omap4_voltage_read_reg(u16 mod, u8 offset)
{
return omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
mod, offset);
}
static void omap4_voltage_write_reg(u32 val, u16 mod, u8 offset)
{
omap4_prminst_write_inst_reg(val, OMAP4430_PRM_PARTITION, mod, offset);
}
static int __init _config_common_vdd_data(struct omap_vdd_info *vdd)
{
char *sys_ck_name;
struct clk *sys_ck;
u32 sys_clk_speed, timeout_val, waittime;
/*
* XXX Clockfw should handle this, or this should be in a
* struct record
*/
if (cpu_is_omap24xx() || cpu_is_omap34xx())
sys_ck_name = "sys_ck";
else if (cpu_is_omap44xx())
sys_ck_name = "sys_clkin_ck";
else
return -EINVAL;
/*
* Sys clk rate is require to calculate vp timeout value and
* smpswaittimemin and smpswaittimemax.
*/
sys_ck = clk_get(NULL, sys_ck_name);
if (IS_ERR(sys_ck)) {
pr_warning("%s: Could not get the sys clk to calculate"
"various vdd_%s params\n", __func__, vdd->voltdm.name);
return -EINVAL;
}
sys_clk_speed = clk_get_rate(sys_ck);
clk_put(sys_ck);
/* Divide to avoid overflow */
sys_clk_speed /= 1000;
/* Generic voltage parameters */
vdd->volt_scale = vp_forceupdate_scale_voltage;
vdd->vp_enabled = false;
vdd->vp_rt_data.vpconfig_erroroffset =
(vdd->pmic_info->vp_erroroffset <<
vdd->vp_data->vp_common->vpconfig_erroroffset_shift);
timeout_val = (sys_clk_speed * vdd->pmic_info->vp_timeout_us) / 1000;
vdd->vp_rt_data.vlimitto_timeout = timeout_val;
vdd->vp_rt_data.vlimitto_vddmin = vdd->pmic_info->vp_vddmin;
vdd->vp_rt_data.vlimitto_vddmax = vdd->pmic_info->vp_vddmax;
waittime = ((vdd->pmic_info->step_size / vdd->pmic_info->slew_rate) *
sys_clk_speed) / 1000;
vdd->vp_rt_data.vstepmin_smpswaittimemin = waittime;
vdd->vp_rt_data.vstepmax_smpswaittimemax = waittime;
vdd->vp_rt_data.vstepmin_stepmin = vdd->pmic_info->vp_vstepmin;
vdd->vp_rt_data.vstepmax_stepmax = vdd->pmic_info->vp_vstepmax;
return 0;
}
/* Voltage debugfs support */
static int vp_volt_debug_get(void *data, u64 *val)
{
struct omap_vdd_info *vdd = (struct omap_vdd_info *) data;
u8 vsel;
if (!vdd) {
pr_warning("Wrong paramater passed\n");
return -EINVAL;
}
vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage);
if (!vdd->pmic_info->vsel_to_uv) {
pr_warning("PMIC function to convert vsel to voltage"
"in uV not registerd\n");
return -EINVAL;
}
*val = vdd->pmic_info->vsel_to_uv(vsel);
return 0;
}
static int nom_volt_debug_get(void *data, u64 *val)
{
struct omap_vdd_info *vdd = (struct omap_vdd_info *) data;
if (!vdd) {
pr_warning("Wrong paramater passed\n");
return -EINVAL;
}
*val = omap_voltage_get_nom_volt(&vdd->voltdm);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(vp_volt_debug_fops, vp_volt_debug_get, NULL, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(nom_volt_debug_fops, nom_volt_debug_get, NULL,
"%llu\n");
static void vp_latch_vsel(struct omap_vdd_info *vdd)
{
u32 vpconfig;
unsigned long uvdc;
char vsel;
uvdc = omap_voltage_get_nom_volt(&vdd->voltdm);
if (!uvdc) {
pr_warning("%s: unable to find current voltage for vdd_%s\n",
__func__, vdd->voltdm.name);
return;
}
if (!vdd->pmic_info || !vdd->pmic_info->uv_to_vsel) {
pr_warning("%s: PMIC function to convert voltage in uV to"
" vsel not registered\n", __func__);
return;
}
vsel = vdd->pmic_info->uv_to_vsel(uvdc);
vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig);
vpconfig &= ~(vdd->vp_data->vp_common->vpconfig_initvoltage_mask |
vdd->vp_data->vp_common->vpconfig_initvdd);
vpconfig |= vsel << vdd->vp_data->vp_common->vpconfig_initvoltage_shift;
vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/* Trigger initVDD value copy to voltage processor */
vdd->write_reg((vpconfig | vdd->vp_data->vp_common->vpconfig_initvdd),
prm_mod_offs, vdd->vp_data->vpconfig);
/* Clear initVDD copy trigger bit */
vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
}
/* Generic voltage init functions */
static void __init vp_init(struct omap_vdd_info *vdd)
{
u32 vp_val;
if (!vdd->read_reg || !vdd->write_reg) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
__func__, vdd->voltdm.name);
return;
}
vp_val = vdd->vp_rt_data.vpconfig_erroroffset |
(vdd->vp_rt_data.vpconfig_errorgain <<
vdd->vp_data->vp_common->vpconfig_errorgain_shift) |
vdd->vp_data->vp_common->vpconfig_timeouten;
vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vpconfig);
vp_val = ((vdd->vp_rt_data.vstepmin_smpswaittimemin <<
vdd->vp_data->vp_common->vstepmin_smpswaittimemin_shift) |
(vdd->vp_rt_data.vstepmin_stepmin <<
vdd->vp_data->vp_common->vstepmin_stepmin_shift));
vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vstepmin);
vp_val = ((vdd->vp_rt_data.vstepmax_smpswaittimemax <<
vdd->vp_data->vp_common->vstepmax_smpswaittimemax_shift) |
(vdd->vp_rt_data.vstepmax_stepmax <<
vdd->vp_data->vp_common->vstepmax_stepmax_shift));
vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vstepmax);
vp_val = ((vdd->vp_rt_data.vlimitto_vddmax <<
vdd->vp_data->vp_common->vlimitto_vddmax_shift) |
(vdd->vp_rt_data.vlimitto_vddmin <<
vdd->vp_data->vp_common->vlimitto_vddmin_shift) |
(vdd->vp_rt_data.vlimitto_timeout <<
vdd->vp_data->vp_common->vlimitto_timeout_shift));
vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vlimitto);
}
static void __init vdd_debugfs_init(struct omap_vdd_info *vdd)
{
char *name;
name = kzalloc(VOLTAGE_DIR_SIZE, GFP_KERNEL);
if (!name) {
pr_warning("%s: Unable to allocate memory for debugfs"
" directory name for vdd_%s",
__func__, vdd->voltdm.name);
return;
}
strcpy(name, "vdd_");
strcat(name, vdd->voltdm.name);
vdd->debug_dir = debugfs_create_dir(name, voltage_dir);
kfree(name);
if (IS_ERR(vdd->debug_dir)) {
pr_warning("%s: Unable to create debugfs directory for"
" vdd_%s\n", __func__, vdd->voltdm.name);
vdd->debug_dir = NULL;
return;
}
(void) debugfs_create_x16("vp_errorgain", S_IRUGO, vdd->debug_dir,
&(vdd->vp_rt_data.vpconfig_errorgain));
(void) debugfs_create_x16("vp_smpswaittimemin", S_IRUGO,
vdd->debug_dir,
&(vdd->vp_rt_data.vstepmin_smpswaittimemin));
(void) debugfs_create_x8("vp_stepmin", S_IRUGO, vdd->debug_dir,
&(vdd->vp_rt_data.vstepmin_stepmin));
(void) debugfs_create_x16("vp_smpswaittimemax", S_IRUGO,
vdd->debug_dir,
&(vdd->vp_rt_data.vstepmax_smpswaittimemax));
(void) debugfs_create_x8("vp_stepmax", S_IRUGO, vdd->debug_dir,
&(vdd->vp_rt_data.vstepmax_stepmax));
(void) debugfs_create_x8("vp_vddmax", S_IRUGO, vdd->debug_dir,
&(vdd->vp_rt_data.vlimitto_vddmax));
(void) debugfs_create_x8("vp_vddmin", S_IRUGO, vdd->debug_dir,
&(vdd->vp_rt_data.vlimitto_vddmin));
(void) debugfs_create_x16("vp_timeout", S_IRUGO, vdd->debug_dir,
&(vdd->vp_rt_data.vlimitto_timeout));
(void) debugfs_create_file("curr_vp_volt", S_IRUGO, vdd->debug_dir,
(void *) vdd, &vp_volt_debug_fops);
(void) debugfs_create_file("curr_nominal_volt", S_IRUGO,
vdd->debug_dir, (void *) vdd,
&nom_volt_debug_fops);
}
/* Voltage scale and accessory APIs */
static int _pre_volt_scale(struct omap_vdd_info *vdd,
unsigned long target_volt, u8 *target_vsel, u8 *current_vsel)
{
struct omap_volt_data *volt_data;
const struct omap_vc_common_data *vc_common;
const struct omap_vp_common_data *vp_common;
u32 vc_cmdval, vp_errgain_val;
vc_common = vdd->vc_data->vc_common;
vp_common = vdd->vp_data->vp_common;
/* Check if suffiecient pmic info is available for this vdd */
if (!vdd->pmic_info) {
pr_err("%s: Insufficient pmic info to scale the vdd_%s\n",
__func__, vdd->voltdm.name);
return -EINVAL;
}
if (!vdd->pmic_info->uv_to_vsel) {
pr_err("%s: PMIC function to convert voltage in uV to"
"vsel not registered. Hence unable to scale voltage"
"for vdd_%s\n", __func__, vdd->voltdm.name);
return -ENODATA;
}
if (!vdd->read_reg || !vdd->write_reg) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
__func__, vdd->voltdm.name);
return -EINVAL;
}
/* Get volt_data corresponding to target_volt */
volt_data = omap_voltage_get_voltdata(&vdd->voltdm, target_volt);
if (IS_ERR(volt_data))
volt_data = NULL;
*target_vsel = vdd->pmic_info->uv_to_vsel(target_volt);
*current_vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage);
/* Setting the ON voltage to the new target voltage */
vc_cmdval = vdd->read_reg(prm_mod_offs, vdd->vc_data->cmdval_reg);
vc_cmdval &= ~vc_common->cmd_on_mask;
vc_cmdval |= (*target_vsel << vc_common->cmd_on_shift);
vdd->write_reg(vc_cmdval, prm_mod_offs, vdd->vc_data->cmdval_reg);
/* Setting vp errorgain based on the voltage */
if (volt_data) {
vp_errgain_val = vdd->read_reg(prm_mod_offs,
vdd->vp_data->vpconfig);
vdd->vp_rt_data.vpconfig_errorgain = volt_data->vp_errgain;
vp_errgain_val &= ~vp_common->vpconfig_errorgain_mask;
vp_errgain_val |= vdd->vp_rt_data.vpconfig_errorgain <<
vp_common->vpconfig_errorgain_shift;
vdd->write_reg(vp_errgain_val, prm_mod_offs,
vdd->vp_data->vpconfig);
}
return 0;
}
static void _post_volt_scale(struct omap_vdd_info *vdd,
unsigned long target_volt, u8 target_vsel, u8 current_vsel)
{
u32 smps_steps = 0, smps_delay = 0;
smps_steps = abs(target_vsel - current_vsel);
/* SMPS slew rate / step size. 2us added as buffer. */
smps_delay = ((smps_steps * vdd->pmic_info->step_size) /
vdd->pmic_info->slew_rate) + 2;
udelay(smps_delay);
vdd->curr_volt = target_volt;
}
/* vc_bypass_scale_voltage - VC bypass method of voltage scaling */
static int vc_bypass_scale_voltage(struct omap_vdd_info *vdd,
unsigned long target_volt)
{
u32 loop_cnt = 0, retries_cnt = 0;
u32 vc_valid, vc_bypass_val_reg, vc_bypass_value;
u8 target_vsel, current_vsel;
int ret;
ret = _pre_volt_scale(vdd, target_volt, &target_vsel, &current_vsel);
if (ret)
return ret;
vc_valid = vdd->vc_data->vc_common->valid;
vc_bypass_val_reg = vdd->vc_data->vc_common->bypass_val_reg;
vc_bypass_value = (target_vsel << vdd->vc_data->vc_common->data_shift) |
(vdd->pmic_info->pmic_reg <<
vdd->vc_data->vc_common->regaddr_shift) |
(vdd->pmic_info->i2c_slave_addr <<
vdd->vc_data->vc_common->slaveaddr_shift);
vdd->write_reg(vc_bypass_value, prm_mod_offs, vc_bypass_val_reg);
vdd->write_reg(vc_bypass_value | vc_valid, prm_mod_offs,
vc_bypass_val_reg);
vc_bypass_value = vdd->read_reg(prm_mod_offs, vc_bypass_val_reg);
/*
* Loop till the bypass command is acknowledged from the SMPS.
* NOTE: This is legacy code. The loop count and retry count needs
* to be revisited.
*/
while (!(vc_bypass_value & vc_valid)) {
loop_cnt++;
if (retries_cnt > 10) {
pr_warning("%s: Retry count exceeded\n", __func__);
return -ETIMEDOUT;
}
if (loop_cnt > 50) {
retries_cnt++;
loop_cnt = 0;
udelay(10);
}
vc_bypass_value = vdd->read_reg(prm_mod_offs,
vc_bypass_val_reg);
}
_post_volt_scale(vdd, target_volt, target_vsel, current_vsel);
return 0;
}
/* VP force update method of voltage scaling */
static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd,
unsigned long target_volt)
{
u32 vpconfig;
u8 target_vsel, current_vsel, prm_irqst_reg;
int ret, timeout = 0;
ret = _pre_volt_scale(vdd, target_volt, &target_vsel, &current_vsel);
if (ret)
return ret;
prm_irqst_reg = vdd->vp_data->prm_irqst_data->prm_irqst_reg;
/*
* Clear all pending TransactionDone interrupt/status. Typical latency
* is <3us
*/
while (timeout++ < VP_TRANXDONE_TIMEOUT) {
vdd->write_reg(vdd->vp_data->prm_irqst_data->tranxdone_status,
prm_irqst_ocp_mod_offs, prm_irqst_reg);
if (!(vdd->read_reg(prm_irqst_ocp_mod_offs, prm_irqst_reg) &
vdd->vp_data->prm_irqst_data->tranxdone_status))
break;
udelay(1);
}
if (timeout >= VP_TRANXDONE_TIMEOUT) {
pr_warning("%s: vdd_%s TRANXDONE timeout exceeded."
"Voltage change aborted", __func__, vdd->voltdm.name);
return -ETIMEDOUT;
}
/* Configure for VP-Force Update */
vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig);
vpconfig &= ~(vdd->vp_data->vp_common->vpconfig_initvdd |
vdd->vp_data->vp_common->vpconfig_forceupdate |
vdd->vp_data->vp_common->vpconfig_initvoltage_mask);
vpconfig |= ((target_vsel <<
vdd->vp_data->vp_common->vpconfig_initvoltage_shift));
vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/* Trigger initVDD value copy to voltage processor */
vpconfig |= vdd->vp_data->vp_common->vpconfig_initvdd;
vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/* Force update of voltage */
vpconfig |= vdd->vp_data->vp_common->vpconfig_forceupdate;
vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/*
* Wait for TransactionDone. Typical latency is <200us.
* Depends on SMPSWAITTIMEMIN/MAX and voltage change
*/
timeout = 0;
omap_test_timeout((vdd->read_reg(prm_irqst_ocp_mod_offs, prm_irqst_reg) &
vdd->vp_data->prm_irqst_data->tranxdone_status),
VP_TRANXDONE_TIMEOUT, timeout);
if (timeout >= VP_TRANXDONE_TIMEOUT)
pr_err("%s: vdd_%s TRANXDONE timeout exceeded."
"TRANXDONE never got set after the voltage update\n",
__func__, vdd->voltdm.name);
_post_volt_scale(vdd, target_volt, target_vsel, current_vsel);
/*
* Disable TransactionDone interrupt , clear all status, clear
* control registers
*/
timeout = 0;
while (timeout++ < VP_TRANXDONE_TIMEOUT) {
vdd->write_reg(vdd->vp_data->prm_irqst_data->tranxdone_status,
prm_irqst_ocp_mod_offs, prm_irqst_reg);
if (!(vdd->read_reg(prm_irqst_ocp_mod_offs, prm_irqst_reg) &
vdd->vp_data->prm_irqst_data->tranxdone_status))
break;
udelay(1);
}
if (timeout >= VP_TRANXDONE_TIMEOUT)
pr_warning("%s: vdd_%s TRANXDONE timeout exceeded while trying"
"to clear the TRANXDONE status\n",
__func__, vdd->voltdm.name);
vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig);
/* Clear initVDD copy trigger bit */
vpconfig &= ~vdd->vp_data->vp_common->vpconfig_initvdd;
vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/* Clear force bit */
vpconfig &= ~vdd->vp_data->vp_common->vpconfig_forceupdate;
vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
return 0;
}
static void __init omap3_vfsm_init(struct omap_vdd_info *vdd)
{
/*
* Voltage Manager FSM parameters init
* XXX This data should be passed in from the board file
*/
vdd->write_reg(OMAP3_CLKSETUP, prm_mod_offs, OMAP3_PRM_CLKSETUP_OFFSET);
vdd->write_reg(OMAP3_VOLTOFFSET, prm_mod_offs,
OMAP3_PRM_VOLTOFFSET_OFFSET);
vdd->write_reg(OMAP3_VOLTSETUP2, prm_mod_offs,
OMAP3_PRM_VOLTSETUP2_OFFSET);
}
static void __init omap3_vc_init(struct omap_vdd_info *vdd)
{
static bool is_initialized;
u8 on_vsel, onlp_vsel, ret_vsel, off_vsel;
u32 vc_val;
if (is_initialized)
return;
/* Set up the on, inactive, retention and off voltage */
on_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->on_volt);
onlp_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->onlp_volt);
ret_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->ret_volt);
off_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->off_volt);
vc_val = ((on_vsel << vdd->vc_data->vc_common->cmd_on_shift) |
(onlp_vsel << vdd->vc_data->vc_common->cmd_onlp_shift) |
(ret_vsel << vdd->vc_data->vc_common->cmd_ret_shift) |
(off_vsel << vdd->vc_data->vc_common->cmd_off_shift));
vdd->write_reg(vc_val, prm_mod_offs, vdd->vc_data->cmdval_reg);
/*
* Generic VC parameters init
* XXX This data should be abstracted out
*/
vdd->write_reg(OMAP3430_CMD1_MASK | OMAP3430_RAV1_MASK, prm_mod_offs,
OMAP3_PRM_VC_CH_CONF_OFFSET);
vdd->write_reg(OMAP3430_MCODE_SHIFT | OMAP3430_HSEN_MASK, prm_mod_offs,
OMAP3_PRM_VC_I2C_CFG_OFFSET);
omap3_vfsm_init(vdd);
is_initialized = true;
}
/* OMAP4 specific voltage init functions */
static void __init omap4_vc_init(struct omap_vdd_info *vdd)
{
static bool is_initialized;
u32 vc_val;
if (is_initialized)
return;
/* TODO: Configure setup times and CMD_VAL values*/
/*
* Generic VC parameters init
* XXX This data should be abstracted out
*/
vc_val = (OMAP4430_RAV_VDD_MPU_L_MASK | OMAP4430_CMD_VDD_MPU_L_MASK |
OMAP4430_RAV_VDD_IVA_L_MASK | OMAP4430_CMD_VDD_IVA_L_MASK |
OMAP4430_RAV_VDD_CORE_L_MASK | OMAP4430_CMD_VDD_CORE_L_MASK);
vdd->write_reg(vc_val, prm_mod_offs, OMAP4_PRM_VC_CFG_CHANNEL_OFFSET);
/* XXX These are magic numbers and do not belong! */
vc_val = (0x60 << OMAP4430_SCLL_SHIFT | 0x26 << OMAP4430_SCLH_SHIFT);
vdd->write_reg(vc_val, prm_mod_offs, OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET);
is_initialized = true;
}
static void __init omap_vc_init(struct omap_vdd_info *vdd)
{
u32 vc_val;
if (!vdd->pmic_info || !vdd->pmic_info->uv_to_vsel) {
pr_err("%s: PMIC info requried to configure vc for"
"vdd_%s not populated.Hence cannot initialize vc\n",
__func__, vdd->voltdm.name);
return;
}
if (!vdd->read_reg || !vdd->write_reg) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
__func__, vdd->voltdm.name);
return;
}
/* Set up the SMPS_SA(i2c slave address in VC */
vc_val = vdd->read_reg(prm_mod_offs,
vdd->vc_data->vc_common->smps_sa_reg);
vc_val &= ~vdd->vc_data->smps_sa_mask;
vc_val |= vdd->pmic_info->i2c_slave_addr << vdd->vc_data->smps_sa_shift;
vdd->write_reg(vc_val, prm_mod_offs,
vdd->vc_data->vc_common->smps_sa_reg);
/* Setup the VOLRA(pmic reg addr) in VC */
vc_val = vdd->read_reg(prm_mod_offs,
vdd->vc_data->vc_common->smps_volra_reg);
vc_val &= ~vdd->vc_data->smps_volra_mask;
vc_val |= vdd->pmic_info->pmic_reg << vdd->vc_data->smps_volra_shift;
vdd->write_reg(vc_val, prm_mod_offs,
vdd->vc_data->vc_common->smps_volra_reg);
/* Configure the setup times */
vc_val = vdd->read_reg(prm_mod_offs, vdd->vfsm->voltsetup_reg);
vc_val &= ~vdd->vfsm->voltsetup_mask;
vc_val |= vdd->pmic_info->volt_setup_time <<
vdd->vfsm->voltsetup_shift;
vdd->write_reg(vc_val, prm_mod_offs, vdd->vfsm->voltsetup_reg);
if (cpu_is_omap34xx())
omap3_vc_init(vdd);
else if (cpu_is_omap44xx())
omap4_vc_init(vdd);
}
static int __init omap_vdd_data_configure(struct omap_vdd_info *vdd)
{
int ret = -EINVAL;
if (!vdd->pmic_info) {
pr_err("%s: PMIC info requried to configure vdd_%s not"
"populated.Hence cannot initialize vdd_%s\n",
__func__, vdd->voltdm.name, vdd->voltdm.name);
goto ovdc_out;
}
if (IS_ERR_VALUE(_config_common_vdd_data(vdd)))
goto ovdc_out;
if (cpu_is_omap34xx()) {
vdd->read_reg = omap3_voltage_read_reg;
vdd->write_reg = omap3_voltage_write_reg;
ret = 0;
} else if (cpu_is_omap44xx()) {
vdd->read_reg = omap4_voltage_read_reg;
vdd->write_reg = omap4_voltage_write_reg;
ret = 0;
}
ovdc_out:
return ret;
}
static LIST_HEAD(voltdm_list);
/* Public functions */
/**
* omap_voltage_get_nom_volt() - Gets the current non-auto-compensated voltage
* @voltdm: pointer to the VDD for which current voltage info is needed
*
* API to get the current non-auto-compensated voltage for a VDD.
* Returns 0 in case of error else returns the current voltage for the VDD.
*/
unsigned long omap_voltage_get_nom_volt(struct voltagedomain *voltdm)
{
struct omap_vdd_info *vdd;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return 0;
}
vdd = container_of(voltdm, struct omap_vdd_info, voltdm);
return vdd->curr_volt;
}
/**
* omap_vp_get_curr_volt() - API to get the current vp voltage.
* @voltdm: pointer to the VDD.
* voltdm_get_voltage() - Gets the current non-auto-compensated voltage
* @voltdm: pointer to the voltdm for which current voltage info is needed
*
* This API returns the current voltage for the specified voltage processor
* API to get the current non-auto-compensated voltage for a voltage domain.
* Returns 0 in case of error else returns the current voltage.
*/
unsigned long omap_vp_get_curr_volt(struct voltagedomain *voltdm)
unsigned long voltdm_get_voltage(struct voltagedomain *voltdm)
{
struct omap_vdd_info *vdd;
u8 curr_vsel;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return 0;
}
vdd = container_of(voltdm, struct omap_vdd_info, voltdm);
if (!vdd->read_reg) {
pr_err("%s: No read API for reading vdd_%s regs\n",
__func__, voltdm->name);
return 0;
}
curr_vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage);
if (!vdd->pmic_info || !vdd->pmic_info->vsel_to_uv) {
pr_warning("%s: PMIC function to convert vsel to voltage"
"in uV not registerd\n", __func__);
return 0;
}
return vdd->pmic_info->vsel_to_uv(curr_vsel);
}
/**
* omap_vp_enable() - API to enable a particular VP
* @voltdm: pointer to the VDD whose VP is to be enabled.
*
* This API enables a particular voltage processor. Needed by the smartreflex
* class drivers.
*/
void omap_vp_enable(struct voltagedomain *voltdm)
{
struct omap_vdd_info *vdd;
u32 vpconfig;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return;
}
vdd = container_of(voltdm, struct omap_vdd_info, voltdm);
if (!vdd->read_reg || !vdd->write_reg) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
__func__, voltdm->name);
return;
}
/* If VP is already enabled, do nothing. Return */
if (vdd->vp_enabled)
return;
vp_latch_vsel(vdd);
/* Enable VP */
vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig);
vpconfig |= vdd->vp_data->vp_common->vpconfig_vpenable;
vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
vdd->vp_enabled = true;
}
/**
* omap_vp_disable() - API to disable a particular VP
* @voltdm: pointer to the VDD whose VP is to be disabled.
*
* This API disables a particular voltage processor. Needed by the smartreflex
* class drivers.
*/
void omap_vp_disable(struct voltagedomain *voltdm)
{
struct omap_vdd_info *vdd;
u32 vpconfig;
int timeout;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return;
}
vdd = container_of(voltdm, struct omap_vdd_info, voltdm);
if (!vdd->read_reg || !vdd->write_reg) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
__func__, voltdm->name);
return;
}
/* If VP is already disabled, do nothing. Return */
if (!vdd->vp_enabled) {
pr_warning("%s: Trying to disable VP for vdd_%s when"
"it is already disabled\n", __func__, voltdm->name);
return;
}
/* Disable VP */
vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig);
vpconfig &= ~vdd->vp_data->vp_common->vpconfig_vpenable;
vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/*
* Wait for VP idle Typical latency is <2us. Maximum latency is ~100us
*/
omap_test_timeout((vdd->read_reg(prm_mod_offs, vdd->vp_data->vstatus)),
VP_IDLE_TIMEOUT, timeout);
if (timeout >= VP_IDLE_TIMEOUT)
pr_warning("%s: vdd_%s idle timedout\n",
__func__, voltdm->name);
vdd->vp_enabled = false;
return;
return voltdm->nominal_volt;
}
/**
* omap_voltage_scale_vdd() - API to scale voltage of a particular
* voltage domain.
* @voltdm: pointer to the VDD which is to be scaled.
* voltdm_scale() - API to scale voltage of a particular voltage domain.
* @voltdm: pointer to the voltage domain which is to be scaled.
* @target_volt: The target voltage of the voltage domain
*
* This API should be called by the kernel to do the voltage scaling
* for a particular voltage domain during dvfs or any other situation.
* for a particular voltage domain during DVFS.
*/
int omap_voltage_scale_vdd(struct voltagedomain *voltdm,
int voltdm_scale(struct voltagedomain *voltdm,
unsigned long target_volt)
{
struct omap_vdd_info *vdd;
int ret;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return -EINVAL;
}
vdd = container_of(voltdm, struct omap_vdd_info, voltdm);
if (!vdd->volt_scale) {
if (!voltdm->scale) {
pr_err("%s: No voltage scale API registered for vdd_%s\n",
__func__, voltdm->name);
return -ENODATA;
}
return vdd->volt_scale(vdd, target_volt);
ret = voltdm->scale(voltdm, target_volt);
if (!ret)
voltdm->nominal_volt = target_volt;
return ret;
}
/**
* omap_voltage_reset() - Resets the voltage of a particular voltage domain
* voltdm_reset() - Resets the voltage of a particular voltage domain
* to that of the current OPP.
* @voltdm: pointer to the VDD whose voltage is to be reset.
* @voltdm: pointer to the voltage domain whose voltage is to be reset.
*
* This API finds out the correct voltage the voltage domain is supposed
* to be at and resets the voltage to that level. Should be used especially
* while disabling any voltage compensation modules.
*/
void omap_voltage_reset(struct voltagedomain *voltdm)
void voltdm_reset(struct voltagedomain *voltdm)
{
unsigned long target_uvdc;
unsigned long target_volt;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return;
}
target_uvdc = omap_voltage_get_nom_volt(voltdm);
if (!target_uvdc) {
target_volt = voltdm_get_voltage(voltdm);
if (!target_volt) {
pr_err("%s: unable to find current voltage for vdd_%s\n",
__func__, voltdm->name);
return;
}
omap_voltage_scale_vdd(voltdm, target_uvdc);
voltdm_scale(voltdm, target_volt);
}
/**
......@@ -886,16 +135,12 @@ void omap_voltage_reset(struct voltagedomain *voltdm)
void omap_voltage_get_volttable(struct voltagedomain *voltdm,
struct omap_volt_data **volt_data)
{
struct omap_vdd_info *vdd;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return;
}
vdd = container_of(voltdm, struct omap_vdd_info, voltdm);
*volt_data = vdd->volt_data;
*volt_data = voltdm->volt_data;
}
/**
......@@ -916,7 +161,6 @@ void omap_voltage_get_volttable(struct voltagedomain *voltdm,
struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
unsigned long volt)
{
struct omap_vdd_info *vdd;
int i;
if (!voltdm || IS_ERR(voltdm)) {
......@@ -924,17 +168,15 @@ struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
return ERR_PTR(-EINVAL);
}
vdd = container_of(voltdm, struct omap_vdd_info, voltdm);
if (!vdd->volt_data) {
if (!voltdm->volt_data) {
pr_warning("%s: voltage table does not exist for vdd_%s\n",
__func__, voltdm->name);
return ERR_PTR(-ENODATA);
}
for (i = 0; vdd->volt_data[i].volt_nominal != 0; i++) {
if (vdd->volt_data[i].volt_nominal == volt)
return &vdd->volt_data[i];
for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) {
if (voltdm->volt_data[i].volt_nominal == volt)
return &voltdm->volt_data[i];
}
pr_notice("%s: Unable to match the current voltage with the voltage"
......@@ -947,53 +189,24 @@ struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
* omap_voltage_register_pmic() - API to register PMIC specific data
* @voltdm: pointer to the VDD for which the PMIC specific data is
* to be registered
* @pmic_info: the structure containing pmic info
* @pmic: the structure containing pmic info
*
* This API is to be called by the SOC/PMIC file to specify the
* pmic specific info as present in omap_volt_pmic_info structure.
* pmic specific info as present in omap_voltdm_pmic structure.
*/
int omap_voltage_register_pmic(struct voltagedomain *voltdm,
struct omap_volt_pmic_info *pmic_info)
struct omap_voltdm_pmic *pmic)
{
struct omap_vdd_info *vdd;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return -EINVAL;
}
vdd = container_of(voltdm, struct omap_vdd_info, voltdm);
vdd->pmic_info = pmic_info;
voltdm->pmic = pmic;
return 0;
}
/**
* omap_voltage_get_dbgdir() - API to get pointer to the debugfs directory
* corresponding to a voltage domain.
*
* @voltdm: pointer to the VDD whose debug directory is required.
*
* This API returns pointer to the debugfs directory corresponding
* to the voltage domain. Should be used by drivers requiring to
* add any debug entry for a particular voltage domain. Returns NULL
* in case of error.
*/
struct dentry *omap_voltage_get_dbgdir(struct voltagedomain *voltdm)
{
struct omap_vdd_info *vdd;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return NULL;
}
vdd = container_of(voltdm, struct omap_vdd_info, voltdm);
return vdd->debug_dir;
}
/**
* omap_change_voltscale_method() - API to change the voltage scaling method.
* @voltdm: pointer to the VDD whose voltage scaling method
......@@ -1007,21 +220,17 @@ struct dentry *omap_voltage_get_dbgdir(struct voltagedomain *voltdm)
void omap_change_voltscale_method(struct voltagedomain *voltdm,
int voltscale_method)
{
struct omap_vdd_info *vdd;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return;
}
vdd = container_of(voltdm, struct omap_vdd_info, voltdm);
switch (voltscale_method) {
case VOLTSCALE_VPFORCEUPDATE:
vdd->volt_scale = vp_forceupdate_scale_voltage;
voltdm->scale = omap_vp_forceupdate_scale;
return;
case VOLTSCALE_VCBYPASS:
vdd->volt_scale = vc_bypass_scale_voltage;
voltdm->scale = omap_vc_bypass_scale;
return;
default:
pr_warning("%s: Trying to change the method of voltage scaling"
......@@ -1030,77 +239,192 @@ void omap_change_voltscale_method(struct voltagedomain *voltdm,
}
/**
* omap_voltage_domain_lookup() - API to get the voltage domain pointer
* @name: Name of the voltage domain
* omap_voltage_late_init() - Init the various voltage parameters
*
* This API looks up in the global vdd_info struct for the
* existence of voltage domain <name>. If it exists, the API returns
* a pointer to the voltage domain structure corresponding to the
* VDD<name>. Else retuns error pointer.
* This API is to be called in the later stages of the
* system boot to init the voltage controller and
* voltage processors.
*/
struct voltagedomain *omap_voltage_domain_lookup(char *name)
int __init omap_voltage_late_init(void)
{
int i;
struct voltagedomain *voltdm;
if (!vdd_info) {
pr_err("%s: Voltage driver init not yet happened.Faulting!\n",
if (list_empty(&voltdm_list)) {
pr_err("%s: Voltage driver support not added\n",
__func__);
return ERR_PTR(-EINVAL);
return -EINVAL;
}
if (!name) {
pr_err("%s: No name to get the votage domain!\n", __func__);
return ERR_PTR(-EINVAL);
list_for_each_entry(voltdm, &voltdm_list, node) {
struct clk *sys_ck;
if (!voltdm->scalable)
continue;
sys_ck = clk_get(NULL, voltdm->sys_clk.name);
if (IS_ERR(sys_ck)) {
pr_warning("%s: Could not get sys clk.\n", __func__);
return -EINVAL;
}
voltdm->sys_clk.rate = clk_get_rate(sys_ck);
WARN_ON(!voltdm->sys_clk.rate);
clk_put(sys_ck);
for (i = 0; i < nr_scalable_vdd; i++) {
if (!(strcmp(name, vdd_info[i]->voltdm.name)))
return &vdd_info[i]->voltdm;
if (voltdm->vc) {
voltdm->scale = omap_vc_bypass_scale;
omap_vc_init_channel(voltdm);
}
return ERR_PTR(-EINVAL);
if (voltdm->vp) {
voltdm->scale = omap_vp_forceupdate_scale;
omap_vp_init(voltdm);
}
}
return 0;
}
static struct voltagedomain *_voltdm_lookup(const char *name)
{
struct voltagedomain *voltdm, *temp_voltdm;
voltdm = NULL;
list_for_each_entry(temp_voltdm, &voltdm_list, node) {
if (!strcmp(name, temp_voltdm->name)) {
voltdm = temp_voltdm;
break;
}
}
return voltdm;
}
/**
* omap_voltage_late_init() - Init the various voltage parameters
* voltdm_add_pwrdm - add a powerdomain to a voltagedomain
* @voltdm: struct voltagedomain * to add the powerdomain to
* @pwrdm: struct powerdomain * to associate with a voltagedomain
*
* This API is to be called in the later stages of the
* system boot to init the voltage controller and
* voltage processors.
* Associate the powerdomain @pwrdm with a voltagedomain @voltdm. This
* enables the use of voltdm_for_each_pwrdm(). Returns -EINVAL if
* presented with invalid pointers; -ENOMEM if memory could not be allocated;
* or 0 upon success.
*/
int __init omap_voltage_late_init(void)
int voltdm_add_pwrdm(struct voltagedomain *voltdm, struct powerdomain *pwrdm)
{
int i;
if (!voltdm || !pwrdm)
return -EINVAL;
if (!vdd_info) {
pr_err("%s: Voltage driver support not added\n",
__func__);
pr_debug("voltagedomain: associating powerdomain %s with voltagedomain "
"%s\n", pwrdm->name, voltdm->name);
list_add(&pwrdm->voltdm_node, &voltdm->pwrdm_list);
return 0;
}
/**
* voltdm_for_each_pwrdm - call function for each pwrdm in a voltdm
* @voltdm: struct voltagedomain * to iterate over
* @fn: callback function *
*
* Call the supplied function @fn for each powerdomain in the
* voltagedomain @voltdm. Returns -EINVAL if presented with invalid
* pointers; or passes along the last return value of the callback
* function, which should be 0 for success or anything else to
* indicate failure.
*/
int voltdm_for_each_pwrdm(struct voltagedomain *voltdm,
int (*fn)(struct voltagedomain *voltdm,
struct powerdomain *pwrdm))
{
struct powerdomain *pwrdm;
int ret = 0;
if (!fn)
return -EINVAL;
}
voltage_dir = debugfs_create_dir("voltage", NULL);
if (IS_ERR(voltage_dir))
pr_err("%s: Unable to create voltage debugfs main dir\n",
__func__);
for (i = 0; i < nr_scalable_vdd; i++) {
if (omap_vdd_data_configure(vdd_info[i]))
continue;
omap_vc_init(vdd_info[i]);
vp_init(vdd_info[i]);
vdd_debugfs_init(vdd_info[i]);
list_for_each_entry(pwrdm, &voltdm->pwrdm_list, voltdm_node)
ret = (*fn)(voltdm, pwrdm);
return ret;
}
/**
* voltdm_for_each - call function on each registered voltagedomain
* @fn: callback function *
*
* Call the supplied function @fn for each registered voltagedomain.
* The callback function @fn can return anything but 0 to bail out
* early from the iterator. Returns the last return value of the
* callback function, which should be 0 for success or anything else
* to indicate failure; or -EINVAL if the function pointer is null.
*/
int voltdm_for_each(int (*fn)(struct voltagedomain *voltdm, void *user),
void *user)
{
struct voltagedomain *temp_voltdm;
int ret = 0;
if (!fn)
return -EINVAL;
list_for_each_entry(temp_voltdm, &voltdm_list, node) {
ret = (*fn)(temp_voltdm, user);
if (ret)
break;
}
return 0;
return ret;
}
/* XXX document */
int __init omap_voltage_early_init(s16 prm_mod, s16 prm_irqst_ocp_mod,
struct omap_vdd_info *omap_vdd_array[],
u8 omap_vdd_count)
static int _voltdm_register(struct voltagedomain *voltdm)
{
prm_mod_offs = prm_mod;
prm_irqst_ocp_mod_offs = prm_irqst_ocp_mod;
vdd_info = omap_vdd_array;
nr_scalable_vdd = omap_vdd_count;
if (!voltdm || !voltdm->name)
return -EINVAL;
INIT_LIST_HEAD(&voltdm->pwrdm_list);
list_add(&voltdm->node, &voltdm_list);
pr_debug("voltagedomain: registered %s\n", voltdm->name);
return 0;
}
/**
* voltdm_lookup - look up a voltagedomain by name, return a pointer
* @name: name of voltagedomain
*
* Find a registered voltagedomain by its name @name. Returns a pointer
* to the struct voltagedomain if found, or NULL otherwise.
*/
struct voltagedomain *voltdm_lookup(const char *name)
{
struct voltagedomain *voltdm ;
if (!name)
return NULL;
voltdm = _voltdm_lookup(name);
return voltdm;
}
/**
* voltdm_init - set up the voltagedomain layer
* @voltdm_list: array of struct voltagedomain pointers to register
*
* Loop through the array of voltagedomains @voltdm_list, registering all
* that are available on the current CPU. If voltdm_list is supplied
* and not null, all of the referenced voltagedomains will be
* registered. No return value.
*/
void voltdm_init(struct voltagedomain **voltdms)
{
struct voltagedomain **v;
if (voltdms) {
for (v = voltdms; *v; v++)
_voltdm_register(*v);
}
}
......@@ -19,6 +19,8 @@
#include "vc.h"
#include "vp.h"
struct powerdomain;
/* XXX document */
#define VOLTSCALE_VPFORCEUPDATE 1
#define VOLTSCALE_VCBYPASS 2
......@@ -32,29 +34,60 @@
#define OMAP3_VOLTSETUP2 0xff
/**
* struct omap_vfsm_instance_data - per-voltage manager FSM register/bitfield
* struct omap_vfsm_instance - per-voltage manager FSM register/bitfield
* data
* @voltsetup_mask: SETUP_TIME* bitmask in the PRM_VOLTSETUP* register
* @voltsetup_reg: register offset of PRM_VOLTSETUP from PRM base
* @voltsetup_shift: SETUP_TIME* field shift in the PRM_VOLTSETUP* register
*
* XXX What about VOLTOFFSET/VOLTCTRL?
* XXX It is not necessary to have both a _mask and a _shift for the same
* bitfield - remove one!
*/
struct omap_vfsm_instance_data {
struct omap_vfsm_instance {
u32 voltsetup_mask;
u8 voltsetup_reg;
u8 voltsetup_shift;
};
/**
* struct voltagedomain - omap voltage domain global structure.
* @name: Name of the voltage domain which can be used as a unique
* identifier.
* @name: Name of the voltage domain which can be used as a unique identifier.
* @scalable: Whether or not this voltage domain is scalable
* @node: list_head linking all voltage domains
* @pwrdm_list: list_head linking all powerdomains in this voltagedomain
* @vc: pointer to VC channel associated with this voltagedomain
* @vp: pointer to VP associated with this voltagedomain
* @read: read a VC/VP register
* @write: write a VC/VP register
* @read: read-modify-write a VC/VP register
* @sys_clk: system clock name/frequency, used for various timing calculations
* @scale: function used to scale the voltage of the voltagedomain
* @nominal_volt: current nominal voltage for this voltage domain
* @volt_data: voltage table having the distinct voltages supported
* by the domain and other associated per voltage data.
*/
struct voltagedomain {
char *name;
bool scalable;
struct list_head node;
struct list_head pwrdm_list;
struct omap_vc_channel *vc;
const struct omap_vfsm_instance *vfsm;
struct omap_vp_instance *vp;
struct omap_voltdm_pmic *pmic;
/* VC/VP register access functions: SoC specific */
u32 (*read) (u8 offset);
void (*write) (u32 val, u8 offset);
u32 (*rmw)(u32 mask, u32 bits, u8 offset);
union {
const char *name;
u32 rate;
} sys_clk;
int (*scale) (struct voltagedomain *voltdm,
unsigned long target_volt);
u32 nominal_volt;
struct omap_volt_data *volt_data;
};
/**
......@@ -77,13 +110,18 @@ struct omap_volt_data {
};
/**
* struct omap_volt_pmic_info - PMIC specific data required by voltage driver.
* struct omap_voltdm_pmic - PMIC specific data required by voltage driver.
* @slew_rate: PMIC slew rate (in uv/us)
* @step_size: PMIC voltage step size (in uv)
* @i2c_slave_addr: I2C slave address of PMIC
* @volt_reg_addr: voltage configuration register address
* @cmd_reg_addr: command (on, on-LP, ret, off) configuration register address
* @i2c_high_speed: whether VC uses I2C high-speed mode to PMIC
* @i2c_mcode: master code value for I2C high-speed preamble transmission
* @vsel_to_uv: PMIC API to convert vsel value to actual voltage in uV.
* @uv_to_vsel: PMIC API to convert voltage in uV to vsel value.
*/
struct omap_volt_pmic_info {
struct omap_voltdm_pmic {
int slew_rate;
int step_size;
u32 on_volt;
......@@ -91,94 +129,44 @@ struct omap_volt_pmic_info {
u32 ret_volt;
u32 off_volt;
u16 volt_setup_time;
u16 i2c_slave_addr;
u16 volt_reg_addr;
u16 cmd_reg_addr;
u8 vp_erroroffset;
u8 vp_vstepmin;
u8 vp_vstepmax;
u8 vp_vddmin;
u8 vp_vddmax;
u8 vp_timeout_us;
u8 i2c_slave_addr;
u8 pmic_reg;
bool i2c_high_speed;
u8 i2c_mcode;
unsigned long (*vsel_to_uv) (const u8 vsel);
u8 (*uv_to_vsel) (unsigned long uV);
};
/**
* omap_vdd_info - Per Voltage Domain info
*
* @volt_data : voltage table having the distinct voltages supported
* by the domain and other associated per voltage data.
* @pmic_info : pmic specific parameters which should be populted by
* the pmic drivers.
* @vp_data : the register values, shifts, masks for various
* vp registers
* @vp_rt_data : VP data derived at runtime, not predefined
* @vc_data : structure containing various various vc registers,
* shifts, masks etc.
* @vfsm : voltage manager FSM data
* @voltdm : pointer to the voltage domain structure
* @debug_dir : debug directory for this voltage domain.
* @curr_volt : current voltage for this vdd.
* @vp_enabled : flag to keep track of whether vp is enabled or not
* @volt_scale : API to scale the voltage of the vdd.
*/
struct omap_vdd_info {
struct omap_volt_data *volt_data;
struct omap_volt_pmic_info *pmic_info;
struct omap_vp_instance_data *vp_data;
struct omap_vp_runtime_data vp_rt_data;
struct omap_vc_instance_data *vc_data;
const struct omap_vfsm_instance_data *vfsm;
struct voltagedomain voltdm;
struct dentry *debug_dir;
u32 curr_volt;
bool vp_enabled;
u32 (*read_reg) (u16 mod, u8 offset);
void (*write_reg) (u32 val, u16 mod, u8 offset);
int (*volt_scale) (struct omap_vdd_info *vdd,
unsigned long target_volt);
};
unsigned long omap_vp_get_curr_volt(struct voltagedomain *voltdm);
void omap_vp_enable(struct voltagedomain *voltdm);
void omap_vp_disable(struct voltagedomain *voltdm);
int omap_voltage_scale_vdd(struct voltagedomain *voltdm,
unsigned long target_volt);
void omap_voltage_reset(struct voltagedomain *voltdm);
void omap_voltage_get_volttable(struct voltagedomain *voltdm,
struct omap_volt_data **volt_data);
struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
unsigned long volt);
unsigned long omap_voltage_get_nom_volt(struct voltagedomain *voltdm);
struct dentry *omap_voltage_get_dbgdir(struct voltagedomain *voltdm);
int __init omap_voltage_early_init(s16 prm_mod, s16 prm_irqst_mod,
struct omap_vdd_info *omap_vdd_array[],
u8 omap_vdd_count);
#ifdef CONFIG_PM
int omap_voltage_register_pmic(struct voltagedomain *voltdm,
struct omap_volt_pmic_info *pmic_info);
struct omap_voltdm_pmic *pmic);
void omap_change_voltscale_method(struct voltagedomain *voltdm,
int voltscale_method);
/* API to get the voltagedomain pointer */
struct voltagedomain *omap_voltage_domain_lookup(char *name);
int omap_voltage_late_init(void);
#else
static inline int omap_voltage_register_pmic(struct voltagedomain *voltdm,
struct omap_volt_pmic_info *pmic_info)
{
return -EINVAL;
}
static inline void omap_change_voltscale_method(struct voltagedomain *voltdm,
int voltscale_method) {}
static inline int omap_voltage_late_init(void)
{
return -EINVAL;
}
static inline struct voltagedomain *omap_voltage_domain_lookup(char *name)
{
return ERR_PTR(-EINVAL);
}
#endif
extern void omap2xxx_voltagedomains_init(void);
extern void omap3xxx_voltagedomains_init(void);
extern void omap44xx_voltagedomains_init(void);
struct voltagedomain *voltdm_lookup(const char *name);
void voltdm_init(struct voltagedomain **voltdm_list);
int voltdm_add_pwrdm(struct voltagedomain *voltdm, struct powerdomain *pwrdm);
int voltdm_for_each(int (*fn)(struct voltagedomain *voltdm, void *user),
void *user);
int voltdm_for_each_pwrdm(struct voltagedomain *voltdm,
int (*fn)(struct voltagedomain *voltdm,
struct powerdomain *pwrdm));
int voltdm_scale(struct voltagedomain *voltdm, unsigned long target_volt);
void voltdm_reset(struct voltagedomain *voltdm);
unsigned long voltdm_get_voltage(struct voltagedomain *voltdm);
#endif
/*
* OMAP3 voltage domain data
*
* Copyright (C) 2011 Texas Instruments, Inc.
*
* 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/kernel.h>
#include <linux/init.h>
#include "voltage.h"
static struct voltagedomain omap2_voltdm_core = {
.name = "core",
};
static struct voltagedomain omap2_voltdm_wkup = {
.name = "wakeup",
};
static struct voltagedomain *voltagedomains_omap2[] __initdata = {
&omap2_voltdm_core,
&omap2_voltdm_wkup,
NULL,
};
void __init omap2xxx_voltagedomains_init(void)
{
voltdm_init(voltagedomains_omap2);
}
......@@ -31,65 +31,70 @@
* VDD data
*/
static const struct omap_vfsm_instance_data omap3_vdd1_vfsm_data = {
static const struct omap_vfsm_instance omap3_vdd1_vfsm = {
.voltsetup_reg = OMAP3_PRM_VOLTSETUP1_OFFSET,
.voltsetup_shift = OMAP3430_SETUP_TIME1_SHIFT,
.voltsetup_mask = OMAP3430_SETUP_TIME1_MASK,
};
static struct omap_vdd_info omap3_vdd1_info = {
.vp_data = &omap3_vp1_data,
.vc_data = &omap3_vc1_data,
.vfsm = &omap3_vdd1_vfsm_data,
.voltdm = {
.name = "mpu",
},
};
static const struct omap_vfsm_instance_data omap3_vdd2_vfsm_data = {
static const struct omap_vfsm_instance omap3_vdd2_vfsm = {
.voltsetup_reg = OMAP3_PRM_VOLTSETUP1_OFFSET,
.voltsetup_shift = OMAP3430_SETUP_TIME2_SHIFT,
.voltsetup_mask = OMAP3430_SETUP_TIME2_MASK,
};
static struct omap_vdd_info omap3_vdd2_info = {
.vp_data = &omap3_vp2_data,
.vc_data = &omap3_vc2_data,
.vfsm = &omap3_vdd2_vfsm_data,
.voltdm = {
static struct voltagedomain omap3_voltdm_mpu = {
.name = "mpu_iva",
.scalable = true,
.read = omap3_prm_vcvp_read,
.write = omap3_prm_vcvp_write,
.rmw = omap3_prm_vcvp_rmw,
.vc = &omap3_vc_mpu,
.vfsm = &omap3_vdd1_vfsm,
.vp = &omap3_vp_mpu,
};
static struct voltagedomain omap3_voltdm_core = {
.name = "core",
},
.scalable = true,
.read = omap3_prm_vcvp_read,
.write = omap3_prm_vcvp_write,
.rmw = omap3_prm_vcvp_rmw,
.vc = &omap3_vc_core,
.vfsm = &omap3_vdd2_vfsm,
.vp = &omap3_vp_core,
};
/* OMAP3 VDD structures */
static struct omap_vdd_info *omap3_vdd_info[] = {
&omap3_vdd1_info,
&omap3_vdd2_info,
static struct voltagedomain omap3_voltdm_wkup = {
.name = "wakeup",
};
/* OMAP3 specific voltage init functions */
static int __init omap3xxx_voltage_early_init(void)
{
s16 prm_mod = OMAP3430_GR_MOD;
s16 prm_irqst_ocp_mod = OCP_MOD;
static struct voltagedomain *voltagedomains_omap3[] __initdata = {
&omap3_voltdm_mpu,
&omap3_voltdm_core,
&omap3_voltdm_wkup,
NULL,
};
static const char *sys_clk_name __initdata = "sys_ck";
if (!cpu_is_omap34xx())
return 0;
void __init omap3xxx_voltagedomains_init(void)
{
struct voltagedomain *voltdm;
int i;
/*
* XXX Will depend on the process, validation, and binning
* for the currently-running IC
*/
if (cpu_is_omap3630()) {
omap3_vdd1_info.volt_data = omap36xx_vddmpu_volt_data;
omap3_vdd2_info.volt_data = omap36xx_vddcore_volt_data;
omap3_voltdm_mpu.volt_data = omap36xx_vddmpu_volt_data;
omap3_voltdm_core.volt_data = omap36xx_vddcore_volt_data;
} else {
omap3_vdd1_info.volt_data = omap34xx_vddmpu_volt_data;
omap3_vdd2_info.volt_data = omap34xx_vddcore_volt_data;
omap3_voltdm_mpu.volt_data = omap34xx_vddmpu_volt_data;
omap3_voltdm_core.volt_data = omap34xx_vddcore_volt_data;
}
return omap_voltage_early_init(prm_mod, prm_irqst_ocp_mod,
omap3_vdd_info,
ARRAY_SIZE(omap3_vdd_info));
for (i = 0; voltdm = voltagedomains_omap3[i], voltdm; i++)
voltdm->sys_clk.name = sys_clk_name;
voltdm_init(voltagedomains_omap3);
};
core_initcall(omap3xxx_voltage_early_init);
......@@ -32,71 +32,80 @@
#include "vc.h"
#include "vp.h"
static const struct omap_vfsm_instance_data omap4_vdd_mpu_vfsm_data = {
static const struct omap_vfsm_instance omap4_vdd_mpu_vfsm = {
.voltsetup_reg = OMAP4_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET,
};
static struct omap_vdd_info omap4_vdd_mpu_info = {
.vp_data = &omap4_vp_mpu_data,
.vc_data = &omap4_vc_mpu_data,
.vfsm = &omap4_vdd_mpu_vfsm_data,
.voltdm = {
.name = "mpu",
},
static const struct omap_vfsm_instance omap4_vdd_iva_vfsm = {
.voltsetup_reg = OMAP4_PRM_VOLTSETUP_IVA_RET_SLEEP_OFFSET,
};
static const struct omap_vfsm_instance_data omap4_vdd_iva_vfsm_data = {
.voltsetup_reg = OMAP4_PRM_VOLTSETUP_IVA_RET_SLEEP_OFFSET,
static const struct omap_vfsm_instance omap4_vdd_core_vfsm = {
.voltsetup_reg = OMAP4_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET,
};
static struct omap_vdd_info omap4_vdd_iva_info = {
.vp_data = &omap4_vp_iva_data,
.vc_data = &omap4_vc_iva_data,
.vfsm = &omap4_vdd_iva_vfsm_data,
.voltdm = {
.name = "iva",
},
static struct voltagedomain omap4_voltdm_mpu = {
.name = "mpu",
.scalable = true,
.read = omap4_prm_vcvp_read,
.write = omap4_prm_vcvp_write,
.rmw = omap4_prm_vcvp_rmw,
.vc = &omap4_vc_mpu,
.vfsm = &omap4_vdd_mpu_vfsm,
.vp = &omap4_vp_mpu,
};
static const struct omap_vfsm_instance_data omap4_vdd_core_vfsm_data = {
.voltsetup_reg = OMAP4_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET,
static struct voltagedomain omap4_voltdm_iva = {
.name = "iva",
.scalable = true,
.read = omap4_prm_vcvp_read,
.write = omap4_prm_vcvp_write,
.rmw = omap4_prm_vcvp_rmw,
.vc = &omap4_vc_iva,
.vfsm = &omap4_vdd_iva_vfsm,
.vp = &omap4_vp_iva,
};
static struct omap_vdd_info omap4_vdd_core_info = {
.vp_data = &omap4_vp_core_data,
.vc_data = &omap4_vc_core_data,
.vfsm = &omap4_vdd_core_vfsm_data,
.voltdm = {
static struct voltagedomain omap4_voltdm_core = {
.name = "core",
},
.scalable = true,
.read = omap4_prm_vcvp_read,
.write = omap4_prm_vcvp_write,
.rmw = omap4_prm_vcvp_rmw,
.vc = &omap4_vc_core,
.vfsm = &omap4_vdd_core_vfsm,
.vp = &omap4_vp_core,
};
/* OMAP4 VDD structures */
static struct omap_vdd_info *omap4_vdd_info[] = {
&omap4_vdd_mpu_info,
&omap4_vdd_iva_info,
&omap4_vdd_core_info,
static struct voltagedomain omap4_voltdm_wkup = {
.name = "wakeup",
};
/* OMAP4 specific voltage init functions */
static int __init omap44xx_voltage_early_init(void)
{
s16 prm_mod = OMAP4430_PRM_DEVICE_INST;
s16 prm_irqst_ocp_mod = OMAP4430_PRM_OCP_SOCKET_INST;
static struct voltagedomain *voltagedomains_omap4[] __initdata = {
&omap4_voltdm_mpu,
&omap4_voltdm_iva,
&omap4_voltdm_core,
&omap4_voltdm_wkup,
NULL,
};
if (!cpu_is_omap44xx())
return 0;
static const char *sys_clk_name __initdata = "sys_clkin_ck";
void __init omap44xx_voltagedomains_init(void)
{
struct voltagedomain *voltdm;
int i;
/*
* XXX Will depend on the process, validation, and binning
* for the currently-running IC
*/
omap4_vdd_mpu_info.volt_data = omap44xx_vdd_mpu_volt_data;
omap4_vdd_iva_info.volt_data = omap44xx_vdd_iva_volt_data;
omap4_vdd_core_info.volt_data = omap44xx_vdd_core_volt_data;
omap4_voltdm_mpu.volt_data = omap44xx_vdd_mpu_volt_data;
omap4_voltdm_iva.volt_data = omap44xx_vdd_iva_volt_data;
omap4_voltdm_core.volt_data = omap44xx_vdd_core_volt_data;
for (i = 0; voltdm = voltagedomains_omap4[i], voltdm; i++)
voltdm->sys_clk.name = sys_clk_name;
return omap_voltage_early_init(prm_mod, prm_irqst_ocp_mod,
omap4_vdd_info,
ARRAY_SIZE(omap4_vdd_info));
voltdm_init(voltagedomains_omap4);
};
core_initcall(omap44xx_voltage_early_init);
#include <linux/kernel.h>
#include <linux/init.h>
#include <plat/common.h>
#include "voltage.h"
#include "vp.h"
#include "prm-regbits-34xx.h"
#include "prm-regbits-44xx.h"
#include "prm44xx.h"
static u32 _vp_set_init_voltage(struct voltagedomain *voltdm, u32 volt)
{
struct omap_vp_instance *vp = voltdm->vp;
u32 vpconfig;
char vsel;
vsel = voltdm->pmic->uv_to_vsel(volt);
vpconfig = voltdm->read(vp->vpconfig);
vpconfig &= ~(vp->common->vpconfig_initvoltage_mask |
vp->common->vpconfig_forceupdate |
vp->common->vpconfig_initvdd);
vpconfig |= vsel << __ffs(vp->common->vpconfig_initvoltage_mask);
voltdm->write(vpconfig, vp->vpconfig);
/* Trigger initVDD value copy to voltage processor */
voltdm->write((vpconfig | vp->common->vpconfig_initvdd),
vp->vpconfig);
/* Clear initVDD copy trigger bit */
voltdm->write(vpconfig, vp->vpconfig);
return vpconfig;
}
/* Generic voltage init functions */
void __init omap_vp_init(struct voltagedomain *voltdm)
{
struct omap_vp_instance *vp = voltdm->vp;
u32 val, sys_clk_rate, timeout, waittime;
u32 vddmin, vddmax, vstepmin, vstepmax;
if (!voltdm->read || !voltdm->write) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
__func__, voltdm->name);
return;
}
vp->enabled = false;
/* Divide to avoid overflow */
sys_clk_rate = voltdm->sys_clk.rate / 1000;
timeout = (sys_clk_rate * voltdm->pmic->vp_timeout_us) / 1000;
vddmin = voltdm->pmic->vp_vddmin;
vddmax = voltdm->pmic->vp_vddmax;
waittime = ((voltdm->pmic->step_size / voltdm->pmic->slew_rate) *
sys_clk_rate) / 1000;
vstepmin = voltdm->pmic->vp_vstepmin;
vstepmax = voltdm->pmic->vp_vstepmax;
/*
* VP_CONFIG: error gain is not set here, it will be updated
* on each scale, based on OPP.
*/
val = (voltdm->pmic->vp_erroroffset <<
__ffs(voltdm->vp->common->vpconfig_erroroffset_mask)) |
vp->common->vpconfig_timeouten;
voltdm->write(val, vp->vpconfig);
/* VSTEPMIN */
val = (waittime << vp->common->vstepmin_smpswaittimemin_shift) |
(vstepmin << vp->common->vstepmin_stepmin_shift);
voltdm->write(val, vp->vstepmin);
/* VSTEPMAX */
val = (vstepmax << vp->common->vstepmax_stepmax_shift) |
(waittime << vp->common->vstepmax_smpswaittimemax_shift);
voltdm->write(val, vp->vstepmax);
/* VLIMITTO */
val = (vddmax << vp->common->vlimitto_vddmax_shift) |
(vddmin << vp->common->vlimitto_vddmin_shift) |
(timeout << vp->common->vlimitto_timeout_shift);
voltdm->write(val, vp->vlimitto);
}
int omap_vp_update_errorgain(struct voltagedomain *voltdm,
unsigned long target_volt)
{
struct omap_volt_data *volt_data;
if (!voltdm->vp)
return -EINVAL;
/* Get volt_data corresponding to target_volt */
volt_data = omap_voltage_get_voltdata(voltdm, target_volt);
if (IS_ERR(volt_data))
return -EINVAL;
/* Setting vp errorgain based on the voltage */
voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask,
volt_data->vp_errgain <<
__ffs(voltdm->vp->common->vpconfig_errorgain_mask),
voltdm->vp->vpconfig);
return 0;
}
/* VP force update method of voltage scaling */
int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
unsigned long target_volt)
{
struct omap_vp_instance *vp = voltdm->vp;
u32 vpconfig;
u8 target_vsel, current_vsel;
int ret, timeout = 0;
ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, &current_vsel);
if (ret)
return ret;
/*
* Clear all pending TransactionDone interrupt/status. Typical latency
* is <3us
*/
while (timeout++ < VP_TRANXDONE_TIMEOUT) {
vp->common->ops->clear_txdone(vp->id);
if (!vp->common->ops->check_txdone(vp->id))
break;
udelay(1);
}
if (timeout >= VP_TRANXDONE_TIMEOUT) {
pr_warning("%s: vdd_%s TRANXDONE timeout exceeded."
"Voltage change aborted", __func__, voltdm->name);
return -ETIMEDOUT;
}
vpconfig = _vp_set_init_voltage(voltdm, target_volt);
/* Force update of voltage */
voltdm->write(vpconfig | vp->common->vpconfig_forceupdate,
voltdm->vp->vpconfig);
/*
* Wait for TransactionDone. Typical latency is <200us.
* Depends on SMPSWAITTIMEMIN/MAX and voltage change
*/
timeout = 0;
omap_test_timeout(vp->common->ops->check_txdone(vp->id),
VP_TRANXDONE_TIMEOUT, timeout);
if (timeout >= VP_TRANXDONE_TIMEOUT)
pr_err("%s: vdd_%s TRANXDONE timeout exceeded."
"TRANXDONE never got set after the voltage update\n",
__func__, voltdm->name);
omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel);
/*
* Disable TransactionDone interrupt , clear all status, clear
* control registers
*/
timeout = 0;
while (timeout++ < VP_TRANXDONE_TIMEOUT) {
vp->common->ops->clear_txdone(vp->id);
if (!vp->common->ops->check_txdone(vp->id))
break;
udelay(1);
}
if (timeout >= VP_TRANXDONE_TIMEOUT)
pr_warning("%s: vdd_%s TRANXDONE timeout exceeded while trying"
"to clear the TRANXDONE status\n",
__func__, voltdm->name);
/* Clear force bit */
voltdm->write(vpconfig, vp->vpconfig);
return 0;
}
/**
* omap_vp_enable() - API to enable a particular VP
* @voltdm: pointer to the VDD whose VP is to be enabled.
*
* This API enables a particular voltage processor. Needed by the smartreflex
* class drivers.
*/
void omap_vp_enable(struct voltagedomain *voltdm)
{
struct omap_vp_instance *vp;
u32 vpconfig, volt;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return;
}
vp = voltdm->vp;
if (!voltdm->read || !voltdm->write) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
__func__, voltdm->name);
return;
}
/* If VP is already enabled, do nothing. Return */
if (vp->enabled)
return;
volt = voltdm_get_voltage(voltdm);
if (!volt) {
pr_warning("%s: unable to find current voltage for %s\n",
__func__, voltdm->name);
return;
}
vpconfig = _vp_set_init_voltage(voltdm, volt);
/* Enable VP */
vpconfig |= vp->common->vpconfig_vpenable;
voltdm->write(vpconfig, vp->vpconfig);
vp->enabled = true;
}
/**
* omap_vp_disable() - API to disable a particular VP
* @voltdm: pointer to the VDD whose VP is to be disabled.
*
* This API disables a particular voltage processor. Needed by the smartreflex
* class drivers.
*/
void omap_vp_disable(struct voltagedomain *voltdm)
{
struct omap_vp_instance *vp;
u32 vpconfig;
int timeout;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
return;
}
vp = voltdm->vp;
if (!voltdm->read || !voltdm->write) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
__func__, voltdm->name);
return;
}
/* If VP is already disabled, do nothing. Return */
if (!vp->enabled) {
pr_warning("%s: Trying to disable VP for vdd_%s when"
"it is already disabled\n", __func__, voltdm->name);
return;
}
/* Disable VP */
vpconfig = voltdm->read(vp->vpconfig);
vpconfig &= ~vp->common->vpconfig_vpenable;
voltdm->write(vpconfig, vp->vpconfig);
/*
* Wait for VP idle Typical latency is <2us. Maximum latency is ~100us
*/
omap_test_timeout((voltdm->read(vp->vstatus)),
VP_IDLE_TIMEOUT, timeout);
if (timeout >= VP_IDLE_TIMEOUT)
pr_warning("%s: vdd_%s idle timedout\n",
__func__, voltdm->name);
vp->enabled = false;
return;
}
......@@ -19,44 +19,60 @@
#include <linux/kernel.h>
struct voltagedomain;
/*
* Voltage Processor (VP) identifiers
*/
#define OMAP3_VP_VDD_MPU_ID 0
#define OMAP3_VP_VDD_CORE_ID 1
#define OMAP4_VP_VDD_CORE_ID 0
#define OMAP4_VP_VDD_IVA_ID 1
#define OMAP4_VP_VDD_MPU_ID 2
/* XXX document */
#define VP_IDLE_TIMEOUT 200
#define VP_TRANXDONE_TIMEOUT 300
/**
* struct omap_vp_ops - per-VP operations
* @check_txdone: check for VP transaction done
* @clear_txdone: clear VP transaction done status
*/
struct omap_vp_ops {
u32 (*check_txdone)(u8 vp_id);
void (*clear_txdone)(u8 vp_id);
};
/**
* struct omap_vp_common_data - register data common to all VDDs
* struct omap_vp_common - register data common to all VDDs
* @vpconfig_erroroffset_mask: ERROROFFSET bitmask in the PRM_VP*_CONFIG reg
* @vpconfig_errorgain_mask: ERRORGAIN bitmask in the PRM_VP*_CONFIG reg
* @vpconfig_initvoltage_mask: INITVOLTAGE bitmask in the PRM_VP*_CONFIG reg
* @vpconfig_timeouten_mask: TIMEOUT bitmask in the PRM_VP*_CONFIG reg
* @vpconfig_timeouten: TIMEOUT bitmask in the PRM_VP*_CONFIG reg
* @vpconfig_initvdd: INITVDD bitmask in the PRM_VP*_CONFIG reg
* @vpconfig_forceupdate: FORCEUPDATE bitmask in the PRM_VP*_CONFIG reg
* @vpconfig_vpenable: VPENABLE bitmask in the PRM_VP*_CONFIG reg
* @vpconfig_erroroffset_shift: ERROROFFSET field shift in PRM_VP*_CONFIG reg
* @vpconfig_errorgain_shift: ERRORGAIN field shift in PRM_VP*_CONFIG reg
* @vpconfig_initvoltage_shift: INITVOLTAGE field shift in PRM_VP*_CONFIG reg
* @vpconfig_stepmin_shift: VSTEPMIN field shift in the PRM_VP*_VSTEPMIN reg
* @vpconfig_smpswaittimemin_shift: SMPSWAITTIMEMIN field shift in PRM_VP*_VSTEPMIN reg
* @vpconfig_stepmax_shift: VSTEPMAX field shift in the PRM_VP*_VSTEPMAX reg
* @vpconfig_smpswaittimemax_shift: SMPSWAITTIMEMAX field shift in PRM_VP*_VSTEPMAX reg
* @vpconfig_vlimitto_vddmin_shift: VDDMIN field shift in PRM_VP*_VLIMITTO reg
* @vpconfig_vlimitto_vddmax_shift: VDDMAX field shift in PRM_VP*_VLIMITTO reg
* @vpconfig_vlimitto_timeout_shift: TIMEOUT field shift in PRM_VP*_VLIMITTO reg
*
* XXX It it not necessary to have both a mask and a shift for the same
* bitfield - remove one
* XXX Many of these fields are wrongly named -- e.g., vpconfig_smps* -- fix!
* @vstepmin_stepmin_shift: VSTEPMIN field shift in the PRM_VP*_VSTEPMIN reg
* @vstepmin_smpswaittimemin_shift: SMPSWAITTIMEMIN field shift in PRM_VP*_VSTEPMIN reg
* @vstepmax_stepmax_shift: VSTEPMAX field shift in the PRM_VP*_VSTEPMAX reg
* @vstepmax_smpswaittimemax_shift: SMPSWAITTIMEMAX field shift in PRM_VP*_VSTEPMAX reg
* @vlimitto_vddmin_shift: VDDMIN field shift in PRM_VP*_VLIMITTO reg
* @vlimitto_vddmax_shift: VDDMAX field shift in PRM_VP*_VLIMITTO reg
* @vlimitto_timeout_shift: TIMEOUT field shift in PRM_VP*_VLIMITTO reg
* @vpvoltage_mask: VPVOLTAGE field mask in PRM_VP*_VOLTAGE reg
*/
struct omap_vp_common_data {
struct omap_vp_common {
u32 vpconfig_erroroffset_mask;
u32 vpconfig_errorgain_mask;
u32 vpconfig_initvoltage_mask;
u32 vpconfig_timeouten;
u32 vpconfig_initvdd;
u32 vpconfig_forceupdate;
u32 vpconfig_vpenable;
u8 vpconfig_erroroffset_shift;
u8 vpconfig_errorgain_shift;
u8 vpconfig_initvoltage_shift;
u8 vpconfig_timeouten;
u8 vpconfig_initvdd;
u8 vpconfig_forceupdate;
u8 vpconfig_vpenable;
u8 vstepmin_stepmin_shift;
u8 vstepmin_smpswaittimemin_shift;
u8 vstepmax_stepmax_shift;
......@@ -64,80 +80,49 @@ struct omap_vp_common_data {
u8 vlimitto_vddmin_shift;
u8 vlimitto_vddmax_shift;
u8 vlimitto_timeout_shift;
};
u8 vpvoltage_mask;
/**
* struct omap_vp_prm_irqst_data - PRM_IRQSTATUS_MPU.VP_TRANXDONE_ST data
* @prm_irqst_reg: reg offset for PRM_IRQSTATUS_MPU from top of PRM
* @tranxdone_status: VP_TRANXDONE_ST bitmask in PRM_IRQSTATUS_MPU reg
*
* XXX prm_irqst_reg does not belong here
* XXX Note that on OMAP3, VP_TRANXDONE interrupt may not work due to a
* hardware bug
* XXX This structure is probably not needed
*/
struct omap_vp_prm_irqst_data {
u8 prm_irqst_reg;
u32 tranxdone_status;
const struct omap_vp_ops *ops;
};
/**
* struct omap_vp_instance_data - VP register offsets (per-VDD)
* @vp_common: pointer to struct omap_vp_common_data * for this SoC
* @prm_irqst_data: pointer to struct omap_vp_prm_irqst_data for this VDD
* struct omap_vp_instance - VP register offsets (per-VDD)
* @common: pointer to struct omap_vp_common * for this SoC
* @vpconfig: PRM_VP*_CONFIG reg offset from PRM start
* @vstepmin: PRM_VP*_VSTEPMIN reg offset from PRM start
* @vlimitto: PRM_VP*_VLIMITTO reg offset from PRM start
* @vstatus: PRM_VP*_VSTATUS reg offset from PRM start
* @voltage: PRM_VP*_VOLTAGE reg offset from PRM start
* @id: Unique identifier for VP instance.
* @enabled: flag to keep track of whether vp is enabled or not
*
* XXX vp_common is probably not needed since it is per-SoC
*/
struct omap_vp_instance_data {
const struct omap_vp_common_data *vp_common;
const struct omap_vp_prm_irqst_data *prm_irqst_data;
struct omap_vp_instance {
const struct omap_vp_common *common;
u8 vpconfig;
u8 vstepmin;
u8 vstepmax;
u8 vlimitto;
u8 vstatus;
u8 voltage;
u8 id;
bool enabled;
};
/**
* struct omap_vp_runtime_data - VP data populated at runtime by code
* @vpconfig_erroroffset: value of ERROROFFSET bitfield in PRM_VP*_CONFIG
* @vpconfig_errorgain: value of ERRORGAIN bitfield in PRM_VP*_CONFIG
* @vstepmin_smpswaittimemin: value of SMPSWAITTIMEMIN bitfield in PRM_VP*_VSTEPMIN
* @vstepmax_smpswaittimemax: value of SMPSWAITTIMEMAX bitfield in PRM_VP*_VSTEPMAX
* @vlimitto_timeout: value of TIMEOUT bitfield in PRM_VP*_VLIMITTO
* @vstepmin_stepmin: value of VSTEPMIN bitfield in PRM_VP*_VSTEPMIN
* @vstepmax_stepmax: value of VSTEPMAX bitfield in PRM_VP*_VSTEPMAX
* @vlimitto_vddmin: value of VDDMIN bitfield in PRM_VP*_VLIMITTO
* @vlimitto_vddmax: value of VDDMAX bitfield in PRM_VP*_VLIMITTO
*
* XXX Is this structure really needed? Why not just program the
* device directly? They are in PRM space, therefore in the WKUP
* powerdomain, so register contents should not be lost in off-mode.
* XXX Some of these fields are incorrectly named, e.g., vstep*
*/
struct omap_vp_runtime_data {
u32 vpconfig_erroroffset;
u16 vpconfig_errorgain;
u16 vstepmin_smpswaittimemin;
u16 vstepmax_smpswaittimemax;
u16 vlimitto_timeout;
u8 vstepmin_stepmin;
u8 vstepmax_stepmax;
u8 vlimitto_vddmin;
u8 vlimitto_vddmax;
};
extern struct omap_vp_instance omap3_vp_mpu;
extern struct omap_vp_instance omap3_vp_core;
extern struct omap_vp_instance_data omap3_vp1_data;
extern struct omap_vp_instance_data omap3_vp2_data;
extern struct omap_vp_instance omap4_vp_mpu;
extern struct omap_vp_instance omap4_vp_iva;
extern struct omap_vp_instance omap4_vp_core;
extern struct omap_vp_instance_data omap4_vp_mpu_data;
extern struct omap_vp_instance_data omap4_vp_iva_data;
extern struct omap_vp_instance_data omap4_vp_core_data;
void omap_vp_init(struct voltagedomain *voltdm);
void omap_vp_enable(struct voltagedomain *voltdm);
void omap_vp_disable(struct voltagedomain *voltdm);
int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
unsigned long target_volt);
int omap_vp_update_errorgain(struct voltagedomain *voltdm,
unsigned long target_volt);
#endif
......@@ -25,16 +25,20 @@
#include "voltage.h"
#include "vp.h"
#include "prm2xxx_3xxx.h"
static const struct omap_vp_ops omap3_vp_ops = {
.check_txdone = omap3_prm_vp_check_txdone,
.clear_txdone = omap3_prm_vp_clear_txdone,
};
/*
* VP data common to 34xx/36xx chips
* XXX This stuff presumably belongs in the vp3xxx.c or vp.c file.
*/
static const struct omap_vp_common_data omap3_vp_common = {
.vpconfig_erroroffset_shift = OMAP3430_ERROROFFSET_SHIFT,
static const struct omap_vp_common omap3_vp_common = {
.vpconfig_erroroffset_mask = OMAP3430_ERROROFFSET_MASK,
.vpconfig_errorgain_mask = OMAP3430_ERRORGAIN_MASK,
.vpconfig_errorgain_shift = OMAP3430_ERRORGAIN_SHIFT,
.vpconfig_initvoltage_shift = OMAP3430_INITVOLTAGE_SHIFT,
.vpconfig_initvoltage_mask = OMAP3430_INITVOLTAGE_MASK,
.vpconfig_timeouten = OMAP3430_TIMEOUTEN_MASK,
.vpconfig_initvdd = OMAP3430_INITVDD_MASK,
......@@ -47,36 +51,29 @@ static const struct omap_vp_common_data omap3_vp_common = {
.vlimitto_vddmin_shift = OMAP3430_VDDMIN_SHIFT,
.vlimitto_vddmax_shift = OMAP3430_VDDMAX_SHIFT,
.vlimitto_timeout_shift = OMAP3430_TIMEOUT_SHIFT,
};
.vpvoltage_mask = OMAP3430_VPVOLTAGE_MASK,
static const struct omap_vp_prm_irqst_data omap3_vp1_prm_irqst_data = {
.prm_irqst_reg = OMAP3_PRM_IRQSTATUS_MPU_OFFSET,
.tranxdone_status = OMAP3430_VP1_TRANXDONE_ST_MASK,
.ops = &omap3_vp_ops,
};
struct omap_vp_instance_data omap3_vp1_data = {
.vp_common = &omap3_vp_common,
struct omap_vp_instance omap3_vp_mpu = {
.id = OMAP3_VP_VDD_MPU_ID,
.common = &omap3_vp_common,
.vpconfig = OMAP3_PRM_VP1_CONFIG_OFFSET,
.vstepmin = OMAP3_PRM_VP1_VSTEPMIN_OFFSET,
.vstepmax = OMAP3_PRM_VP1_VSTEPMAX_OFFSET,
.vlimitto = OMAP3_PRM_VP1_VLIMITTO_OFFSET,
.vstatus = OMAP3_PRM_VP1_STATUS_OFFSET,
.voltage = OMAP3_PRM_VP1_VOLTAGE_OFFSET,
.prm_irqst_data = &omap3_vp1_prm_irqst_data,
};
static const struct omap_vp_prm_irqst_data omap3_vp2_prm_irqst_data = {
.prm_irqst_reg = OMAP3_PRM_IRQSTATUS_MPU_OFFSET,
.tranxdone_status = OMAP3430_VP2_TRANXDONE_ST_MASK,
};
struct omap_vp_instance_data omap3_vp2_data = {
.vp_common = &omap3_vp_common,
struct omap_vp_instance omap3_vp_core = {
.id = OMAP3_VP_VDD_CORE_ID,
.common = &omap3_vp_common,
.vpconfig = OMAP3_PRM_VP2_CONFIG_OFFSET,
.vstepmin = OMAP3_PRM_VP2_VSTEPMIN_OFFSET,
.vstepmax = OMAP3_PRM_VP2_VSTEPMAX_OFFSET,
.vlimitto = OMAP3_PRM_VP2_VLIMITTO_OFFSET,
.vstatus = OMAP3_PRM_VP2_STATUS_OFFSET,
.voltage = OMAP3_PRM_VP2_VOLTAGE_OFFSET,
.prm_irqst_data = &omap3_vp2_prm_irqst_data,
};
......@@ -27,15 +27,18 @@
#include "vp.h"
static const struct omap_vp_ops omap4_vp_ops = {
.check_txdone = omap4_prm_vp_check_txdone,
.clear_txdone = omap4_prm_vp_clear_txdone,
};
/*
* VP data common to 44xx chips
* XXX This stuff presumably belongs in the vp44xx.c or vp.c file.
*/
static const struct omap_vp_common_data omap4_vp_common = {
.vpconfig_erroroffset_shift = OMAP4430_ERROROFFSET_SHIFT,
static const struct omap_vp_common omap4_vp_common = {
.vpconfig_erroroffset_mask = OMAP4430_ERROROFFSET_MASK,
.vpconfig_errorgain_mask = OMAP4430_ERRORGAIN_MASK,
.vpconfig_errorgain_shift = OMAP4430_ERRORGAIN_SHIFT,
.vpconfig_initvoltage_shift = OMAP4430_INITVOLTAGE_SHIFT,
.vpconfig_initvoltage_mask = OMAP4430_INITVOLTAGE_MASK,
.vpconfig_timeouten = OMAP4430_TIMEOUTEN_MASK,
.vpconfig_initvdd = OMAP4430_INITVDD_MASK,
......@@ -48,53 +51,39 @@ static const struct omap_vp_common_data omap4_vp_common = {
.vlimitto_vddmin_shift = OMAP4430_VDDMIN_SHIFT,
.vlimitto_vddmax_shift = OMAP4430_VDDMAX_SHIFT,
.vlimitto_timeout_shift = OMAP4430_TIMEOUT_SHIFT,
.vpvoltage_mask = OMAP4430_VPVOLTAGE_MASK,
.ops = &omap4_vp_ops,
};
static const struct omap_vp_prm_irqst_data omap4_vp_mpu_prm_irqst_data = {
.prm_irqst_reg = OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET,
.tranxdone_status = OMAP4430_VP_MPU_TRANXDONE_ST_MASK,
};
struct omap_vp_instance_data omap4_vp_mpu_data = {
.vp_common = &omap4_vp_common,
struct omap_vp_instance omap4_vp_mpu = {
.id = OMAP4_VP_VDD_MPU_ID,
.common = &omap4_vp_common,
.vpconfig = OMAP4_PRM_VP_MPU_CONFIG_OFFSET,
.vstepmin = OMAP4_PRM_VP_MPU_VSTEPMIN_OFFSET,
.vstepmax = OMAP4_PRM_VP_MPU_VSTEPMAX_OFFSET,
.vlimitto = OMAP4_PRM_VP_MPU_VLIMITTO_OFFSET,
.vstatus = OMAP4_PRM_VP_MPU_STATUS_OFFSET,
.voltage = OMAP4_PRM_VP_MPU_VOLTAGE_OFFSET,
.prm_irqst_data = &omap4_vp_mpu_prm_irqst_data,
};
static const struct omap_vp_prm_irqst_data omap4_vp_iva_prm_irqst_data = {
.prm_irqst_reg = OMAP4_PRM_IRQSTATUS_MPU_OFFSET,
.tranxdone_status = OMAP4430_VP_IVA_TRANXDONE_ST_MASK,
};
struct omap_vp_instance_data omap4_vp_iva_data = {
.vp_common = &omap4_vp_common,
struct omap_vp_instance omap4_vp_iva = {
.id = OMAP4_VP_VDD_IVA_ID,
.common = &omap4_vp_common,
.vpconfig = OMAP4_PRM_VP_IVA_CONFIG_OFFSET,
.vstepmin = OMAP4_PRM_VP_IVA_VSTEPMIN_OFFSET,
.vstepmax = OMAP4_PRM_VP_IVA_VSTEPMAX_OFFSET,
.vlimitto = OMAP4_PRM_VP_IVA_VLIMITTO_OFFSET,
.vstatus = OMAP4_PRM_VP_IVA_STATUS_OFFSET,
.voltage = OMAP4_PRM_VP_IVA_VOLTAGE_OFFSET,
.prm_irqst_data = &omap4_vp_iva_prm_irqst_data,
};
static const struct omap_vp_prm_irqst_data omap4_vp_core_prm_irqst_data = {
.prm_irqst_reg = OMAP4_PRM_IRQSTATUS_MPU_OFFSET,
.tranxdone_status = OMAP4430_VP_CORE_TRANXDONE_ST_MASK,
};
struct omap_vp_instance_data omap4_vp_core_data = {
.vp_common = &omap4_vp_common,
struct omap_vp_instance omap4_vp_core = {
.id = OMAP4_VP_VDD_CORE_ID,
.common = &omap4_vp_common,
.vpconfig = OMAP4_PRM_VP_CORE_CONFIG_OFFSET,
.vstepmin = OMAP4_PRM_VP_CORE_VSTEPMIN_OFFSET,
.vstepmax = OMAP4_PRM_VP_CORE_VSTEPMAX_OFFSET,
.vlimitto = OMAP4_PRM_VP_CORE_VLIMITTO_OFFSET,
.vstatus = OMAP4_PRM_VP_CORE_STATUS_OFFSET,
.voltage = OMAP4_PRM_VP_CORE_VOLTAGE_OFFSET,
.prm_irqst_data = &omap4_vp_core_prm_irqst_data,
};
......@@ -3,5 +3,7 @@ obj-y += irq.o
obj-y += clock.o
obj-y += rstc.o
obj-y += prima2.o
obj-y += rtciobrg.o
obj-$(CONFIG_DEBUG_LL) += lluart.o
obj-$(CONFIG_CACHE_L2X0) += l2x0.o
obj-$(CONFIG_SUSPEND) += pm.o sleep.o
......@@ -13,6 +13,8 @@
#include <asm/mach/irq.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/irqdomain.h>
#include <linux/syscore_ops.h>
#define SIRFSOC_INT_RISC_MASK0 0x0018
#define SIRFSOC_INT_RISC_MASK1 0x001C
......@@ -66,7 +68,48 @@ void __init sirfsoc_of_irq_init(void)
if (!sirfsoc_intc_base)
panic("unable to map intc cpu registers\n");
irq_domain_add_simple(np, 0);
of_node_put(np);
sirfsoc_irq_init();
}
struct sirfsoc_irq_status {
u32 mask0;
u32 mask1;
u32 level0;
u32 level1;
};
static struct sirfsoc_irq_status sirfsoc_irq_st;
static int sirfsoc_irq_suspend(void)
{
sirfsoc_irq_st.mask0 = readl_relaxed(sirfsoc_intc_base + SIRFSOC_INT_RISC_MASK0);
sirfsoc_irq_st.mask1 = readl_relaxed(sirfsoc_intc_base + SIRFSOC_INT_RISC_MASK1);
sirfsoc_irq_st.level0 = readl_relaxed(sirfsoc_intc_base + SIRFSOC_INT_RISC_LEVEL0);
sirfsoc_irq_st.level1 = readl_relaxed(sirfsoc_intc_base + SIRFSOC_INT_RISC_LEVEL1);
return 0;
}
static void sirfsoc_irq_resume(void)
{
writel_relaxed(sirfsoc_irq_st.mask0, sirfsoc_intc_base + SIRFSOC_INT_RISC_MASK0);
writel_relaxed(sirfsoc_irq_st.mask1, sirfsoc_intc_base + SIRFSOC_INT_RISC_MASK1);
writel_relaxed(sirfsoc_irq_st.level0, sirfsoc_intc_base + SIRFSOC_INT_RISC_LEVEL0);
writel_relaxed(sirfsoc_irq_st.level1, sirfsoc_intc_base + SIRFSOC_INT_RISC_LEVEL1);
}
static struct syscore_ops sirfsoc_irq_syscore_ops = {
.suspend = sirfsoc_irq_suspend,
.resume = sirfsoc_irq_resume,
};
static int __init sirfsoc_irq_pm_init(void)
{
register_syscore_ops(&sirfsoc_irq_syscore_ops);
return 0;
}
device_initcall(sirfsoc_irq_pm_init);
......@@ -8,51 +8,24 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/hardware/cache-l2x0.h>
#define L2X0_ADDR_FILTERING_START 0xC00
#define L2X0_ADDR_FILTERING_END 0xC04
static struct of_device_id l2x_ids[] = {
{ .compatible = "arm,pl310-cache" },
static struct of_device_id prima2_l2x0_ids[] = {
{ .compatible = "sirf,prima2-pl310-cache" },
{},
};
static int __init sirfsoc_of_l2x_init(void)
static int __init sirfsoc_l2x0_init(void)
{
struct device_node *np;
void __iomem *sirfsoc_l2x_base;
np = of_find_matching_node(NULL, l2x_ids);
if (!np)
panic("unable to find compatible l2x node in dtb\n");
sirfsoc_l2x_base = of_iomap(np, 0);
if (!sirfsoc_l2x_base)
panic("unable to map l2x cpu registers\n");
of_node_put(np);
if (!(readl_relaxed(sirfsoc_l2x_base + L2X0_CTRL) & 1)) {
/*
* set the physical memory windows L2 cache will cover
*/
writel_relaxed(PHYS_OFFSET + 1024 * 1024 * 1024,
sirfsoc_l2x_base + L2X0_ADDR_FILTERING_END);
writel_relaxed(PHYS_OFFSET | 0x1,
sirfsoc_l2x_base + L2X0_ADDR_FILTERING_START);
writel_relaxed(0,
sirfsoc_l2x_base + L2X0_TAG_LATENCY_CTRL);
writel_relaxed(0,
sirfsoc_l2x_base + L2X0_DATA_LATENCY_CTRL);
np = of_find_matching_node(NULL, prima2_l2x0_ids);
if (np) {
pr_info("Initializing prima2 L2 cache\n");
return l2x0_of_init(0x40000, 0);
}
l2x0_init((void __iomem *)sirfsoc_l2x_base, 0x00040000,
0x00000000);
return 0;
}
early_initcall(sirfsoc_of_l2x_init);
early_initcall(sirfsoc_l2x0_init);
/*
* power management entry for CSR SiRFprimaII
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#include <linux/kernel.h>
#include <linux/suspend.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#include <linux/rtc/sirfsoc_rtciobrg.h>
#include <asm/suspend.h>
#include <asm/hardware/cache-l2x0.h>
#include "pm.h"
/*
* suspend asm codes will access these to make DRAM become self-refresh and
* system sleep
*/
u32 sirfsoc_pwrc_base;
void __iomem *sirfsoc_memc_base;
static void sirfsoc_set_wakeup_source(void)
{
u32 pwr_trigger_en_reg;
pwr_trigger_en_reg = sirfsoc_rtc_iobrg_readl(sirfsoc_pwrc_base +
SIRFSOC_PWRC_TRIGGER_EN);
#define X_ON_KEY_B (1 << 0)
sirfsoc_rtc_iobrg_writel(pwr_trigger_en_reg | X_ON_KEY_B,
sirfsoc_pwrc_base + SIRFSOC_PWRC_TRIGGER_EN);
}
static void sirfsoc_set_sleep_mode(u32 mode)
{
u32 sleep_mode = sirfsoc_rtc_iobrg_readl(sirfsoc_pwrc_base +
SIRFSOC_PWRC_PDN_CTRL);
sleep_mode &= ~(SIRFSOC_SLEEP_MODE_MASK << 1);
sleep_mode |= mode << 1;
sirfsoc_rtc_iobrg_writel(sleep_mode, sirfsoc_pwrc_base +
SIRFSOC_PWRC_PDN_CTRL);
}
static int sirfsoc_pre_suspend_power_off(void)
{
u32 wakeup_entry = virt_to_phys(cpu_resume);
sirfsoc_rtc_iobrg_writel(wakeup_entry, sirfsoc_pwrc_base +
SIRFSOC_PWRC_SCRATCH_PAD1);
sirfsoc_set_wakeup_source();
sirfsoc_set_sleep_mode(SIRFSOC_DEEP_SLEEP_MODE);
return 0;
}
static int sirfsoc_pm_enter(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_MEM:
sirfsoc_pre_suspend_power_off();
outer_flush_all();
outer_disable();
/* go zzz */
cpu_suspend(0, sirfsoc_finish_suspend);
outer_resume();
break;
default:
return -EINVAL;
}
return 0;
}
static const struct platform_suspend_ops sirfsoc_pm_ops = {
.enter = sirfsoc_pm_enter,
.valid = suspend_valid_only_mem,
};
static int __init sirfsoc_pm_init(void)
{
suspend_set_ops(&sirfsoc_pm_ops);
return 0;
}
late_initcall(sirfsoc_pm_init);
static const struct of_device_id pwrc_ids[] = {
{ .compatible = "sirf,prima2-pwrc" },
{}
};
static int __init sirfsoc_of_pwrc_init(void)
{
struct device_node *np;
np = of_find_matching_node(NULL, pwrc_ids);
if (!np)
panic("unable to find compatible pwrc node in dtb\n");
/*
* pwrc behind rtciobrg is not located in memory space
* though the property is named reg. reg only means base
* offset for pwrc. then of_iomap is not suitable here.
*/
if (of_property_read_u32(np, "reg", &sirfsoc_pwrc_base))
panic("unable to find base address of pwrc node in dtb\n");
of_node_put(np);
return 0;
}
postcore_initcall(sirfsoc_of_pwrc_init);
static const struct of_device_id memc_ids[] = {
{ .compatible = "sirf,prima2-memc" },
{}
};
static int __devinit sirfsoc_memc_probe(struct platform_device *op)
{
struct device_node *np = op->dev.of_node;
sirfsoc_memc_base = of_iomap(np, 0);
if (!sirfsoc_memc_base)
panic("unable to map memc registers\n");
return 0;
}
static struct platform_driver sirfsoc_memc_driver = {
.probe = sirfsoc_memc_probe,
.driver = {
.name = "sirfsoc-memc",
.owner = THIS_MODULE,
.of_match_table = memc_ids,
},
};
static int __init sirfsoc_memc_init(void)
{
return platform_driver_register(&sirfsoc_memc_driver);
}
postcore_initcall(sirfsoc_memc_init);
/*
* arch/arm/mach-prima2/pm.h
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#ifndef _MACH_PRIMA2_PM_H_
#define _MACH_PRIMA2_PM_H_
#define SIRFSOC_PWR_SLEEPFORCE 0x01
#define SIRFSOC_SLEEP_MODE_MASK 0x3
#define SIRFSOC_DEEP_SLEEP_MODE 0x1
#define SIRFSOC_PWRC_PDN_CTRL 0x0
#define SIRFSOC_PWRC_PON_OFF 0x4
#define SIRFSOC_PWRC_TRIGGER_EN 0x8
#define SIRFSOC_PWRC_PIN_STATUS 0x14
#define SIRFSOC_PWRC_SCRATCH_PAD1 0x18
#define SIRFSOC_PWRC_SCRATCH_PAD2 0x1C
#ifndef __ASSEMBLY__
extern int sirfsoc_finish_suspend(unsigned long);
#endif
#endif
/*
* RTC I/O Bridge interfaces for CSR SiRFprimaII
* ARM access the registers of SYSRTC, GPSRTC and PWRC through this module
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#define SIRFSOC_CPUIOBRG_CTRL 0x00
#define SIRFSOC_CPUIOBRG_WRBE 0x04
#define SIRFSOC_CPUIOBRG_ADDR 0x08
#define SIRFSOC_CPUIOBRG_DATA 0x0c
/*
* suspend asm codes will access this address to make system deepsleep
* after DRAM becomes self-refresh
*/
void __iomem *sirfsoc_rtciobrg_base;
static DEFINE_SPINLOCK(rtciobrg_lock);
/*
* symbols without lock are only used by suspend asm codes
* and these symbols are not exported too
*/
void sirfsoc_rtc_iobrg_wait_sync(void)
{
while (readl_relaxed(sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_CTRL))
cpu_relax();
}
void sirfsoc_rtc_iobrg_besyncing(void)
{
unsigned long flags;
spin_lock_irqsave(&rtciobrg_lock, flags);
sirfsoc_rtc_iobrg_wait_sync();
spin_unlock_irqrestore(&rtciobrg_lock, flags);
}
EXPORT_SYMBOL_GPL(sirfsoc_rtc_iobrg_besyncing);
u32 __sirfsoc_rtc_iobrg_readl(u32 addr)
{
sirfsoc_rtc_iobrg_wait_sync();
writel_relaxed(0x00, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_WRBE);
writel_relaxed(addr, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_ADDR);
writel_relaxed(0x01, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_CTRL);
sirfsoc_rtc_iobrg_wait_sync();
return readl_relaxed(sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_DATA);
}
u32 sirfsoc_rtc_iobrg_readl(u32 addr)
{
unsigned long flags, val;
spin_lock_irqsave(&rtciobrg_lock, flags);
val = __sirfsoc_rtc_iobrg_readl(addr);
spin_unlock_irqrestore(&rtciobrg_lock, flags);
return val;
}
EXPORT_SYMBOL_GPL(sirfsoc_rtc_iobrg_readl);
void sirfsoc_rtc_iobrg_pre_writel(u32 val, u32 addr)
{
sirfsoc_rtc_iobrg_wait_sync();
writel_relaxed(0xf1, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_WRBE);
writel_relaxed(addr, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_ADDR);
writel_relaxed(val, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_DATA);
}
void sirfsoc_rtc_iobrg_writel(u32 val, u32 addr)
{
unsigned long flags;
spin_lock_irqsave(&rtciobrg_lock, flags);
sirfsoc_rtc_iobrg_pre_writel(val, addr);
writel_relaxed(0x01, sirfsoc_rtciobrg_base + SIRFSOC_CPUIOBRG_CTRL);
sirfsoc_rtc_iobrg_wait_sync();
spin_unlock_irqrestore(&rtciobrg_lock, flags);
}
EXPORT_SYMBOL_GPL(sirfsoc_rtc_iobrg_writel);
static const struct of_device_id rtciobrg_ids[] = {
{ .compatible = "sirf,prima2-rtciobg" },
{}
};
static int __devinit sirfsoc_rtciobrg_probe(struct platform_device *op)
{
struct device_node *np = op->dev.of_node;
sirfsoc_rtciobrg_base = of_iomap(np, 0);
if (!sirfsoc_rtciobrg_base)
panic("unable to map rtc iobrg registers\n");
return 0;
}
static struct platform_driver sirfsoc_rtciobrg_driver = {
.probe = sirfsoc_rtciobrg_probe,
.driver = {
.name = "sirfsoc-rtciobrg",
.owner = THIS_MODULE,
.of_match_table = rtciobrg_ids,
},
};
static int __init sirfsoc_rtciobrg_init(void)
{
return platform_driver_register(&sirfsoc_rtciobrg_driver);
}
postcore_initcall(sirfsoc_rtciobrg_init);
MODULE_AUTHOR("Zhiwu Song <zhiwu.song@csr.com>, "
"Barry Song <baohua.song@csr.com>");
MODULE_DESCRIPTION("CSR SiRFprimaII rtc io bridge");
MODULE_LICENSE("GPL");
/*
* sleep mode for CSR SiRFprimaII
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#include <linux/linkage.h>
#include <asm/ptrace.h>
#include <asm/assembler.h>
#include "pm.h"
#define DENALI_CTL_22_OFF 0x58
#define DENALI_CTL_112_OFF 0x1c0
.text
ENTRY(sirfsoc_finish_suspend)
@ r5: mem controller
ldr r0, =sirfsoc_memc_base
ldr r5, [r0]
@ r6: pwrc base offset
ldr r0, =sirfsoc_pwrc_base
ldr r6, [r0]
@ r7: rtc iobrg controller
ldr r0, =sirfsoc_rtciobrg_base
ldr r7, [r0]
@ Read the power control register and set the
@ sleep force bit.
add r0, r6, #SIRFSOC_PWRC_PDN_CTRL
bl __sirfsoc_rtc_iobrg_readl
orr r0,r0,#SIRFSOC_PWR_SLEEPFORCE
add r1, r6, #SIRFSOC_PWRC_PDN_CTRL
bl sirfsoc_rtc_iobrg_pre_writel
mov r1, #0x1
@ read the MEM ctl register and set the self
@ refresh bit
ldr r2, [r5, #DENALI_CTL_22_OFF]
orr r2, r2, #0x1
@ Following code has to run from cache since
@ the RAM is going to self refresh mode
.align 5
str r2, [r5, #DENALI_CTL_22_OFF]
1:
ldr r4, [r5, #DENALI_CTL_112_OFF]
tst r4, #0x1
bne 1b
@ write SLEEPFORCE through rtc iobridge
str r1, [r7]
@ wait rtc io bridge sync
1:
ldr r3, [r7]
tst r3, #0x01
bne 1b
b .
......@@ -40,6 +40,17 @@
#define SIRFSOC_TIMER_LATCH_BIT BIT(0)
#define SIRFSOC_TIMER_REG_CNT 11
static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
SIRFSOC_TIMER_MATCH_0, SIRFSOC_TIMER_MATCH_1, SIRFSOC_TIMER_MATCH_2,
SIRFSOC_TIMER_MATCH_3, SIRFSOC_TIMER_MATCH_4, SIRFSOC_TIMER_MATCH_5,
SIRFSOC_TIMER_INT_EN, SIRFSOC_TIMER_WATCHDOG_EN, SIRFSOC_TIMER_DIV,
SIRFSOC_TIMER_LATCHED_LO, SIRFSOC_TIMER_LATCHED_HI,
};
static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
static void __iomem *sirfsoc_timer_base;
static void __init sirfsoc_of_timer_map(void);
......@@ -106,6 +117,27 @@ static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
}
}
static void sirfsoc_clocksource_suspend(struct clocksource *cs)
{
int i;
writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
}
static void sirfsoc_clocksource_resume(struct clocksource *cs)
{
int i;
for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
writel_relaxed(sirfsoc_timer_reg_val[i - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
writel_relaxed(sirfsoc_timer_reg_val[i - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
}
static struct clock_event_device sirfsoc_clockevent = {
.name = "sirfsoc_clockevent",
.rating = 200,
......@@ -120,6 +152,8 @@ static struct clocksource sirfsoc_clocksource = {
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.read = sirfsoc_timer_read,
.suspend = sirfsoc_clocksource_suspend,
.resume = sirfsoc_clocksource_resume,
};
static struct irqaction sirfsoc_timer_irq = {
......
......@@ -525,7 +525,6 @@ struct omap_hwmod {
char *clkdm_name;
struct clockdomain *clkdm;
char *vdd_name;
struct voltagedomain *voltdm;
struct omap_hwmod_ocp_if **masters; /* connect to *_IA */
struct omap_hwmod_ocp_if **slaves; /* connect to *_TA */
void *dev_attr;
......
/*
* OMAP Voltage Management Routines
*
* Copyright (C) 2011, Texas Instruments, Inc.
*
* 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.
*/
#ifndef __ARCH_ARM_OMAP_VOLTAGE_H
#define __ARCH_ARM_OMAP_VOLTAGE_H
struct voltagedomain;
struct voltagedomain *voltdm_lookup(const char *name);
int voltdm_scale(struct voltagedomain *voltdm, unsigned long target_volt);
unsigned long voltdm_get_voltage(struct voltagedomain *voltdm);
#endif
/*
* RTC I/O Bridge interfaces for CSR SiRFprimaII
* ARM access the registers of SYSRTC, GPSRTC and PWRC through this module
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#ifndef _SIRFSOC_RTC_IOBRG_H_
#define _SIRFSOC_RTC_IOBRG_H_
extern void sirfsoc_rtc_iobrg_besyncing(void);
extern u32 sirfsoc_rtc_iobrg_readl(u32 addr);
extern void sirfsoc_rtc_iobrg_writel(u32 val, u32 addr);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment