arm-soc: power management changes

These are various power management related changes, mainly concerning
 cpuidle on i.MX and OMAP, as well as a the move of the omap smartreflex
 driver to live in the power subsystem.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIVAwUAUA2dhWCrR//JCVInAQLuqRAA1FxxzAMTESs3/rpjnQmZUUKef4SuJwY2
 GBenXFLY5PlxgcrwTbKwepapu8IWfmw171+tRrrMWvQgtBwa+SefwFCaLcRkvRrs
 kNAHIVI+Gqm4/m6d+WC+ymJLOZdkcTHES+40eycxcjiIElGEMtiW5/qwh060GAgC
 YxtjoN9BKegjsDLPZdZghO855YUV8CKEg+q5kIYW0Q3Ci0POGvOhgvbI61K5w8z7
 fTdbFRDRBqy0BEx9noKTu8XIB/inwlyUY7N3bAv494TsU48kxMIb95FdSGiY/0yV
 1883wCacBYBNemWRvWBHNilSsDcuDmM3yNvdwi3JvQnzFBPc8uyze9wbPFOW4aQd
 Vhf+g8hjuHkw1xreWpO+nREysOjiiSzRUci2nT6aAQTcpWCacVTJ5sW7KOQ63nrH
 OQpe/fvm/qT8FKPDh/lcrqIUKrHfeFjZx7XlYjw7j0ZL+99mIpwuOql18mQee9G5
 OV6c0rfgeTnGLdc1kOlLPElkXe7SQ/GJK1JI1mA5BNYJlVKx+o0qVlcnRzY6bWaP
 dmSIA+9Bs/fglvmAQHT3u68zn5KfoTbnJWb0v5PQJfitEBdlugKG8nF9mVRIX70X
 EygOta8vApF9N20WhE2TLLaDhlrOmd4bOtRVdoO8pDVN/hsWIylnEu952ZBSZg3U
 9wF0Ydy2LP4=
 =tgT5
 -----END PGP SIGNATURE-----

Merge tag 'pm' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull arm-soc power management changes from Arnd Bergmann:
 "These are various power management related changes, mainly concerning
  cpuidle on i.MX and OMAP, as well as a the move of the omap
  smartreflex driver to live in the power subsystem."

Fix up conflicts in arch/arm/mach-{imx/mach-imx6q.c,omap2/prm2xxx_3xxx.h}

* tag 'pm' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (37 commits)
  ARM: OMAP2+: PM: fix IRQ_NOAUTOEN removal by mis-merge
  ARM: OMAP2+: do not allow SmartReflex to be built as a module
  ARM: OMAP2: Use hwmod to initialize mmc for 2420
  ARM: OMAP3: PM: cpuidle: optimize the clkdm idle latency in C1 state
  ARM: OMAP3: PM: cpuidle: optimize the PER latency in C1 state
  ARM: OMAP3: PM: cpuidle: default to C1 in next_valid_state
  ARM: OMAP3: PM: cleanup cam_pwrdm leftovers
  ARM: OMAP3: PM: call pre/post transition per powerdomain
  ARM: OMAP2+: powerdomain: allow pre/post transtion to be per pwrdm
  ARM: OMAP3: PM: Remove IO Daisychain control from cpuidle
  ARM: OMAP3PLUS: hwmod: reconfigure IO Daisychain during hwmod mux
  ARM: OMAP3+: PRM: Enable IO wake up
  ARM: OMAP4: PRM: Add IO Daisychain support
  ARM: OMAP3: PM: Move IO Daisychain function to omap3 prm file
  ARM: OMAP3: PM: correct enable/disable of daisy io chain
  ARM: OMAP2+: PRM: fix compile for OMAP4-only build
  W1: OMAP HDQ1W: use runtime PM
  ARM: OMAP2+: HDQ1W: use omap_device
  W1: OMAP HDQ1W: use 32-bit register accesses
  W1: OMAP HDQ1W: allow driver to be built on all OMAP2+
  ...
This commit is contained in:
Linus Torvalds 2012-07-23 17:43:53 -07:00
commit a5ebba6b54
46 changed files with 810 additions and 522 deletions

View file

@ -279,6 +279,7 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil,
clk_register_clkdev(clk[dummy], NULL, "imx-keypad");
clk_register_clkdev(clk[tve_gate], NULL, "imx-tve.0");
clk_register_clkdev(clk[ipu_di1_gate], "di1", "imx-tve.0");
clk_register_clkdev(clk[gpc_dvfs], "gpc_dvfs", NULL);
/* Set SDHC parents to be PLL2 */
clk_set_parent(clk[esdhc_a_sel], clk[pll2_sw]);
@ -336,7 +337,6 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
clk_register_clkdev(clk[mx51_mipi], "mipi_hsp", NULL);
clk_register_clkdev(clk[vpu_gate], NULL, "imx51-vpu.0");
clk_register_clkdev(clk[fec_gate], NULL, "imx27-fec.0");
clk_register_clkdev(clk[gpc_dvfs], "gpc_dvfs", NULL);
clk_register_clkdev(clk[ipu_gate], "bus", "imx51-ipu");
clk_register_clkdev(clk[ipu_di0_gate], "di0", "imx51-ipu");
clk_register_clkdev(clk[ipu_di1_gate], "di1", "imx51-ipu");

View file

@ -120,6 +120,7 @@ DT_MACHINE_START(IMX53_DT, "Freescale i.MX53 (Device Tree Support)")
.handle_irq = imx53_handle_irq,
.timer = &imx53_timer,
.init_machine = imx53_dt_init,
.init_late = imx53_init_late,
.dt_compat = imx53_dt_board_compat,
.restart = mxc_restart,
MACHINE_END

View file

@ -12,7 +12,9 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/cpuidle.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
@ -24,6 +26,7 @@
#include <linux/phy.h>
#include <linux/micrel_phy.h>
#include <linux/mfd/anatop.h>
#include <asm/cpuidle.h>
#include <asm/smp_twd.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/hardware/gic.h>
@ -31,8 +34,10 @@
#include <asm/mach/time.h>
#include <asm/system_misc.h>
#include <mach/common.h>
#include <mach/cpuidle.h>
#include <mach/hardware.h>
void imx6q_restart(char mode, const char *cmd)
{
struct device_node *np;
@ -169,6 +174,19 @@ static void __init imx6q_init_machine(void)
imx6q_usb_init();
}
static struct cpuidle_driver imx6q_cpuidle_driver = {
.name = "imx6q_cpuidle",
.owner = THIS_MODULE,
.en_core_tk_irqen = 1,
.states[0] = ARM_CPUIDLE_WFI_STATE,
.state_count = 1,
};
static void __init imx6q_init_late(void)
{
imx_cpuidle_init(&imx6q_cpuidle_driver);
}
static void __init imx6q_map_io(void)
{
imx_lluart_map_io();
@ -213,6 +231,7 @@ DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad (Device Tree)")
.handle_irq = imx6q_handle_irq,
.timer = &imx6q_timer,
.init_machine = imx6q_init_machine,
.init_late = imx6q_init_late,
.dt_compat = imx6q_dt_compat,
.restart = imx6q_restart,
MACHINE_END

View file

@ -267,5 +267,6 @@ MACHINE_START(MX53_ARD, "Freescale MX53 ARD Board")
.handle_irq = imx53_handle_irq,
.timer = &mx53_ard_timer,
.init_machine = mx53_ard_board_init,
.init_late = imx53_init_late,
.restart = mxc_restart,
MACHINE_END

View file

@ -174,5 +174,6 @@ MACHINE_START(MX53_EVK, "Freescale MX53 EVK Board")
.handle_irq = imx53_handle_irq,
.timer = &mx53_evk_timer,
.init_machine = mx53_evk_board_init,
.init_late = imx53_init_late,
.restart = mxc_restart,
MACHINE_END

View file

@ -316,5 +316,6 @@ MACHINE_START(MX53_LOCO, "Freescale MX53 LOCO Board")
.handle_irq = imx53_handle_irq,
.timer = &mx53_loco_timer,
.init_machine = mx53_loco_board_init,
.init_late = imx53_init_late,
.restart = mxc_restart,
MACHINE_END

View file

@ -163,5 +163,6 @@ MACHINE_START(MX53_SMD, "Freescale MX53 SMD Board")
.handle_irq = imx53_handle_irq,
.timer = &mx53_smd_timer,
.init_machine = mx53_smd_board_init,
.init_late = imx53_init_late,
.restart = mxc_restart,
MACHINE_END

View file

@ -16,7 +16,6 @@
#include <linux/clk.h>
#include <linux/pinctrl/machine.h>
#include <asm/system_misc.h>
#include <asm/mach/map.h>
#include <mach/hardware.h>
@ -24,24 +23,6 @@
#include <mach/devices-common.h>
#include <mach/iomux-v3.h>
static struct clk *gpc_dvfs_clk;
static void imx5_idle(void)
{
/* gpc clock is needed for SRPG */
if (gpc_dvfs_clk == NULL) {
gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs");
if (IS_ERR(gpc_dvfs_clk))
return;
clk_prepare(gpc_dvfs_clk);
}
clk_enable(gpc_dvfs_clk);
mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
if (!tzic_enable_wake())
cpu_do_idle();
clk_disable(gpc_dvfs_clk);
}
/*
* Define the MX50 memory map.
*/
@ -105,7 +86,6 @@ void __init imx51_init_early(void)
mxc_set_cpu_type(MXC_CPU_MX51);
mxc_iomux_v3_init(MX51_IO_ADDRESS(MX51_IOMUXC_BASE_ADDR));
mxc_arch_reset_init(MX51_IO_ADDRESS(MX51_WDOG1_BASE_ADDR));
arm_pm_idle = imx5_idle;
}
void __init imx53_init_early(void)
@ -243,4 +223,10 @@ void __init imx53_soc_init(void)
void __init imx51_init_late(void)
{
mx51_neon_fixup();
imx51_pm_init();
}
void __init imx53_init_late(void)
{
imx53_pm_init();
}

View file

@ -12,19 +12,30 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/export.h>
#include <asm/cacheflush.h>
#include <asm/system_misc.h>
#include <asm/tlbflush.h>
#include <mach/common.h>
#include <mach/cpuidle.h>
#include <mach/hardware.h>
#include "crm-regs-imx5.h"
static struct clk *gpc_dvfs_clk;
/*
* The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit.
* This is also the lowest power state possible without affecting
* non-cpu parts of the system. For these reasons, imx5 should default
* to always using this state for cpu idling. The PM_SUSPEND_STANDBY also
* uses this state and needs to take no action when registers remain confgiured
* for this state.
*/
#define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF
/*
* set cpu low power mode before WFI instruction. This function is called
* mx5 because it can be used for mx50, mx51, and mx53.
*/
void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
static void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
{
u32 plat_lpc, arm_srpgcr, ccm_clpcr;
u32 empgc0, empgc1;
@ -87,11 +98,6 @@ void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
}
}
static int mx5_suspend_prepare(void)
{
return clk_prepare_enable(gpc_dvfs_clk);
}
static int mx5_suspend_enter(suspend_state_t state)
{
switch (state) {
@ -99,7 +105,7 @@ static int mx5_suspend_enter(suspend_state_t state)
mx5_cpu_lp_set(STOP_POWER_OFF);
break;
case PM_SUSPEND_STANDBY:
mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
/* DEFAULT_IDLE_STATE already configured */
break;
default:
return -EINVAL;
@ -114,12 +120,10 @@ static int mx5_suspend_enter(suspend_state_t state)
__raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR);
}
cpu_do_idle();
return 0;
}
static void mx5_suspend_finish(void)
{
clk_disable_unprepare(gpc_dvfs_clk);
/* return registers to default idle state */
mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE);
return 0;
}
static int mx5_pm_valid(suspend_state_t state)
@ -129,25 +133,80 @@ static int mx5_pm_valid(suspend_state_t state)
static const struct platform_suspend_ops mx5_suspend_ops = {
.valid = mx5_pm_valid,
.prepare = mx5_suspend_prepare,
.enter = mx5_suspend_enter,
.finish = mx5_suspend_finish,
};
static int __init mx5_pm_init(void)
static inline int imx5_cpu_do_idle(void)
{
if (!cpu_is_mx51() && !cpu_is_mx53())
return 0;
int ret = tzic_enable_wake();
if (gpc_dvfs_clk == NULL)
gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs");
if (likely(!ret))
cpu_do_idle();
if (!IS_ERR(gpc_dvfs_clk)) {
if (cpu_is_mx51())
suspend_set_ops(&mx5_suspend_ops);
} else
return -EPERM;
return ret;
}
static void imx5_pm_idle(void)
{
imx5_cpu_do_idle();
}
static int imx5_cpuidle_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
int ret;
ret = imx5_cpu_do_idle();
if (ret < 0)
return ret;
return idx;
}
static struct cpuidle_driver imx5_cpuidle_driver = {
.name = "imx5_cpuidle",
.owner = THIS_MODULE,
.en_core_tk_irqen = 1,
.states[0] = {
.enter = imx5_cpuidle_enter,
.exit_latency = 2,
.target_residency = 1,
.flags = CPUIDLE_FLAG_TIME_VALID,
.name = "IMX5 SRPG",
.desc = "CPU state retained,powered off",
},
.state_count = 1,
};
static int __init imx5_pm_common_init(void)
{
int ret;
struct clk *gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs");
if (IS_ERR(gpc_dvfs_clk))
return PTR_ERR(gpc_dvfs_clk);
ret = clk_prepare_enable(gpc_dvfs_clk);
if (ret)
return ret;
arm_pm_idle = imx5_pm_idle;
/* Set the registers to the default cpu idle state. */
mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE);
imx_cpuidle_init(&imx5_cpuidle_driver);
return 0;
}
device_initcall(mx5_pm_init);
void __init imx51_pm_init(void)
{
int ret = imx5_pm_common_init();
if (!ret)
suspend_set_ops(&mx5_suspend_ops);
}
void __init imx53_pm_init(void)
{
imx5_pm_common_init();
}

View file

@ -74,8 +74,9 @@ obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o
obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o omap-mpuss-lowpower.o
obj-$(CONFIG_SOC_OMAP5) += omap-mpuss-lowpower.o
obj-$(CONFIG_PM_DEBUG) += pm-debug.o
obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o
obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o
obj-$(CONFIG_POWER_AVS_OMAP) += sr_device.o
obj-$(CONFIG_POWER_AVS_OMAP_CLASS3) += smartreflex-class3.o
AFLAGS_sleep24xx.o :=-Wa,-march=armv6
AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec)

View file

@ -75,20 +75,6 @@ static struct omap3_idle_statedata omap3_idle_data[] = {
static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;
static int _cpuidle_allow_idle(struct powerdomain *pwrdm,
struct clockdomain *clkdm)
{
clkdm_allow_idle(clkdm);
return 0;
}
static int _cpuidle_deny_idle(struct powerdomain *pwrdm,
struct clockdomain *clkdm)
{
clkdm_deny_idle(clkdm);
return 0;
}
static int __omap3_enter_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
@ -106,8 +92,8 @@ static int __omap3_enter_idle(struct cpuidle_device *dev,
/* Deny idle for C1 */
if (index == 0) {
pwrdm_for_each_clkdm(mpu_pd, _cpuidle_deny_idle);
pwrdm_for_each_clkdm(core_pd, _cpuidle_deny_idle);
clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]);
clkdm_deny_idle(core_pd->pwrdm_clkdms[0]);
}
/*
@ -129,8 +115,8 @@ static int __omap3_enter_idle(struct cpuidle_device *dev,
/* Re-allow idle for C1 */
if (index == 0) {
pwrdm_for_each_clkdm(mpu_pd, _cpuidle_allow_idle);
pwrdm_for_each_clkdm(core_pd, _cpuidle_allow_idle);
clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]);
clkdm_allow_idle(core_pd->pwrdm_clkdms[0]);
}
return_sleep_time:
@ -176,7 +162,7 @@ static int next_valid_state(struct cpuidle_device *dev,
u32 mpu_deepest_state = PWRDM_POWER_RET;
u32 core_deepest_state = PWRDM_POWER_RET;
int idx;
int next_index = -1;
int next_index = 0; /* C1 is the default value */
if (enable_off_mode) {
mpu_deepest_state = PWRDM_POWER_OFF;
@ -207,12 +193,6 @@ static int next_valid_state(struct cpuidle_device *dev,
}
}
/*
* C1 is always valid.
* So, no need to check for 'next_index == -1' outside
* this loop.
*/
return next_index;
}
@ -226,23 +206,22 @@ static int next_valid_state(struct cpuidle_device *dev,
* the device to the specified or a safer state.
*/
static int omap3_enter_idle_bm(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
struct cpuidle_driver *drv,
int index)
{
int new_state_idx;
u32 core_next_state, per_next_state = 0, per_saved_state = 0, cam_state;
u32 core_next_state, per_next_state = 0, per_saved_state = 0;
struct omap3_idle_statedata *cx;
int ret;
/*
* Prevent idle completely if CAM is active.
* Use only C1 if CAM is active.
* CAM does not have wakeup capability in OMAP3.
*/
cam_state = pwrdm_read_pwrst(cam_pd);
if (cam_state == PWRDM_POWER_ON) {
if (pwrdm_read_pwrst(cam_pd) == PWRDM_POWER_ON)
new_state_idx = drv->safe_state_index;
goto select_state;
}
else
new_state_idx = next_valid_state(dev, drv, index);
/*
* FIXME: we currently manage device-specific idle states
@ -252,24 +231,28 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
* its own code.
*/
/*
* Prevent PER off if CORE is not in retention or off as this
* would disable PER wakeups completely.
*/
cx = &omap3_idle_data[index];
/* Program PER state */
cx = &omap3_idle_data[new_state_idx];
core_next_state = cx->core_state;
per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd);
if ((per_next_state == PWRDM_POWER_OFF) &&
(core_next_state > PWRDM_POWER_RET))
per_next_state = PWRDM_POWER_RET;
if (new_state_idx == 0) {
/* In C1 do not allow PER state lower than CORE state */
if (per_next_state < core_next_state)
per_next_state = core_next_state;
} else {
/*
* Prevent PER OFF if CORE is not in RETention or OFF as this
* would disable PER wakeups completely.
*/
if ((per_next_state == PWRDM_POWER_OFF) &&
(core_next_state > PWRDM_POWER_RET))
per_next_state = PWRDM_POWER_RET;
}
/* Are we changing PER target state? */
if (per_next_state != per_saved_state)
pwrdm_set_next_pwrst(per_pd, per_next_state);
new_state_idx = next_valid_state(dev, drv, index);
select_state:
ret = omap3_enter_idle(dev, drv, new_state_idx);
/* Restore original PER state if it was modified */
@ -286,7 +269,7 @@ struct cpuidle_driver omap3_idle_driver = {
.owner = THIS_MODULE,
.states = {
{
.enter = omap3_enter_idle,
.enter = omap3_enter_idle_bm,
.exit_latency = 2 + 2,
.target_residency = 5,
.flags = CPUIDLE_FLAG_TIME_VALID,

View file

@ -27,7 +27,6 @@
#include "iomap.h"
#include <plat/board.h>
#include <plat/mmc.h>
#include <plat/dma.h>
#include <plat/omap_hwmod.h>
#include <plat/omap_device.h>
@ -603,112 +602,6 @@ static inline void omap_init_aes(void) { }
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
static inline void omap242x_mmc_mux(struct omap_mmc_platform_data
*mmc_controller)
{
if ((mmc_controller->slots[0].switch_pin > 0) && \
(mmc_controller->slots[0].switch_pin < OMAP_MAX_GPIO_LINES))
omap_mux_init_gpio(mmc_controller->slots[0].switch_pin,
OMAP_PIN_INPUT_PULLUP);
if ((mmc_controller->slots[0].gpio_wp > 0) && \
(mmc_controller->slots[0].gpio_wp < OMAP_MAX_GPIO_LINES))
omap_mux_init_gpio(mmc_controller->slots[0].gpio_wp,
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc_cmd", 0);
omap_mux_init_signal("sdmmc_clki", 0);
omap_mux_init_signal("sdmmc_clko", 0);
omap_mux_init_signal("sdmmc_dat0", 0);
omap_mux_init_signal("sdmmc_dat_dir0", 0);
omap_mux_init_signal("sdmmc_cmd_dir", 0);
if (mmc_controller->slots[0].caps & MMC_CAP_4_BIT_DATA) {
omap_mux_init_signal("sdmmc_dat1", 0);
omap_mux_init_signal("sdmmc_dat2", 0);
omap_mux_init_signal("sdmmc_dat3", 0);
omap_mux_init_signal("sdmmc_dat_dir1", 0);
omap_mux_init_signal("sdmmc_dat_dir2", 0);
omap_mux_init_signal("sdmmc_dat_dir3", 0);
}
/*
* Use internal loop-back in MMC/SDIO Module Input Clock
* selection
*/
if (mmc_controller->slots[0].internal_clock) {
u32 v = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
v |= (1 << 24);
omap_ctrl_writel(v, OMAP2_CONTROL_DEVCONF0);
}
}
void __init omap242x_init_mmc(struct omap_mmc_platform_data **mmc_data)
{
struct platform_device *pdev;
struct omap_hwmod *oh;
int id = 0;
char *oh_name = "msdi1";
char *dev_name = "mmci-omap";
if (!mmc_data[0]) {
pr_err("%s fails: Incomplete platform data\n", __func__);
return;
}
omap242x_mmc_mux(mmc_data[0]);
oh = omap_hwmod_lookup(oh_name);
if (!oh) {
pr_err("Could not look up %s\n", oh_name);
return;
}
pdev = omap_device_build(dev_name, id, oh, mmc_data[0],
sizeof(struct omap_mmc_platform_data), NULL, 0, 0);
if (IS_ERR(pdev))
WARN(1, "Can'd build omap_device for %s:%s.\n",
dev_name, oh->name);
}
#endif
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_HDQ_MASTER_OMAP) || defined(CONFIG_HDQ_MASTER_OMAP_MODULE)
#define OMAP_HDQ_BASE 0x480B2000
static struct resource omap_hdq_resources[] = {
{
.start = OMAP_HDQ_BASE,
.end = OMAP_HDQ_BASE + 0x1C,
.flags = IORESOURCE_MEM,
},
{
.start = INT_24XX_HDQ_IRQ,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device omap_hdq_dev = {
.name = "omap_hdq",
.id = 0,
.dev = {
.platform_data = NULL,
},
.num_resources = ARRAY_SIZE(omap_hdq_resources),
.resource = omap_hdq_resources,
};
static inline void omap_hdq_init(void)
{
if (cpu_is_omap2420())
return;
platform_device_register(&omap_hdq_dev);
}
#else
static inline void omap_hdq_init(void) {}
#endif
/*---------------------------------------------------------------------------*/
#if defined(CONFIG_VIDEO_OMAP2_VOUT) || \
defined(CONFIG_VIDEO_OMAP2_VOUT_MODULE)
#if defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE)
@ -753,7 +646,6 @@ static int __init omap2_init_devices(void)
omap_init_mcspi();
}
omap_init_pmu();
omap_hdq_init();
omap_init_sti();
omap_init_sham();
omap_init_aes();

View file

@ -22,7 +22,13 @@
* 02110-1301 USA
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <plat/omap_hwmod.h>
#include <plat/omap_device.h>
#include <plat/hdq1w.h>
#include "common.h"
@ -70,3 +76,23 @@ int omap_hdq1w_reset(struct omap_hwmod *oh)
return 0;
}
static int __init omap_init_hdq(void)
{
int id = -1;
struct platform_device *pdev;
struct omap_hwmod *oh;
char *oh_name = "hdq1w";
char *devname = "omap_hdq";
oh = omap_hwmod_lookup(oh_name);
if (!oh)
return 0;
pdev = omap_device_build(devname, id, oh, NULL, 0, NULL, 0, 0);
WARN(IS_ERR(pdev), "Can't build omap_device for %s:%s.\n",
devname, oh->name);
return 0;
}
arch_initcall(omap_init_hdq);

View file

@ -22,11 +22,15 @@
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <plat/omap_hwmod.h>
#include <plat/omap_device.h>
#include <plat/mmc.h>
#include "common.h"
#include "control.h"
#include "mux.h"
/*
* MSDI_CON_OFFSET: offset in bytes of the MSDI IP block's CON register
@ -86,3 +90,72 @@ int omap_msdi_reset(struct omap_hwmod *oh)
return 0;
}
#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
static inline void omap242x_mmc_mux(struct omap_mmc_platform_data
*mmc_controller)
{
if ((mmc_controller->slots[0].switch_pin > 0) && \
(mmc_controller->slots[0].switch_pin < OMAP_MAX_GPIO_LINES))
omap_mux_init_gpio(mmc_controller->slots[0].switch_pin,
OMAP_PIN_INPUT_PULLUP);
if ((mmc_controller->slots[0].gpio_wp > 0) && \
(mmc_controller->slots[0].gpio_wp < OMAP_MAX_GPIO_LINES))
omap_mux_init_gpio(mmc_controller->slots[0].gpio_wp,
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc_cmd", 0);
omap_mux_init_signal("sdmmc_clki", 0);
omap_mux_init_signal("sdmmc_clko", 0);
omap_mux_init_signal("sdmmc_dat0", 0);
omap_mux_init_signal("sdmmc_dat_dir0", 0);
omap_mux_init_signal("sdmmc_cmd_dir", 0);
if (mmc_controller->slots[0].caps & MMC_CAP_4_BIT_DATA) {
omap_mux_init_signal("sdmmc_dat1", 0);
omap_mux_init_signal("sdmmc_dat2", 0);
omap_mux_init_signal("sdmmc_dat3", 0);
omap_mux_init_signal("sdmmc_dat_dir1", 0);
omap_mux_init_signal("sdmmc_dat_dir2", 0);
omap_mux_init_signal("sdmmc_dat_dir3", 0);
}
/*
* Use internal loop-back in MMC/SDIO Module Input Clock
* selection
*/
if (mmc_controller->slots[0].internal_clock) {
u32 v = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
v |= (1 << 24);
omap_ctrl_writel(v, OMAP2_CONTROL_DEVCONF0);
}
}
void __init omap242x_init_mmc(struct omap_mmc_platform_data **mmc_data)
{
struct platform_device *pdev;
struct omap_hwmod *oh;
int id = 0;
char *oh_name = "msdi1";
char *dev_name = "mmci-omap";
if (!mmc_data[0]) {
pr_err("%s fails: Incomplete platform data\n", __func__);
return;
}
omap242x_mmc_mux(mmc_data[0]);
oh = omap_hwmod_lookup(oh_name);
if (!oh) {
pr_err("Could not look up %s\n", oh_name);
return;
}
pdev = omap_device_build(dev_name, id, oh, mmc_data[0],
sizeof(struct omap_mmc_platform_data), NULL, 0, 0);
if (IS_ERR(pdev))
WARN(1, "Can'd build omap_device for %s:%s.\n",
dev_name, oh->name);
}
#endif

View file

@ -255,7 +255,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
return -ENXIO;
}
pwrdm_pre_transition();
pwrdm_pre_transition(NULL);
/*
* Check MPUSS next state and save interrupt controller if needed.
@ -287,7 +287,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
wakeup_cpu = smp_processor_id();
set_cpu_next_pwrst(wakeup_cpu, PWRDM_POWER_ON);
pwrdm_post_transition();
pwrdm_post_transition(NULL);
return 0;
}

View file

@ -153,6 +153,7 @@
#include "prm44xx.h"
#include "prminst44xx.h"
#include "mux.h"
#include "pm.h"
/* Maximum microseconds to wait for OMAP module to softreset */
#define MAX_MODULE_SOFTRESET_WAIT 10000
@ -197,6 +198,9 @@ static LIST_HEAD(omap_hwmod_list);
/* mpu_oh: used to add/remove MPU initiator from sleepdep list */
static struct omap_hwmod *mpu_oh;
/* io_chain_lock: used to serialize reconfigurations of the I/O chain */
static DEFINE_SPINLOCK(io_chain_lock);
/*
* linkspace: ptr to a buffer that struct omap_hwmod_link records are
* allocated from - used to reduce the number of small memory
@ -1758,6 +1762,32 @@ static int _reset(struct omap_hwmod *oh)
return r;
}
/**
* _reconfigure_io_chain - clear any I/O chain wakeups and reconfigure chain
*
* Call the appropriate PRM function to clear any logged I/O chain
* wakeups and to reconfigure the chain. This apparently needs to be
* done upon every mux change. Since hwmods can be concurrently
* enabled and idled, hold a spinlock around the I/O chain
* reconfiguration sequence. No return value.
*
* XXX When the PRM code is moved to drivers, this function can be removed,
* as the PRM infrastructure should abstract this.
*/
static void _reconfigure_io_chain(void)
{
unsigned long flags;
spin_lock_irqsave(&io_chain_lock, flags);
if (cpu_is_omap34xx() && omap3_has_io_chain_ctrl())
omap3xxx_prm_reconfigure_io_chain();
else if (cpu_is_omap44xx())
omap44xx_prm_reconfigure_io_chain();
spin_unlock_irqrestore(&io_chain_lock, flags);
}
/**
* _enable - enable an omap_hwmod
* @oh: struct omap_hwmod *
@ -1814,8 +1844,10 @@ static int _enable(struct omap_hwmod *oh)
/* Mux pins for device runtime if populated */
if (oh->mux && (!oh->mux->enabled ||
((oh->_state == _HWMOD_STATE_IDLE) &&
oh->mux->pads_dynamic)))
oh->mux->pads_dynamic))) {
omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
_reconfigure_io_chain();
}
_add_initiator_dep(oh, mpu_oh);
@ -1907,8 +1939,10 @@ static int _idle(struct omap_hwmod *oh)
clkdm_hwmod_disable(oh->clkdm, oh);
/* Mux pins for device idle if populated */
if (oh->mux && oh->mux->pads_dynamic)
if (oh->mux && oh->mux->pads_dynamic) {
omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE);
_reconfigure_io_chain();
}
oh->_state = _HWMOD_STATE_IDLE;

View file

@ -14,6 +14,8 @@
*
* XXX these should be marked initdata for multi-OMAP kernels
*/
#include <linux/power/smartreflex.h>
#include <plat/omap_hwmod.h>
#include <mach/irqs.h>
#include <plat/cpu.h>
@ -29,8 +31,6 @@
#include <plat/dmtimer.h>
#include "omap_hwmod_common_data.h"
#include "smartreflex.h"
#include "prm-regbits-34xx.h"
#include "cm-regbits-34xx.h"
#include "wd_timer.h"
@ -1357,7 +1357,7 @@ static struct omap_hwmod_irq_info omap3_smartreflex_mpu_irqs[] = {
};
static struct omap_hwmod omap34xx_sr1_hwmod = {
.name = "sr1",
.name = "smartreflex_mpu_iva",
.class = &omap34xx_smartreflex_hwmod_class,
.main_clk = "sr1_fck",
.prcm = {
@ -1375,7 +1375,7 @@ static struct omap_hwmod omap34xx_sr1_hwmod = {
};
static struct omap_hwmod omap36xx_sr1_hwmod = {
.name = "sr1",
.name = "smartreflex_mpu_iva",
.class = &omap36xx_smartreflex_hwmod_class,
.main_clk = "sr1_fck",
.prcm = {
@ -1402,7 +1402,7 @@ static struct omap_hwmod_irq_info omap3_smartreflex_core_irqs[] = {
};
static struct omap_hwmod omap34xx_sr2_hwmod = {
.name = "sr2",
.name = "smartreflex_core",
.class = &omap34xx_smartreflex_hwmod_class,
.main_clk = "sr2_fck",
.prcm = {
@ -1420,7 +1420,7 @@ static struct omap_hwmod omap34xx_sr2_hwmod = {
};
static struct omap_hwmod omap36xx_sr2_hwmod = {
.name = "sr2",
.name = "smartreflex_core",
.class = &omap36xx_smartreflex_hwmod_class,
.main_clk = "sr2_fck",
.prcm = {

View file

@ -19,6 +19,7 @@
*/
#include <linux/io.h>
#include <linux/power/smartreflex.h>
#include <plat/omap_hwmod.h>
#include <plat/cpu.h>
@ -32,8 +33,6 @@
#include <plat/common.h>
#include "omap_hwmod_common_data.h"
#include "smartreflex.h"
#include "cm1_44xx.h"
#include "cm2_44xx.h"
#include "prm44xx.h"

View file

@ -101,7 +101,7 @@ extern void enable_omap3630_toggle_l2_on_restore(void);
static inline void enable_omap3630_toggle_l2_on_restore(void) { }
#endif /* defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP3) */
#ifdef CONFIG_OMAP_SMARTREFLEX
#ifdef CONFIG_POWER_AVS_OMAP
extern int omap_devinit_smartreflex(void);
extern void omap_enable_smartreflex_on_init(void);
#else

View file

@ -70,34 +70,6 @@ void (*omap3_do_wfi_sram)(void);
static struct powerdomain *mpu_pwrdm, *neon_pwrdm;
static struct powerdomain *core_pwrdm, *per_pwrdm;
static struct powerdomain *cam_pwrdm;
static void omap3_enable_io_chain(void)
{
int timeout = 0;
omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD,
PM_WKEN);
/* Do a readback to assure write has been done */
omap2_prm_read_mod_reg(WKUP_MOD, PM_WKEN);
while (!(omap2_prm_read_mod_reg(WKUP_MOD, PM_WKEN) &
OMAP3430_ST_IO_CHAIN_MASK)) {
timeout++;
if (timeout > 1000) {
pr_err("Wake up daisy chain activation failed.\n");
return;
}
omap2_prm_set_mod_reg_bits(OMAP3430_ST_IO_CHAIN_MASK,
WKUP_MOD, PM_WKEN);
}
}
static void omap3_disable_io_chain(void)
{
omap2_prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD,
PM_WKEN);
}
static void omap3_core_save_context(void)
{
@ -299,24 +271,22 @@ void omap_sram_idle(void)
/* Enable IO-PAD and IO-CHAIN wakeups */
per_next_state = pwrdm_read_next_pwrst(per_pwrdm);
core_next_state = pwrdm_read_next_pwrst(core_pwrdm);
if (omap3_has_io_wakeup() &&
(per_next_state < PWRDM_POWER_ON ||
core_next_state < PWRDM_POWER_ON)) {
omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_MASK, WKUP_MOD, PM_WKEN);
if (omap3_has_io_chain_ctrl())
omap3_enable_io_chain();
}
pwrdm_pre_transition();
if (mpu_next_state < PWRDM_POWER_ON) {
pwrdm_pre_transition(mpu_pwrdm);
pwrdm_pre_transition(neon_pwrdm);
}
/* PER */
if (per_next_state < PWRDM_POWER_ON) {
pwrdm_pre_transition(per_pwrdm);
per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0;
omap2_gpio_prepare_for_idle(per_going_off);
}
/* CORE */
if (core_next_state < PWRDM_POWER_ON) {
pwrdm_pre_transition(core_pwrdm);
if (core_next_state == PWRDM_POWER_OFF) {
omap3_core_save_context();
omap3_cm_save_context();
@ -369,26 +339,20 @@ void omap_sram_idle(void)
omap2_prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF_MASK,
OMAP3430_GR_MOD,
OMAP3_PRM_VOLTCTRL_OFFSET);
pwrdm_post_transition(core_pwrdm);
}
omap3_intc_resume_idle();
pwrdm_post_transition();
/* PER */
if (per_next_state < PWRDM_POWER_ON)
if (per_next_state < PWRDM_POWER_ON) {
omap2_gpio_resume_after_idle();
/* Disable IO-PAD and IO-CHAIN wakeup */
if (omap3_has_io_wakeup() &&
(per_next_state < PWRDM_POWER_ON ||
core_next_state < PWRDM_POWER_ON)) {
omap2_prm_clear_mod_reg_bits(OMAP3430_EN_IO_MASK, WKUP_MOD,
PM_WKEN);
if (omap3_has_io_chain_ctrl())
omap3_disable_io_chain();
pwrdm_post_transition(per_pwrdm);
}
clkdm_allow_idle(mpu_pwrdm->pwrdm_clkdms[0]);
if (mpu_next_state < PWRDM_POWER_ON) {
pwrdm_post_transition(mpu_pwrdm);
pwrdm_post_transition(neon_pwrdm);
}
}
static void omap3_pm_idle(void)
@ -754,7 +718,6 @@ int __init omap3_pm_init(void)
neon_pwrdm = pwrdm_lookup("neon_pwrdm");
per_pwrdm = pwrdm_lookup("per_pwrdm");
core_pwrdm = pwrdm_lookup("core_pwrdm");
cam_pwrdm = pwrdm_lookup("cam_pwrdm");
neon_clkdm = clkdm_lookup("neon_clkdm");
mpu_clkdm = clkdm_lookup("mpu_clkdm");

View file

@ -985,15 +985,23 @@ int pwrdm_state_switch(struct powerdomain *pwrdm)
return ret;
}
int pwrdm_pre_transition(void)
int pwrdm_pre_transition(struct powerdomain *pwrdm)
{
pwrdm_for_each(_pwrdm_pre_transition_cb, NULL);
if (pwrdm)
_pwrdm_pre_transition_cb(pwrdm, NULL);
else
pwrdm_for_each(_pwrdm_pre_transition_cb, NULL);
return 0;
}
int pwrdm_post_transition(void)
int pwrdm_post_transition(struct powerdomain *pwrdm)
{
pwrdm_for_each(_pwrdm_post_transition_cb, NULL);
if (pwrdm)
_pwrdm_post_transition_cb(pwrdm, NULL);
else
pwrdm_for_each(_pwrdm_post_transition_cb, NULL);
return 0;
}

View file

@ -230,8 +230,8 @@ bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm);
int pwrdm_wait_transition(struct powerdomain *pwrdm);
int pwrdm_state_switch(struct powerdomain *pwrdm);
int pwrdm_pre_transition(void);
int pwrdm_post_transition(void);
int pwrdm_pre_transition(struct powerdomain *pwrdm);
int pwrdm_post_transition(struct powerdomain *pwrdm);
int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
int pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm);

View file

@ -410,6 +410,14 @@
*/
#define MAX_MODULE_HARDRESET_WAIT 10000
/*
* Maximum time(us) it takes to output the signal WUCLKOUT of the last
* pad of the I/O ring after asserting WUCLKIN high. Tero measured
* the actual time at 7 to 8 microseconds on OMAP3 and 2 to 4
* microseconds on OMAP4, so this timeout may be too high.
*/
#define MAX_IOPAD_LATCH_TIME 100
# ifndef __ASSEMBLER__
extern void __iomem *prm_base;
extern void __iomem *cm_base;

View file

@ -302,11 +302,59 @@ void omap3xxx_prm_restore_irqen(u32 *saved_mask)
OMAP3_PRM_IRQENABLE_MPU_OFFSET);
}
/**
* omap3xxx_prm_reconfigure_io_chain - clear latches and reconfigure I/O chain
*
* Clear any previously-latched I/O wakeup events and ensure that the
* I/O wakeup gates are aligned with the current mux settings. Works
* by asserting WUCLKIN, waiting for WUCLKOUT to be asserted, and then
* deasserting WUCLKIN and clearing the ST_IO_CHAIN WKST bit. No
* return value.
*/
void omap3xxx_prm_reconfigure_io_chain(void)
{
int i = 0;
omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD,
PM_WKEN);
omap_test_timeout(omap2_prm_read_mod_reg(WKUP_MOD, PM_WKST) &
OMAP3430_ST_IO_CHAIN_MASK,
MAX_IOPAD_LATCH_TIME, i);
if (i == MAX_IOPAD_LATCH_TIME)
pr_warn("PRM: I/O chain clock line assertion timed out\n");
omap2_prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD,
PM_WKEN);
omap2_prm_set_mod_reg_bits(OMAP3430_ST_IO_CHAIN_MASK, WKUP_MOD,
PM_WKST);
omap2_prm_read_mod_reg(WKUP_MOD, PM_WKST);
}
/**
* omap3xxx_prm_enable_io_wakeup - enable wakeup events from I/O wakeup latches
*
* Activates the I/O wakeup event latches and allows events logged by
* those latches to signal a wakeup event to the PRCM. For I/O
* wakeups to occur, WAKEUPENABLE bits must be set in the pad mux
* registers, and omap3xxx_prm_reconfigure_io_chain() must be called.
* No return value.
*/
static void __init omap3xxx_prm_enable_io_wakeup(void)
{
if (omap3_has_io_wakeup())
omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_MASK, WKUP_MOD,
PM_WKEN);
}
static int __init omap3xxx_prcm_init(void)
{
int ret = 0;
if (cpu_is_omap34xx()) {
omap3xxx_prm_enable_io_wakeup();
ret = omap_prcm_register_chain_handler(&omap3_prcm_irq_setup);
if (!ret)
irq_set_status_flags(omap_prcm_event_to_irq("io"),

View file

@ -253,12 +253,15 @@ 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);
extern void omap3xxx_prm_reconfigure_io_chain(void);
/* PRM interrupt-related functions */
extern void omap3xxx_prm_read_pending_irqs(unsigned long *events);
extern void omap3xxx_prm_ocp_barrier(void);
extern void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask);
extern void omap3xxx_prm_restore_irqen(u32 *saved_mask);
#endif
#endif /* __ASSEMBLER */
/*
* Bits common to specific registers

View file

@ -233,10 +233,71 @@ void omap44xx_prm_restore_irqen(u32 *saved_mask)
OMAP4_PRM_IRQENABLE_MPU_2_OFFSET);
}
/**
* omap44xx_prm_reconfigure_io_chain - clear latches and reconfigure I/O chain
*
* Clear any previously-latched I/O wakeup events and ensure that the
* I/O wakeup gates are aligned with the current mux settings. Works
* by asserting WUCLKIN, waiting for WUCLKOUT to be asserted, and then
* deasserting WUCLKIN and waiting for WUCLKOUT to be deasserted.
* No return value. XXX Are the final two steps necessary?
*/
void omap44xx_prm_reconfigure_io_chain(void)
{
int i = 0;
/* Trigger WUCLKIN enable */
omap4_prm_rmw_inst_reg_bits(OMAP4430_WUCLK_CTRL_MASK,
OMAP4430_WUCLK_CTRL_MASK,
OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_IO_PMCTRL_OFFSET);
omap_test_timeout(
(((omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_IO_PMCTRL_OFFSET) &
OMAP4430_WUCLK_STATUS_MASK) >>
OMAP4430_WUCLK_STATUS_SHIFT) == 1),
MAX_IOPAD_LATCH_TIME, i);
if (i == MAX_IOPAD_LATCH_TIME)
pr_warn("PRM: I/O chain clock line assertion timed out\n");
/* Trigger WUCLKIN disable */
omap4_prm_rmw_inst_reg_bits(OMAP4430_WUCLK_CTRL_MASK, 0x0,
OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_IO_PMCTRL_OFFSET);
omap_test_timeout(
(((omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_IO_PMCTRL_OFFSET) &
OMAP4430_WUCLK_STATUS_MASK) >>
OMAP4430_WUCLK_STATUS_SHIFT) == 0),
MAX_IOPAD_LATCH_TIME, i);
if (i == MAX_IOPAD_LATCH_TIME)
pr_warn("PRM: I/O chain clock line deassertion timed out\n");
return;
}
/**
* omap44xx_prm_enable_io_wakeup - enable wakeup events from I/O wakeup latches
*
* Activates the I/O wakeup event latches and allows events logged by
* those latches to signal a wakeup event to the PRCM. For I/O wakeups
* to occur, WAKEUPENABLE bits must be set in the pad mux registers, and
* omap44xx_prm_reconfigure_io_chain() must be called. No return value.
*/
static void __init omap44xx_prm_enable_io_wakeup(void)
{
omap4_prm_rmw_inst_reg_bits(OMAP4430_GLOBAL_WUEN_MASK,
OMAP4430_GLOBAL_WUEN_MASK,
OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_IO_PMCTRL_OFFSET);
}
static int __init omap4xxx_prcm_init(void)
{
if (cpu_is_omap44xx())
if (cpu_is_omap44xx()) {
omap44xx_prm_enable_io_wakeup();
return omap_prcm_register_chain_handler(&omap4_prcm_irq_setup);
}
return 0;
}
subsys_initcall(omap4xxx_prcm_init);

View file

@ -763,6 +763,8 @@ 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);
extern void omap44xx_prm_reconfigure_io_chain(void);
/* PRM interrupt-related functions */
extern void omap44xx_prm_read_pending_irqs(unsigned long *events);
extern void omap44xx_prm_ocp_barrier(void);

View file

@ -11,36 +11,37 @@
* published by the Free Software Foundation.
*/
#include "smartreflex.h"
#include <linux/power/smartreflex.h>
#include "voltage.h"
static int sr_class3_enable(struct voltagedomain *voltdm)
static int sr_class3_enable(struct omap_sr *sr)
{
unsigned long volt = voltdm_get_voltage(voltdm);
unsigned long volt = voltdm_get_voltage(sr->voltdm);
if (!volt) {
pr_warning("%s: Curr voltage unknown. Cannot enable sr_%s\n",
__func__, voltdm->name);
pr_warning("%s: Curr voltage unknown. Cannot enable %s\n",
__func__, sr->name);
return -ENODATA;
}
omap_vp_enable(voltdm);
return sr_enable(voltdm, volt);
omap_vp_enable(sr->voltdm);
return sr_enable(sr->voltdm, volt);
}
static int sr_class3_disable(struct voltagedomain *voltdm, int is_volt_reset)
static int sr_class3_disable(struct omap_sr *sr, int is_volt_reset)
{
sr_disable_errgen(voltdm);
omap_vp_disable(voltdm);
sr_disable(voltdm);
sr_disable_errgen(sr->voltdm);
omap_vp_disable(sr->voltdm);
sr_disable(sr->voltdm);
if (is_volt_reset)
voltdm_reset(voltdm);
voltdm_reset(sr->voltdm);
return 0;
}
static int sr_class3_configure(struct voltagedomain *voltdm)
static int sr_class3_configure(struct omap_sr *sr)
{
return sr_configure_errgen(voltdm);
return sr_configure_errgen(sr->voltdm);
}
/* SR class3 structure */

View file

@ -17,6 +17,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/power/smartreflex.h>
#include <linux/err.h>
#include <linux/slab.h>
@ -24,7 +25,6 @@
#include <plat/omap_device.h>
#include "smartreflex.h"
#include "voltage.h"
#include "control.h"
#include "pm.h"
@ -36,7 +36,10 @@ static void __init sr_set_nvalues(struct omap_volt_data *volt_data,
struct omap_sr_data *sr_data)
{
struct omap_sr_nvalue_table *nvalue_table;
int i, count = 0;
int i, j, count = 0;
sr_data->nvalue_count = 0;
sr_data->nvalue_table = NULL;
while (volt_data[count].volt_nominal)
count++;
@ -44,8 +47,14 @@ static void __init sr_set_nvalues(struct omap_volt_data *volt_data,
nvalue_table = kzalloc(sizeof(struct omap_sr_nvalue_table)*count,
GFP_KERNEL);
for (i = 0; i < count; i++) {
if (!nvalue_table) {
pr_err("OMAP: SmartReflex: cannot allocate memory for n-value table\n");
return;
}
for (i = 0, j = 0; i < count; i++) {
u32 v;
/*
* In OMAP4 the efuse registers are 24 bit aligned.
* A __raw_readl will fail for non-32 bit aligned address
@ -58,15 +67,30 @@ static void __init sr_set_nvalues(struct omap_volt_data *volt_data,
omap_ctrl_readb(offset + 1) << 8 |
omap_ctrl_readb(offset + 2) << 16;
} else {
v = omap_ctrl_readl(volt_data[i].sr_efuse_offs);
v = omap_ctrl_readl(volt_data[i].sr_efuse_offs);
}
nvalue_table[i].efuse_offs = volt_data[i].sr_efuse_offs;
nvalue_table[i].nvalue = v;
/*
* Many OMAP SoCs don't have the eFuse values set.
* For example, pretty much all OMAP3xxx before
* ES3.something.
*
* XXX There needs to be some way for board files or
* userspace to add these in.
*/
if (v == 0)
continue;
nvalue_table[j].nvalue = v;
nvalue_table[j].efuse_offs = volt_data[i].sr_efuse_offs;
nvalue_table[j].errminlimit = volt_data[i].sr_errminlimit;
nvalue_table[j].volt_nominal = volt_data[i].volt_nominal;
j++;
}
sr_data->nvalue_table = nvalue_table;
sr_data->nvalue_count = count;
sr_data->nvalue_count = j;
}
static int __init sr_dev_init(struct omap_hwmod *oh, void *user)
@ -93,6 +117,7 @@ static int __init sr_dev_init(struct omap_hwmod *oh, void *user)
goto exit;
}
sr_data->name = oh->name;
sr_data->ip_type = oh->class->rev;
sr_data->senn_mod = 0x1;
sr_data->senp_mod = 0x1;

View file

@ -16,6 +16,8 @@
#include <linux/err.h>
#include <plat/voltage.h>
#include "vc.h"
#include "vp.h"
@ -90,25 +92,6 @@ struct voltagedomain {
struct omap_volt_data *volt_data;
};
/**
* struct omap_volt_data - Omap voltage specific data.
* @voltage_nominal: The possible voltage value in uV
* @sr_efuse_offs: The offset of the efuse register(from system
* control module base address) from where to read
* the n-target value for the smartreflex module.
* @sr_errminlimit: Error min limit value for smartreflex. This value
* differs at differnet opp and thus is linked
* with voltage.
* @vp_errorgain: Error gain value for the voltage processor. This
* field also differs according to the voltage/opp.
*/
struct omap_volt_data {
u32 volt_nominal;
u32 sr_efuse_offs;
u8 sr_errminlimit;
u8 vp_errgain;
};
/**
* struct omap_voltdm_pmic - PMIC specific data required by voltage driver.
* @slew_rate: PMIC slew rate (in uv/us)

View file

@ -16,6 +16,7 @@ obj-$(CONFIG_MXC_ULPI) += ulpi.o
obj-$(CONFIG_MXC_USE_EPIT) += epit.o
obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o
obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
ifdef CONFIG_SND_IMX_SOC
obj-y += ssi-fiq.o
obj-y += ssi-fiq-ksym.o

View file

@ -0,0 +1,80 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright 2012 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/cpuidle.h>
#include <linux/err.h>
#include <linux/hrtimer.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
static struct cpuidle_device __percpu * imx_cpuidle_devices;
static void __init imx_cpuidle_devices_uninit(void)
{
int cpu_id;
struct cpuidle_device *dev;
for_each_possible_cpu(cpu_id) {
dev = per_cpu_ptr(imx_cpuidle_devices, cpu_id);
cpuidle_unregister_device(dev);
}
free_percpu(imx_cpuidle_devices);
}
int __init imx_cpuidle_init(struct cpuidle_driver *drv)
{
struct cpuidle_device *dev;
int cpu_id, ret;
if (drv->state_count > CPUIDLE_STATE_MAX) {
pr_err("%s: state_count exceeds maximum\n", __func__);
return -EINVAL;
}
ret = cpuidle_register_driver(drv);
if (ret) {
pr_err("%s: Failed to register cpuidle driver with error: %d\n",
__func__, ret);
return ret;
}
imx_cpuidle_devices = alloc_percpu(struct cpuidle_device);
if (imx_cpuidle_devices == NULL) {
ret = -ENOMEM;
goto unregister_drv;
}
/* initialize state data for each cpuidle_device */
for_each_possible_cpu(cpu_id) {
dev = per_cpu_ptr(imx_cpuidle_devices, cpu_id);
dev->cpu = cpu_id;
dev->state_count = drv->state_count;
ret = cpuidle_register_device(dev);
if (ret) {
pr_err("%s: Failed to register cpu %u, error: %d\n",
__func__, cpu_id, ret);
goto uninit;
}
}
return 0;
uninit:
imx_cpuidle_devices_uninit();
unregister_drv:
cpuidle_unregister_driver(drv);
return ret;
}

View file

@ -54,6 +54,7 @@ extern void imx50_soc_init(void);
extern void imx51_soc_init(void);
extern void imx53_soc_init(void);
extern void imx51_init_late(void);
extern void imx53_init_late(void);
extern void epit_timer_init(void __iomem *base, int irq);
extern void mxc_timer_init(void __iomem *, int);
extern int mx1_clocks_init(unsigned long fref);
@ -96,7 +97,6 @@ enum mx3_cpu_pwr_mode {
};
extern void mx3_cpu_lp_set(enum mx3_cpu_pwr_mode mode);
extern void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode);
extern void imx_print_silicon_rev(const char *cpu, int srev);
void avic_handle_irq(struct pt_regs *);
@ -147,8 +147,12 @@ extern void imx6q_clock_map_io(void);
#ifdef CONFIG_PM
extern void imx6q_pm_init(void);
extern void imx51_pm_init(void);
extern void imx53_pm_init(void);
#else
static inline void imx6q_pm_init(void) {}
static inline void imx51_pm_init(void) {}
static inline void imx53_pm_init(void) {}
#endif
#ifdef CONFIG_NEON

View file

@ -0,0 +1,22 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright 2012 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/cpuidle.h>
#ifdef CONFIG_CPU_IDLE
extern int imx_cpuidle_init(struct cpuidle_driver *drv);
#else
static inline int imx_cpuidle_init(struct cpuidle_driver *drv)
{
return -ENODEV;
}
#endif

View file

@ -50,7 +50,7 @@
* IO 0x00200000+0x100000 -> 0xf4000000+0x100000
* mx21:
* AIPI 0x10000000+0x100000 -> 0xf4400000+0x100000
* SAHB1 0x80000000+0x100000 -> 0xf4000000+0x100000
* SAHB1 0x80000000+0x100000 -> 0xf5000000+0x100000
* X_MEMC 0xdf000000+0x004000 -> 0xf5f00000+0x004000
* mx25:
* AIPS1 0x43f00000+0x100000 -> 0xf5300000+0x100000
@ -58,47 +58,50 @@
* AVIC 0x68000000+0x100000 -> 0xf5800000+0x100000
* mx27:
* AIPI 0x10000000+0x100000 -> 0xf4400000+0x100000
* SAHB1 0x80000000+0x100000 -> 0xf4000000+0x100000
* SAHB1 0x80000000+0x100000 -> 0xf5000000+0x100000
* X_MEMC 0xd8000000+0x100000 -> 0xf5c00000+0x100000
* mx31:
* AIPS1 0x43f00000+0x100000 -> 0xf5300000+0x100000
* AIPS2 0x53f00000+0x100000 -> 0xf5700000+0x100000
* AVIC 0x68000000+0x100000 -> 0xf5800000+0x100000
* X_MEMC 0xb8000000+0x010000 -> 0xf4c00000+0x010000
* X_MEMC 0xb8000000+0x010000 -> 0xf5c00000+0x010000
* SPBA0 0x50000000+0x100000 -> 0xf5400000+0x100000
* mx35:
* AIPS1 0x43f00000+0x100000 -> 0xf5300000+0x100000
* AIPS2 0x53f00000+0x100000 -> 0xf5700000+0x100000
* AVIC 0x68000000+0x100000 -> 0xf5800000+0x100000
* X_MEMC 0xb8000000+0x010000 -> 0xf4c00000+0x010000
* X_MEMC 0xb8000000+0x010000 -> 0xf5c00000+0x010000
* SPBA0 0x50000000+0x100000 -> 0xf5400000+0x100000
* mx50:
* TZIC 0x0fffc000+0x004000 -> 0xf4bfc000+0x004000
* SPBA0 0x50000000+0x100000 -> 0xf5400000+0x100000
* AIPS1 0x53f00000+0x100000 -> 0xf5700000+0x100000
* SPBA0 0x50000000+0x100000 -> 0xf5400000+0x100000
* AIPS2 0x63f00000+0x100000 -> 0xf5300000+0x100000
* mx51:
* TZIC 0xe0000000+0x004000 -> 0xf5000000+0x004000
* TZIC 0x0fffc000+0x004000 -> 0xf4bfc000+0x004000
* IRAM 0x1ffe0000+0x020000 -> 0xf4fe0000+0x020000
* DEBUG 0x60000000+0x100000 -> 0xf5000000+0x100000
* SPBA0 0x70000000+0x100000 -> 0xf5400000+0x100000
* AIPS1 0x73f00000+0x100000 -> 0xf5700000+0x100000
* AIPS2 0x83f00000+0x100000 -> 0xf4300000+0x100000
* AIPS2 0x83f00000+0x100000 -> 0xf5300000+0x100000
* mx53:
* TZIC 0x0fffc000+0x004000 -> 0xf4bfc000+0x004000
* DEBUG 0x40000000+0x100000 -> 0xf5000000+0x100000
* SPBA0 0x50000000+0x100000 -> 0xf5400000+0x100000
* AIPS1 0x53f00000+0x100000 -> 0xf5700000+0x100000
* AIPS2 0x63f00000+0x100000 -> 0xf5300000+0x100000
* mx6q:
* SCU 0x00a00000+0x001000 -> 0xf4000000+0x001000
* SCU 0x00a00000+0x004000 -> 0xf4000000+0x004000
* CCM 0x020c4000+0x004000 -> 0xf42c4000+0x004000
* ANATOP 0x020c8000+0x001000 -> 0xf42c8000+0x001000
* ANATOP 0x020c8000+0x004000 -> 0xf42c8000+0x004000
* UART4 0x021f0000+0x004000 -> 0xf42f0000+0x004000
*/
#define IMX_IO_P2V(x) ( \
0xf4000000 + \
(((x) & 0x80000000) >> 7) | \
(0xf4000000 + \
(((x) & 0x50000000) >> 6) + \
(((x) & 0x0b000000) >> 4) + \
(((x) & 0x000fffff)))
(((x) & 0x000fffff))))
#define IMX_IO_ADDRESS(x) IOMEM(IMX_IO_P2V(x))

View file

@ -202,6 +202,10 @@ void __init tzic_init_irq(void __iomem *irqbase)
* tzic_enable_wake() - enable wakeup interrupt
*
* @return 0 if successful; non-zero otherwise
*
* This function provides an interrupt synchronization point that is required
* by tzic enabled platforms before entering imx specific low power modes (ie,
* those low power modes beyond the WAIT_CLOCKED basic ARM WFI only mode).
*/
int tzic_enable_wake(void)
{

View file

@ -45,31 +45,30 @@ config OMAP_DEBUG_LEDS
depends on OMAP_DEBUG_DEVICES
default y if LEDS_CLASS
config OMAP_SMARTREFLEX
bool "SmartReflex support"
depends on (ARCH_OMAP3 || ARCH_OMAP4) && PM
config POWER_AVS_OMAP
bool "AVS(Adaptive Voltage Scaling) support for OMAP IP versions 1&2"
depends on POWER_AVS && (ARCH_OMAP3 || ARCH_OMAP4) && PM
help
Say Y if you want to enable SmartReflex.
SmartReflex can perform continuous dynamic voltage
scaling around the nominal operating point voltage
according to silicon characteristics and operating
conditions. Enabling SmartReflex reduces power
consumption.
Say Y to enable AVS(Adaptive Voltage Scaling)
support on OMAP containing the version 1 or
version 2 of the SmartReflex IP.
V1 is the 65nm version used in OMAP3430.
V2 is the update for the 45nm version of the IP used in OMAP3630
and OMAP4430
Please note, that by default SmartReflex is only
initialized. To enable the automatic voltage
compensation for vdd mpu and vdd core from user space,
initialized and not enabled. To enable the automatic voltage
compensation for vdd mpu and vdd core from user space,
user must write 1 to
/debug/voltage/vdd_<X>/smartreflex/autocomp,
where X is mpu or core for OMAP3.
/debug/smartreflex/sr_<X>/autocomp,
where X is mpu_iva or core for OMAP3.
Optionally autocompensation can be enabled in the kernel
by default during system init via the enable_on_init flag
which an be passed as platform data to the smartreflex driver.
config OMAP_SMARTREFLEX_CLASS3
config POWER_AVS_OMAP_CLASS3
bool "Class 3 mode of Smartreflex Implementation"
depends on OMAP_SMARTREFLEX && TWL4030_CORE
depends on POWER_AVS_OMAP && TWL4030_CORE
help
Say Y to enable Class 3 implementation of Smartreflex

View file

@ -11,10 +11,29 @@
#ifndef __ARCH_ARM_OMAP_VOLTAGE_H
#define __ARCH_ARM_OMAP_VOLTAGE_H
/**
* struct omap_volt_data - Omap voltage specific data.
* @voltage_nominal: The possible voltage value in uV
* @sr_efuse_offs: The offset of the efuse register(from system
* control module base address) from where to read
* the n-target value for the smartreflex module.
* @sr_errminlimit: Error min limit value for smartreflex. This value
* differs at differnet opp and thus is linked
* with voltage.
* @vp_errorgain: Error gain value for the voltage processor. This
* field also differs according to the voltage/opp.
*/
struct omap_volt_data {
u32 volt_nominal;
u32 sr_efuse_offs;
u8 sr_errminlimit;
u8 vp_errgain;
};
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);
struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
unsigned long volt);
#endif

View file

@ -310,3 +310,5 @@ config AB8500_BATTERY_THERM_ON_BATCTRL
Say Y to enable battery temperature measurements using
thermistor connected on BATCTRL ADC.
endif # POWER_SUPPLY
source "drivers/power/avs/Kconfig"

View file

@ -43,4 +43,5 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_POWER_AVS) += avs/
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o

12
drivers/power/avs/Kconfig Normal file
View file

@ -0,0 +1,12 @@
menuconfig POWER_AVS
bool "Adaptive Voltage Scaling class support"
help
AVS is a power management technique which finely controls the
operating voltage of a device in order to optimize (i.e. reduce)
its power consumption.
At a given operating point the voltage is adapted depending on
static factors (chip manufacturing process) and dynamic factors
(temperature depending performance).
AVS is also called SmartReflex on OMAP devices.
Say Y here to enable Adaptive Voltage Scaling class support.

View file

@ -0,0 +1 @@
obj-$(CONFIG_POWER_AVS_OMAP) += smartreflex.o

View file

@ -3,7 +3,7 @@
*
* Author: Thara Gopinath <thara@ti.com>
*
* Copyright (C) 2010 Texas Instruments, Inc.
* Copyright (C) 2012 Texas Instruments, Inc.
* Thara Gopinath <thara@ti.com>
*
* Copyright (C) 2008 Nokia Corporation
@ -25,39 +25,12 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include "common.h"
#include "pm.h"
#include "smartreflex.h"
#include <linux/power/smartreflex.h>
#define SMARTREFLEX_NAME_LEN 16
#define NVALUE_NAME_LEN 40
#define SR_DISABLE_TIMEOUT 200
struct omap_sr {
struct list_head node;
struct platform_device *pdev;
struct omap_sr_nvalue_table *nvalue_table;
struct voltagedomain *voltdm;
struct dentry *dbg_dir;
unsigned int irq;
int srid;
int ip_type;
int nvalue_count;
bool autocomp_active;
u32 clk_length;
u32 err_weight;
u32 err_minlimit;
u32 err_maxlimit;
u32 accum_data;
u32 senn_avgweight;
u32 senp_avgweight;
u32 senp_mod;
u32 senn_mod;
void __iomem *base;
};
/* sr_list contains all the instances of smartreflex module */
static LIST_HEAD(sr_list);
@ -148,7 +121,7 @@ static irqreturn_t sr_interrupt(int irq, void *data)
}
if (sr_class->notify)
sr_class->notify(sr_info->voltdm, status);
sr_class->notify(sr_info, status);
return IRQ_HANDLED;
}
@ -207,7 +180,7 @@ static void sr_set_regfields(struct omap_sr *sr)
sr->err_weight = OMAP3430_SR_ERRWEIGHT;
sr->err_maxlimit = OMAP3430_SR_ERRMAXLIMIT;
sr->accum_data = OMAP3430_SR_ACCUMDATA;
if (!(strcmp(sr->voltdm->name, "mpu"))) {
if (!(strcmp(sr->name, "smartreflex_mpu_iva"))) {
sr->senn_avgweight = OMAP3430_SR1_SENNAVGWEIGHT;
sr->senp_avgweight = OMAP3430_SR1_SENPAVGWEIGHT;
} else {
@ -226,7 +199,7 @@ static void sr_start_vddautocomp(struct omap_sr *sr)
return;
}
if (!sr_class->enable(sr->voltdm))
if (!sr_class->enable(sr))
sr->autocomp_active = true;
}
@ -240,7 +213,7 @@ static void sr_stop_vddautocomp(struct omap_sr *sr)
}
if (sr->autocomp_active) {
sr_class->disable(sr->voltdm, 1);
sr_class->disable(sr, 1);
sr->autocomp_active = false;
}
}
@ -258,19 +231,13 @@ static void sr_stop_vddautocomp(struct omap_sr *sr)
*/
static int sr_late_init(struct omap_sr *sr_info)
{
char *name;
struct omap_sr_data *pdata = sr_info->pdev->dev.platform_data;
struct resource *mem;
int ret = 0;
if (sr_class->notify && sr_class->notify_flags && sr_info->irq) {
name = kasprintf(GFP_KERNEL, "sr_%s", sr_info->voltdm->name);
if (name == NULL) {
ret = -ENOMEM;
goto error;
}
ret = request_irq(sr_info->irq, sr_interrupt,
0, name, sr_info);
0, sr_info->name, sr_info);
if (ret)
goto error;
disable_irq(sr_info->irq);
@ -289,7 +256,6 @@ static int sr_late_init(struct omap_sr *sr_info)
dev_err(&sr_info->pdev->dev, "%s: ERROR in registering"
"interrupt handler. Smartreflex will"
"not function as desired\n", __func__);
kfree(name);
kfree(sr_info);
return ret;
@ -320,9 +286,9 @@ static void sr_v1_disable(struct omap_sr *sr)
* Wait for SR to be disabled.
* wait until ERRCONFIG.MCUDISACKINTST = 1. Typical latency is 1us.
*/
omap_test_timeout((sr_read_reg(sr, ERRCONFIG_V1) &
ERRCONFIG_MCUDISACKINTST), SR_DISABLE_TIMEOUT,
timeout);
sr_test_cond_timeout((sr_read_reg(sr, ERRCONFIG_V1) &
ERRCONFIG_MCUDISACKINTST), SR_DISABLE_TIMEOUT,
timeout);
if (timeout >= SR_DISABLE_TIMEOUT)
dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n",
@ -365,9 +331,9 @@ static void sr_v2_disable(struct omap_sr *sr)
* Wait for SR to be disabled.
* wait until IRQSTATUS.MCUDISACKINTST = 1. Typical latency is 1us.
*/
omap_test_timeout((sr_read_reg(sr, IRQSTATUS) &
IRQSTATUS_MCUDISABLEACKINT), SR_DISABLE_TIMEOUT,
timeout);
sr_test_cond_timeout((sr_read_reg(sr, IRQSTATUS) &
IRQSTATUS_MCUDISABLEACKINT), SR_DISABLE_TIMEOUT,
timeout);
if (timeout >= SR_DISABLE_TIMEOUT)
dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n",
@ -378,22 +344,23 @@ static void sr_v2_disable(struct omap_sr *sr)
sr_write_reg(sr, IRQSTATUS, IRQSTATUS_MCUDISABLEACKINT);
}
static u32 sr_retrieve_nvalue(struct omap_sr *sr, u32 efuse_offs)
static struct omap_sr_nvalue_table *sr_retrieve_nvalue_row(
struct omap_sr *sr, u32 efuse_offs)
{
int i;
if (!sr->nvalue_table) {
dev_warn(&sr->pdev->dev, "%s: Missing ntarget value table\n",
__func__);
return 0;
return NULL;
}
for (i = 0; i < sr->nvalue_count; i++) {
if (sr->nvalue_table[i].efuse_offs == efuse_offs)
return sr->nvalue_table[i].nvalue;
return &sr->nvalue_table[i];
}
return 0;
return NULL;
}
/* Public Functions */
@ -419,8 +386,7 @@ int sr_configure_errgen(struct voltagedomain *voltdm)
struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
pr_warning("%s: omap_sr struct for sr_%s not found\n",
__func__, voltdm->name);
pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
return PTR_ERR(sr);
}
@ -487,8 +453,7 @@ int sr_disable_errgen(struct voltagedomain *voltdm)
struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
pr_warning("%s: omap_sr struct for sr_%s not found\n",
__func__, voltdm->name);
pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
return PTR_ERR(sr);
}
@ -538,8 +503,7 @@ int sr_configure_minmax(struct voltagedomain *voltdm)
struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
pr_warning("%s: omap_sr struct for sr_%s not found\n",
__func__, voltdm->name);
pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
return PTR_ERR(sr);
}
@ -620,12 +584,11 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt)
{
struct omap_volt_data *volt_data;
struct omap_sr *sr = _sr_lookup(voltdm);
u32 nvalue_reciprocal;
struct omap_sr_nvalue_table *nvalue_row;
int ret;
if (IS_ERR(sr)) {
pr_warning("%s: omap_sr struct for sr_%s not found\n",
__func__, voltdm->name);
pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
return PTR_ERR(sr);
}
@ -637,16 +600,16 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt)
return PTR_ERR(volt_data);
}
nvalue_reciprocal = sr_retrieve_nvalue(sr, volt_data->sr_efuse_offs);
nvalue_row = sr_retrieve_nvalue_row(sr, volt_data->sr_efuse_offs);
if (!nvalue_reciprocal) {
dev_warn(&sr->pdev->dev, "%s: NVALUE = 0 at voltage %ld\n",
__func__, volt);
if (!nvalue_row) {
dev_warn(&sr->pdev->dev, "%s: failure getting SR data for this voltage %ld\n",
__func__, volt);
return -ENODATA;
}
/* errminlimit is opp dependent and hence linked to voltage */
sr->err_minlimit = volt_data->sr_errminlimit;
sr->err_minlimit = nvalue_row->errminlimit;
pm_runtime_get_sync(&sr->pdev->dev);
@ -655,11 +618,11 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt)
return 0;
/* Configure SR */
ret = sr_class->configure(voltdm);
ret = sr_class->configure(sr);
if (ret)
return ret;
sr_write_reg(sr, NVALUERECIPROCAL, nvalue_reciprocal);
sr_write_reg(sr, NVALUERECIPROCAL, nvalue_row->nvalue);
/* SRCONFIG - enable SR */
sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, SRCONFIG_SRENABLE);
@ -678,8 +641,7 @@ void sr_disable(struct voltagedomain *voltdm)
struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
pr_warning("%s: omap_sr struct for sr_%s not found\n",
__func__, voltdm->name);
pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
return;
}
@ -759,8 +721,7 @@ void omap_sr_enable(struct voltagedomain *voltdm)
struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
pr_warning("%s: omap_sr struct for sr_%s not found\n",
__func__, voltdm->name);
pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
return;
}
@ -773,7 +734,7 @@ void omap_sr_enable(struct voltagedomain *voltdm)
return;
}
sr_class->enable(voltdm);
sr_class->enable(sr);
}
/**
@ -792,8 +753,7 @@ void omap_sr_disable(struct voltagedomain *voltdm)
struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
pr_warning("%s: omap_sr struct for sr_%s not found\n",
__func__, voltdm->name);
pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
return;
}
@ -806,7 +766,7 @@ void omap_sr_disable(struct voltagedomain *voltdm)
return;
}
sr_class->disable(voltdm, 0);
sr_class->disable(sr, 0);
}
/**
@ -825,8 +785,7 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm)
struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
pr_warning("%s: omap_sr struct for sr_%s not found\n",
__func__, voltdm->name);
pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
return;
}
@ -839,7 +798,7 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm)
return;
}
sr_class->disable(voltdm, 1);
sr_class->disable(sr, 1);
}
/**
@ -911,9 +870,7 @@ static int __init omap_sr_probe(struct platform_device *pdev)
struct omap_sr_data *pdata = pdev->dev.platform_data;
struct resource *mem, *irq;
struct dentry *nvalue_dir;
struct omap_volt_data *volt_data;
int i, ret = 0;
char *name;
sr_info = kzalloc(sizeof(struct omap_sr), GFP_KERNEL);
if (!sr_info) {
@ -950,6 +907,14 @@ static int __init omap_sr_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_irq_safe(&pdev->dev);
sr_info->name = kasprintf(GFP_KERNEL, "%s", pdata->name);
if (!sr_info->name) {
dev_err(&pdev->dev, "%s: Unable to alloc SR instance name\n",
__func__);
ret = -ENOMEM;
goto err_release_region;
}
sr_info->pdev = pdev;
sr_info->srid = pdev->id;
sr_info->voltdm = pdata->voltdm;
@ -997,20 +962,12 @@ static int __init omap_sr_probe(struct platform_device *pdev)
}
}
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);
sr_info->dbg_dir = debugfs_create_dir(sr_info->name, sr_dbg_dir);
if (IS_ERR_OR_NULL(sr_info->dbg_dir)) {
dev_err(&pdev->dev, "%s: Unable to create debugfs directory\n",
__func__);
ret = PTR_ERR(sr_info->dbg_dir);
goto err_iounmap;
goto err_free_name;
}
(void) debugfs_create_file("autocomp", S_IRUGO | S_IWUSR,
@ -1019,8 +976,6 @@ static int __init omap_sr_probe(struct platform_device *pdev)
&sr_info->err_weight);
(void) debugfs_create_x32("errmaxlimit", S_IRUGO, sr_info->dbg_dir,
&sr_info->err_maxlimit);
(void) debugfs_create_x32("errminlimit", S_IRUGO, sr_info->dbg_dir,
&sr_info->err_minlimit);
nvalue_dir = debugfs_create_dir("nvalue", sr_info->dbg_dir);
if (IS_ERR_OR_NULL(nvalue_dir)) {
@ -1030,12 +985,10 @@ static int __init omap_sr_probe(struct platform_device *pdev)
goto err_debugfs;
}
omap_voltage_get_volttable(sr_info->voltdm, &volt_data);
if (!volt_data) {
dev_warn(&pdev->dev, "%s: No Voltage table for the"
" corresponding vdd vdd_%s. Cannot create debugfs"
"entries for n-values\n",
__func__, sr_info->voltdm->name);
if (sr_info->nvalue_count == 0 || !sr_info->nvalue_table) {
dev_warn(&pdev->dev, "%s: %s: No Voltage table for the corresponding vdd. Cannot create debugfs entries for n-values\n",
__func__, sr_info->name);
ret = -ENODATA;
goto err_debugfs;
}
@ -1043,16 +996,23 @@ static int __init omap_sr_probe(struct platform_device *pdev)
for (i = 0; i < sr_info->nvalue_count; i++) {
char name[NVALUE_NAME_LEN + 1];
snprintf(name, sizeof(name), "volt_%d",
volt_data[i].volt_nominal);
snprintf(name, sizeof(name), "volt_%lu",
sr_info->nvalue_table[i].volt_nominal);
(void) debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir,
&(sr_info->nvalue_table[i].nvalue));
snprintf(name, sizeof(name), "errminlimit_%lu",
sr_info->nvalue_table[i].volt_nominal);
(void) debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir,
&(sr_info->nvalue_table[i].errminlimit));
}
return ret;
err_debugfs:
debugfs_remove_recursive(sr_info->dbg_dir);
err_free_name:
kfree(sr_info->name);
err_iounmap:
list_del(&sr_info->node);
iounmap(sr_info->base);
@ -1089,6 +1049,7 @@ static int __devexit omap_sr_remove(struct platform_device *pdev)
list_del(&sr_info->node);
iounmap(sr_info->base);
kfree(sr_info->name);
kfree(sr_info);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, resource_size(mem));

View file

@ -60,7 +60,7 @@ config W1_MASTER_GPIO
config HDQ_MASTER_OMAP
tristate "OMAP HDQ driver"
depends on SOC_OMAP2430 || ARCH_OMAP3
depends on ARCH_OMAP2PLUS
help
Say Y here if you want support for the 1-wire or HDQ Interface
on an OMAP processor.

View file

@ -1,7 +1,7 @@
/*
* drivers/w1/masters/omap_hdq.c
*
* Copyright (C) 2007 Texas Instruments, Inc.
* Copyright (C) 2007,2012 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
@ -14,9 +14,9 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/pm_runtime.h>
#include <asm/irq.h>
#include <mach/hardware.h>
@ -61,8 +61,6 @@ struct hdq_data {
/* lock status update */
struct mutex hdq_mutex;
int hdq_usecount;
struct clk *hdq_ick;
struct clk *hdq_fck;
u8 hdq_irqstatus;
/* device lock */
spinlock_t hdq_spinlock;
@ -102,20 +100,20 @@ static struct w1_bus_master omap_w1_master = {
/* HDQ register I/O routines */
static inline u8 hdq_reg_in(struct hdq_data *hdq_data, u32 offset)
{
return __raw_readb(hdq_data->hdq_base + offset);
return __raw_readl(hdq_data->hdq_base + offset);
}
static inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val)
{
__raw_writeb(val, hdq_data->hdq_base + offset);
__raw_writel(val, hdq_data->hdq_base + offset);
}
static inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset,
u8 val, u8 mask)
{
u8 new_val = (__raw_readb(hdq_data->hdq_base + offset) & ~mask)
u8 new_val = (__raw_readl(hdq_data->hdq_base + offset) & ~mask)
| (val & mask);
__raw_writeb(new_val, hdq_data->hdq_base + offset);
__raw_writel(new_val, hdq_data->hdq_base + offset);
return new_val;
}
@ -419,17 +417,8 @@ static int omap_hdq_get(struct hdq_data *hdq_data)
hdq_data->hdq_usecount++;
try_module_get(THIS_MODULE);
if (1 == hdq_data->hdq_usecount) {
if (clk_enable(hdq_data->hdq_ick)) {
dev_dbg(hdq_data->dev, "Can not enable ick\n");
ret = -ENODEV;
goto clk_err;
}
if (clk_enable(hdq_data->hdq_fck)) {
dev_dbg(hdq_data->dev, "Can not enable fck\n");
clk_disable(hdq_data->hdq_ick);
ret = -ENODEV;
goto clk_err;
}
pm_runtime_get_sync(hdq_data->dev);
/* make sure HDQ is out of reset */
if (!(hdq_reg_in(hdq_data, OMAP_HDQ_SYSSTATUS) &
@ -450,9 +439,6 @@ static int omap_hdq_get(struct hdq_data *hdq_data)
}
}
clk_err:
clk_put(hdq_data->hdq_ick);
clk_put(hdq_data->hdq_fck);
out:
mutex_unlock(&hdq_data->hdq_mutex);
rtn:
@ -475,10 +461,8 @@ static int omap_hdq_put(struct hdq_data *hdq_data)
} else {
hdq_data->hdq_usecount--;
module_put(THIS_MODULE);
if (0 == hdq_data->hdq_usecount) {
clk_disable(hdq_data->hdq_ick);
clk_disable(hdq_data->hdq_fck);
}
if (0 == hdq_data->hdq_usecount)
pm_runtime_put_sync(hdq_data->dev);
}
mutex_unlock(&hdq_data->hdq_mutex);
@ -591,35 +575,11 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev)
goto err_ioremap;
}
/* get interface & functional clock objects */
hdq_data->hdq_ick = clk_get(&pdev->dev, "ick");
if (IS_ERR(hdq_data->hdq_ick)) {
dev_dbg(&pdev->dev, "Can't get HDQ ick clock object\n");
ret = PTR_ERR(hdq_data->hdq_ick);
goto err_ick;
}
hdq_data->hdq_fck = clk_get(&pdev->dev, "fck");
if (IS_ERR(hdq_data->hdq_fck)) {
dev_dbg(&pdev->dev, "Can't get HDQ fck clock object\n");
ret = PTR_ERR(hdq_data->hdq_fck);
goto err_fck;
}
hdq_data->hdq_usecount = 0;
mutex_init(&hdq_data->hdq_mutex);
if (clk_enable(hdq_data->hdq_ick)) {
dev_dbg(&pdev->dev, "Can not enable ick\n");
ret = -ENODEV;
goto err_intfclk;
}
if (clk_enable(hdq_data->hdq_fck)) {
dev_dbg(&pdev->dev, "Can not enable fck\n");
ret = -ENODEV;
goto err_fnclk;
}
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION);
dev_info(&pdev->dev, "OMAP HDQ Hardware Rev %c.%c. Driver in %s mode\n",
@ -641,9 +601,7 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev)
omap_hdq_break(hdq_data);
/* don't clock the HDQ until it is needed */
clk_disable(hdq_data->hdq_ick);
clk_disable(hdq_data->hdq_fck);
pm_runtime_put_sync(&pdev->dev);
omap_w1_master.data = hdq_data;
@ -655,20 +613,11 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev)
return 0;
err_w1:
err_irq:
clk_disable(hdq_data->hdq_fck);
pm_runtime_put_sync(&pdev->dev);
err_w1:
pm_runtime_disable(&pdev->dev);
err_fnclk:
clk_disable(hdq_data->hdq_ick);
err_intfclk:
clk_put(hdq_data->hdq_fck);
err_fck:
clk_put(hdq_data->hdq_ick);
err_ick:
iounmap(hdq_data->hdq_base);
err_ioremap:
@ -696,8 +645,7 @@ static int omap_hdq_remove(struct platform_device *pdev)
mutex_unlock(&hdq_data->hdq_mutex);
/* remove module dependency */
clk_put(hdq_data->hdq_ick);
clk_put(hdq_data->hdq_fck);
pm_runtime_disable(&pdev->dev);
free_irq(INT_24XX_HDQ_IRQ, hdq_data);
platform_set_drvdata(pdev, NULL);
iounmap(hdq_data->hdq_base);

View file

@ -17,12 +17,13 @@
* published by the Free Software Foundation.
*/
#ifndef __ASM_ARM_OMAP_SMARTREFLEX_H
#define __ASM_ARM_OMAP_SMARTREFLEX_H
#ifndef __POWER_SMARTREFLEX_H
#define __POWER_SMARTREFLEX_H
#include <linux/types.h>
#include <linux/platform_device.h>
#include "voltage.h"
#include <linux/delay.h>
#include <plat/voltage.h>
/*
* Different Smartreflex IPs version. The v1 is the 65nm version used in
@ -142,6 +143,51 @@
#define OMAP3430_SR_ERRWEIGHT 0x04
#define OMAP3430_SR_ERRMAXLIMIT 0x02
struct omap_sr {
char *name;
struct list_head node;
struct platform_device *pdev;
struct omap_sr_nvalue_table *nvalue_table;
struct voltagedomain *voltdm;
struct dentry *dbg_dir;
unsigned int irq;
int srid;
int ip_type;
int nvalue_count;
bool autocomp_active;
u32 clk_length;
u32 err_weight;
u32 err_minlimit;
u32 err_maxlimit;
u32 accum_data;
u32 senn_avgweight;
u32 senp_avgweight;
u32 senp_mod;
u32 senn_mod;
void __iomem *base;
};
/**
* test_cond_timeout - busy-loop, testing a condition
* @cond: condition to test until it evaluates to true
* @timeout: maximum number of microseconds in the timeout
* @index: loop index (integer)
*
* Loop waiting for @cond to become true or until at least @timeout
* microseconds have passed. To use, define some integer @index in the
* calling code. After running, if @index == @timeout, then the loop has
* timed out.
*
* Copied from omap_test_timeout */
#define sr_test_cond_timeout(cond, timeout, index) \
({ \
for (index = 0; index < timeout; index++) { \
if (cond) \
break; \
udelay(1); \
} \
})
/**
* struct omap_sr_pmic_data - Strucutre to be populated by pmic code to pass
* pmic specific info to smartreflex driver
@ -161,7 +207,7 @@ struct omap_smartreflex_dev_attr {
const char *sensor_voltdm_name;
};
#ifdef CONFIG_OMAP_SMARTREFLEX
#ifdef CONFIG_POWER_AVS_OMAP
/*
* The smart reflex driver supports CLASS1 CLASS2 and CLASS3 SR.
* The smartreflex class driver should pass the class type.
@ -186,10 +232,10 @@ struct omap_smartreflex_dev_attr {
* based decisions.
*/
struct omap_sr_class_data {
int (*enable)(struct voltagedomain *voltdm);
int (*disable)(struct voltagedomain *voltdm, int is_volt_reset);
int (*configure)(struct voltagedomain *voltdm);
int (*notify)(struct voltagedomain *voltdm, u32 status);
int (*enable)(struct omap_sr *sr);
int (*disable)(struct omap_sr *sr, int is_volt_reset);
int (*configure)(struct omap_sr *sr);
int (*notify)(struct omap_sr *sr, u32 status);
u8 notify_flags;
u8 class_type;
};
@ -197,17 +243,22 @@ struct omap_sr_class_data {
/**
* struct omap_sr_nvalue_table - Smartreflex n-target value info
*
* @efuse_offs: The offset of the efuse where n-target values are stored.
* @nvalue: The n-target value.
* @efuse_offs: The offset of the efuse where n-target values are stored.
* @nvalue: The n-target value.
* @errminlimit: The value of the ERRMINLIMIT bitfield for this n-target
* @volt_nominal: microvolts DC that the VDD is initially programmed to
*/
struct omap_sr_nvalue_table {
u32 efuse_offs;
u32 nvalue;
u32 errminlimit;
unsigned long volt_nominal;
};
/**
* struct omap_sr_data - Smartreflex platform data.
*
* @name: instance name
* @ip_type: Smartreflex IP type.
* @senp_mod: SENPENABLE value for the sr
* @senn_mod: SENNENABLE value for sr
@ -219,6 +270,7 @@ struct omap_sr_nvalue_table {
* @voltdm: Pointer to the voltage domain associated with the SR
*/
struct omap_sr_data {
const char *name;
int ip_type;
u32 senp_mod;
u32 senn_mod;