提交 b0189cd0 编写于 作者: L Linus Torvalds

Merge branch 'next/devel2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/linux-arm-soc

* 'next/devel2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/linux-arm-soc: (47 commits)
  OMAP: Add debugfs node to show the summary of all clocks
  OMAP2+: hwmod: Follow the recommended PRCM module enable sequence
  OMAP2+: clock: allow per-SoC clock init code to prevent clockdomain calls from clock code
  OMAP2+: clockdomain: Add per clkdm lock to prevent concurrent state programming
  OMAP2+: PM: idle clkdms only if already in idle
  OMAP2+: clockdomain: add clkdm_in_hwsup()
  OMAP2+: clockdomain: Add 2 APIs to control clockdomain from hwmod framework
  OMAP: clockdomain: Remove redundant call to pwrdm_wait_transition()
  OMAP4: hwmod: Introduce the module control in hwmod control
  OMAP4: cm: Add two new APIs for modulemode control
  OMAP4: hwmod data: Add modulemode entry in omap_hwmod structure
  OMAP4: hwmod data: Add PRM context register offset
  OMAP4: prm: Remove deprecated functions
  OMAP4: prm: Replace warm reset API with the offset based version
  OMAP4: hwmod: Replace RSTCTRL absolute address with offset macros
  OMAP: hwmod: Wait the idle status to be disabled
  OMAP4: hwmod: Replace CLKCTRL absolute address with offset macros
  OMAP2+: hwmod: Init clkdm field at boot time
  OMAP4: hwmod data: Add clock domain attribute
  OMAP4: clock data: Add missing divider selection for auxclks
  ...
...@@ -345,11 +345,40 @@ static struct platform_device sdp4430_lcd_device = { ...@@ -345,11 +345,40 @@ static struct platform_device sdp4430_lcd_device = {
.id = -1, .id = -1,
}; };
static struct regulator_consumer_supply sdp4430_vbat_supply[] = {
REGULATOR_SUPPLY("vddvibl", "twl6040-vibra"),
REGULATOR_SUPPLY("vddvibr", "twl6040-vibra"),
};
static struct regulator_init_data sdp4430_vbat_data = {
.constraints = {
.always_on = 1,
},
.num_consumer_supplies = ARRAY_SIZE(sdp4430_vbat_supply),
.consumer_supplies = sdp4430_vbat_supply,
};
static struct fixed_voltage_config sdp4430_vbat_pdata = {
.supply_name = "VBAT",
.microvolts = 3750000,
.init_data = &sdp4430_vbat_data,
.gpio = -EINVAL,
};
static struct platform_device sdp4430_vbat = {
.name = "reg-fixed-voltage",
.id = -1,
.dev = {
.platform_data = &sdp4430_vbat_pdata,
},
};
static struct platform_device *sdp4430_devices[] __initdata = { static struct platform_device *sdp4430_devices[] __initdata = {
&sdp4430_lcd_device, &sdp4430_lcd_device,
&sdp4430_gpio_keys_device, &sdp4430_gpio_keys_device,
&sdp4430_leds_gpio, &sdp4430_leds_gpio,
&sdp4430_leds_pwm, &sdp4430_leds_pwm,
&sdp4430_vbat,
}; };
static struct omap_lcd_config sdp4430_lcd_config __initdata = { static struct omap_lcd_config sdp4430_lcd_config __initdata = {
...@@ -505,7 +534,33 @@ static struct regulator_init_data sdp4430_vusim = { ...@@ -505,7 +534,33 @@ static struct regulator_init_data sdp4430_vusim = {
}, },
}; };
static struct twl4030_codec_data twl6040_codec = {
/* single-step ramp for headset and handsfree */
.hs_left_step = 0x0f,
.hs_right_step = 0x0f,
.hf_left_step = 0x1d,
.hf_right_step = 0x1d,
};
static struct twl4030_vibra_data twl6040_vibra = {
.vibldrv_res = 8,
.vibrdrv_res = 3,
.viblmotor_res = 10,
.vibrmotor_res = 10,
.vddvibl_uV = 0, /* fixed volt supply - VBAT */
.vddvibr_uV = 0, /* fixed volt supply - VBAT */
};
static struct twl4030_audio_data twl6040_audio = {
.codec = &twl6040_codec,
.vibra = &twl6040_vibra,
.audpwron_gpio = 127,
.naudint_irq = OMAP44XX_IRQ_SYS_2N,
.irq_base = TWL6040_CODEC_IRQ_BASE,
};
static struct twl4030_platform_data sdp4430_twldata = { static struct twl4030_platform_data sdp4430_twldata = {
.audio = &twl6040_audio,
/* Regulators */ /* Regulators */
.vusim = &sdp4430_vusim, .vusim = &sdp4430_vusim,
.vaux1 = &sdp4430_vaux1, .vaux1 = &sdp4430_vaux1,
......
...@@ -864,11 +864,11 @@ static struct twl4030_power_data rx51_t2scripts_data __initdata = { ...@@ -864,11 +864,11 @@ static struct twl4030_power_data rx51_t2scripts_data __initdata = {
.resource_config = twl4030_rconfig, .resource_config = twl4030_rconfig,
}; };
struct twl4030_codec_vibra_data rx51_vibra_data __initdata = { struct twl4030_vibra_data rx51_vibra_data __initdata = {
.coexist = 0, .coexist = 0,
}; };
struct twl4030_codec_data rx51_codec_data __initdata = { struct twl4030_audio_data rx51_audio_data __initdata = {
.audio_mclk = 26000000, .audio_mclk = 26000000,
.vibra = &rx51_vibra_data, .vibra = &rx51_vibra_data,
}; };
...@@ -878,7 +878,7 @@ static struct twl4030_platform_data rx51_twldata __initdata = { ...@@ -878,7 +878,7 @@ static struct twl4030_platform_data rx51_twldata __initdata = {
.gpio = &rx51_gpio_data, .gpio = &rx51_gpio_data,
.keypad = &rx51_kp_data, .keypad = &rx51_kp_data,
.power = &rx51_t2scripts_data, .power = &rx51_t2scripts_data,
.codec = &rx51_codec_data, .audio = &rx51_audio_data,
.vaux1 = &rx51_vaux1, .vaux1 = &rx51_vaux1,
.vaux2 = &rx51_vaux2, .vaux2 = &rx51_vaux2,
......
...@@ -274,12 +274,12 @@ static int __init omap_i2c_init(void) ...@@ -274,12 +274,12 @@ static int __init omap_i2c_init(void)
TWL_COMMON_REGULATOR_VDAC | TWL_COMMON_REGULATOR_VPLL2); TWL_COMMON_REGULATOR_VDAC | TWL_COMMON_REGULATOR_VPLL2);
if (machine_is_omap_zoom2()) { if (machine_is_omap_zoom2()) {
struct twl4030_codec_audio_data *audio_data; struct twl4030_codec_data *codec_data;
audio_data = zoom_twldata.codec->audio; codec_data = zoom_twldata.audio->codec;
audio_data->ramp_delay_value = 3; /* 161 ms */ codec_data->ramp_delay_value = 3; /* 161 ms */
audio_data->hs_extmute = 1; codec_data->hs_extmute = 1;
audio_data->set_hs_extmute = zoom2_set_hs_extmute; codec_data->set_hs_extmute = zoom2_set_hs_extmute;
} }
omap_pmic_init(1, 2400, "twl5030", INT_34XX_SYS_NIRQ, &zoom_twldata); omap_pmic_init(1, 2400, "twl5030", INT_34XX_SYS_NIRQ, &zoom_twldata);
omap_register_i2c_bus(2, 400, NULL, 0); omap_register_i2c_bus(2, 400, NULL, 0);
......
...@@ -37,6 +37,14 @@ ...@@ -37,6 +37,14 @@
u8 cpu_mask; u8 cpu_mask;
/*
* clkdm_control: if true, then when a clock is enabled in the
* hardware, its clockdomain will first be enabled; and when a clock
* is disabled in the hardware, its clockdomain will be disabled
* afterwards.
*/
static bool clkdm_control = true;
/* /*
* OMAP2+ specific clock functions * OMAP2+ specific clock functions
*/ */
...@@ -99,6 +107,19 @@ void omap2_init_clk_clkdm(struct clk *clk) ...@@ -99,6 +107,19 @@ void omap2_init_clk_clkdm(struct clk *clk)
} }
} }
/**
* omap2_clk_disable_clkdm_control - disable clkdm control on clk enable/disable
*
* Prevent the OMAP clock code from calling into the clockdomain code
* when a hardware clock in that clockdomain is enabled or disabled.
* Intended to be called at init time from omap*_clk_init(). No
* return value.
*/
void __init omap2_clk_disable_clkdm_control(void)
{
clkdm_control = false;
}
/** /**
* omap2_clk_dflt_find_companion - find companion clock to @clk * omap2_clk_dflt_find_companion - find companion clock to @clk
* @clk: struct clk * to find the companion clock of * @clk: struct clk * to find the companion clock of
...@@ -268,7 +289,7 @@ void omap2_clk_disable(struct clk *clk) ...@@ -268,7 +289,7 @@ void omap2_clk_disable(struct clk *clk)
clk->ops->disable(clk); clk->ops->disable(clk);
} }
if (clk->clkdm) if (clkdm_control && clk->clkdm)
clkdm_clk_disable(clk->clkdm, clk); clkdm_clk_disable(clk->clkdm, clk);
if (clk->parent) if (clk->parent)
...@@ -308,7 +329,7 @@ int omap2_clk_enable(struct clk *clk) ...@@ -308,7 +329,7 @@ int omap2_clk_enable(struct clk *clk)
} }
} }
if (clk->clkdm) { if (clkdm_control && clk->clkdm) {
ret = clkdm_clk_enable(clk->clkdm, clk); ret = clkdm_clk_enable(clk->clkdm, clk);
if (ret) { if (ret) {
WARN(1, "clock: %s: could not enable clockdomain %s: " WARN(1, "clock: %s: could not enable clockdomain %s: "
...@@ -330,7 +351,7 @@ int omap2_clk_enable(struct clk *clk) ...@@ -330,7 +351,7 @@ int omap2_clk_enable(struct clk *clk)
return 0; return 0;
oce_err3: oce_err3:
if (clk->clkdm) if (clkdm_control && clk->clkdm)
clkdm_clk_disable(clk->clkdm, clk); clkdm_clk_disable(clk->clkdm, clk);
oce_err2: oce_err2:
if (clk->parent) if (clk->parent)
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H #ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H
#define __ARCH_ARM_MACH_OMAP2_CLOCK_H #define __ARCH_ARM_MACH_OMAP2_CLOCK_H
#include <linux/kernel.h>
#include <plat/clock.h> #include <plat/clock.h>
/* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */ /* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */
...@@ -72,6 +74,7 @@ void omap2_clk_disable_unused(struct clk *clk); ...@@ -72,6 +74,7 @@ void omap2_clk_disable_unused(struct clk *clk);
#endif #endif
void omap2_init_clk_clkdm(struct clk *clk); void omap2_init_clk_clkdm(struct clk *clk);
void __init omap2_clk_disable_clkdm_control(void);
/* clkt_clksel.c public functions */ /* clkt_clksel.c public functions */
u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
......
...@@ -1805,9 +1805,9 @@ static struct omap_clk omap2420_clks[] = { ...@@ -1805,9 +1805,9 @@ static struct omap_clk omap2420_clks[] = {
CLK(NULL, "gfx_ick", &gfx_ick, CK_242X), CLK(NULL, "gfx_ick", &gfx_ick, CK_242X),
/* DSS domain clocks */ /* DSS domain clocks */
CLK("omapdss_dss", "ick", &dss_ick, CK_242X), CLK("omapdss_dss", "ick", &dss_ick, CK_242X),
CLK("omapdss_dss", "fck", &dss1_fck, CK_242X), CLK(NULL, "dss1_fck", &dss1_fck, CK_242X),
CLK("omapdss_dss", "sys_clk", &dss2_fck, CK_242X), CLK(NULL, "dss2_fck", &dss2_fck, CK_242X),
CLK("omapdss_dss", "tv_clk", &dss_54m_fck, CK_242X), CLK(NULL, "dss_54m_fck", &dss_54m_fck, CK_242X),
/* L3 domain clocks */ /* L3 domain clocks */
CLK(NULL, "core_l3_ck", &core_l3_ck, CK_242X), CLK(NULL, "core_l3_ck", &core_l3_ck, CK_242X),
CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_242X), CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_242X),
...@@ -1844,13 +1844,13 @@ static struct omap_clk omap2420_clks[] = { ...@@ -1844,13 +1844,13 @@ static struct omap_clk omap2420_clks[] = {
CLK(NULL, "gpt12_ick", &gpt12_ick, CK_242X), CLK(NULL, "gpt12_ick", &gpt12_ick, CK_242X),
CLK(NULL, "gpt12_fck", &gpt12_fck, CK_242X), CLK(NULL, "gpt12_fck", &gpt12_fck, CK_242X),
CLK("omap-mcbsp.1", "ick", &mcbsp1_ick, CK_242X), CLK("omap-mcbsp.1", "ick", &mcbsp1_ick, CK_242X),
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_242X), CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_242X),
CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_242X), CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_242X),
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_242X), CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_242X),
CLK("omap2_mcspi.1", "ick", &mcspi1_ick, CK_242X), CLK("omap2_mcspi.1", "ick", &mcspi1_ick, CK_242X),
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_242X), CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_242X),
CLK("omap2_mcspi.2", "ick", &mcspi2_ick, CK_242X), CLK("omap2_mcspi.2", "ick", &mcspi2_ick, CK_242X),
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_242X), CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_242X),
CLK(NULL, "uart1_ick", &uart1_ick, CK_242X), CLK(NULL, "uart1_ick", &uart1_ick, CK_242X),
CLK(NULL, "uart1_fck", &uart1_fck, CK_242X), CLK(NULL, "uart1_fck", &uart1_fck, CK_242X),
CLK(NULL, "uart2_ick", &uart2_ick, CK_242X), CLK(NULL, "uart2_ick", &uart2_ick, CK_242X),
...@@ -1860,7 +1860,7 @@ static struct omap_clk omap2420_clks[] = { ...@@ -1860,7 +1860,7 @@ static struct omap_clk omap2420_clks[] = {
CLK(NULL, "gpios_ick", &gpios_ick, CK_242X), CLK(NULL, "gpios_ick", &gpios_ick, CK_242X),
CLK(NULL, "gpios_fck", &gpios_fck, CK_242X), CLK(NULL, "gpios_fck", &gpios_fck, CK_242X),
CLK("omap_wdt", "ick", &mpu_wdt_ick, CK_242X), CLK("omap_wdt", "ick", &mpu_wdt_ick, CK_242X),
CLK("omap_wdt", "fck", &mpu_wdt_fck, CK_242X), CLK(NULL, "mpu_wdt_fck", &mpu_wdt_fck, CK_242X),
CLK(NULL, "sync_32k_ick", &sync_32k_ick, CK_242X), CLK(NULL, "sync_32k_ick", &sync_32k_ick, CK_242X),
CLK(NULL, "wdt1_ick", &wdt1_ick, CK_242X), CLK(NULL, "wdt1_ick", &wdt1_ick, CK_242X),
CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_242X), CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_242X),
...@@ -1880,11 +1880,11 @@ static struct omap_clk omap2420_clks[] = { ...@@ -1880,11 +1880,11 @@ static struct omap_clk omap2420_clks[] = {
CLK(NULL, "eac_ick", &eac_ick, CK_242X), CLK(NULL, "eac_ick", &eac_ick, CK_242X),
CLK(NULL, "eac_fck", &eac_fck, CK_242X), CLK(NULL, "eac_fck", &eac_fck, CK_242X),
CLK("omap_hdq.0", "ick", &hdq_ick, CK_242X), CLK("omap_hdq.0", "ick", &hdq_ick, CK_242X),
CLK("omap_hdq.1", "fck", &hdq_fck, CK_242X), CLK("omap_hdq.0", "fck", &hdq_fck, CK_242X),
CLK("omap_i2c.1", "ick", &i2c1_ick, CK_242X), CLK("omap_i2c.1", "ick", &i2c1_ick, CK_242X),
CLK("omap_i2c.1", "fck", &i2c1_fck, CK_242X), CLK(NULL, "i2c1_fck", &i2c1_fck, CK_242X),
CLK("omap_i2c.2", "ick", &i2c2_ick, CK_242X), CLK("omap_i2c.2", "ick", &i2c2_ick, CK_242X),
CLK("omap_i2c.2", "fck", &i2c2_fck, CK_242X), CLK(NULL, "i2c2_fck", &i2c2_fck, CK_242X),
CLK(NULL, "gpmc_fck", &gpmc_fck, CK_242X), CLK(NULL, "gpmc_fck", &gpmc_fck, CK_242X),
CLK(NULL, "sdma_fck", &sdma_fck, CK_242X), CLK(NULL, "sdma_fck", &sdma_fck, CK_242X),
CLK(NULL, "sdma_ick", &sdma_ick, CK_242X), CLK(NULL, "sdma_ick", &sdma_ick, CK_242X),
......
...@@ -1895,9 +1895,9 @@ static struct omap_clk omap2430_clks[] = { ...@@ -1895,9 +1895,9 @@ static struct omap_clk omap2430_clks[] = {
CLK(NULL, "mdm_osc_ck", &mdm_osc_ck, CK_243X), CLK(NULL, "mdm_osc_ck", &mdm_osc_ck, CK_243X),
/* DSS domain clocks */ /* DSS domain clocks */
CLK("omapdss_dss", "ick", &dss_ick, CK_243X), CLK("omapdss_dss", "ick", &dss_ick, CK_243X),
CLK("omapdss_dss", "fck", &dss1_fck, CK_243X), CLK(NULL, "dss1_fck", &dss1_fck, CK_243X),
CLK("omapdss_dss", "sys_clk", &dss2_fck, CK_243X), CLK(NULL, "dss2_fck", &dss2_fck, CK_243X),
CLK("omapdss_dss", "tv_clk", &dss_54m_fck, CK_243X), CLK(NULL, "dss_54m_fck", &dss_54m_fck, CK_243X),
/* L3 domain clocks */ /* L3 domain clocks */
CLK(NULL, "core_l3_ck", &core_l3_ck, CK_243X), CLK(NULL, "core_l3_ck", &core_l3_ck, CK_243X),
CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_243X), CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_243X),
...@@ -1934,21 +1934,21 @@ static struct omap_clk omap2430_clks[] = { ...@@ -1934,21 +1934,21 @@ static struct omap_clk omap2430_clks[] = {
CLK(NULL, "gpt12_ick", &gpt12_ick, CK_243X), CLK(NULL, "gpt12_ick", &gpt12_ick, CK_243X),
CLK(NULL, "gpt12_fck", &gpt12_fck, CK_243X), CLK(NULL, "gpt12_fck", &gpt12_fck, CK_243X),
CLK("omap-mcbsp.1", "ick", &mcbsp1_ick, CK_243X), CLK("omap-mcbsp.1", "ick", &mcbsp1_ick, CK_243X),
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_243X), CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_243X),
CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_243X), CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_243X),
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_243X), CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_243X),
CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_243X), CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_243X),
CLK("omap-mcbsp.3", "fck", &mcbsp3_fck, CK_243X), CLK(NULL, "mcbsp3_fck", &mcbsp3_fck, CK_243X),
CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_243X), CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_243X),
CLK("omap-mcbsp.4", "fck", &mcbsp4_fck, CK_243X), CLK(NULL, "mcbsp4_fck", &mcbsp4_fck, CK_243X),
CLK("omap-mcbsp.5", "ick", &mcbsp5_ick, CK_243X), CLK("omap-mcbsp.5", "ick", &mcbsp5_ick, CK_243X),
CLK("omap-mcbsp.5", "fck", &mcbsp5_fck, CK_243X), CLK(NULL, "mcbsp5_fck", &mcbsp5_fck, CK_243X),
CLK("omap2_mcspi.1", "ick", &mcspi1_ick, CK_243X), CLK("omap2_mcspi.1", "ick", &mcspi1_ick, CK_243X),
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_243X), CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_243X),
CLK("omap2_mcspi.2", "ick", &mcspi2_ick, CK_243X), CLK("omap2_mcspi.2", "ick", &mcspi2_ick, CK_243X),
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_243X), CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_243X),
CLK("omap2_mcspi.3", "ick", &mcspi3_ick, CK_243X), CLK("omap2_mcspi.3", "ick", &mcspi3_ick, CK_243X),
CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_243X), CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_243X),
CLK(NULL, "uart1_ick", &uart1_ick, CK_243X), CLK(NULL, "uart1_ick", &uart1_ick, CK_243X),
CLK(NULL, "uart1_fck", &uart1_fck, CK_243X), CLK(NULL, "uart1_fck", &uart1_fck, CK_243X),
CLK(NULL, "uart2_ick", &uart2_ick, CK_243X), CLK(NULL, "uart2_ick", &uart2_ick, CK_243X),
...@@ -1958,7 +1958,7 @@ static struct omap_clk omap2430_clks[] = { ...@@ -1958,7 +1958,7 @@ static struct omap_clk omap2430_clks[] = {
CLK(NULL, "gpios_ick", &gpios_ick, CK_243X), CLK(NULL, "gpios_ick", &gpios_ick, CK_243X),
CLK(NULL, "gpios_fck", &gpios_fck, CK_243X), CLK(NULL, "gpios_fck", &gpios_fck, CK_243X),
CLK("omap_wdt", "ick", &mpu_wdt_ick, CK_243X), CLK("omap_wdt", "ick", &mpu_wdt_ick, CK_243X),
CLK("omap_wdt", "fck", &mpu_wdt_fck, CK_243X), CLK(NULL, "mpu_wdt_fck", &mpu_wdt_fck, CK_243X),
CLK(NULL, "sync_32k_ick", &sync_32k_ick, CK_243X), CLK(NULL, "sync_32k_ick", &sync_32k_ick, CK_243X),
CLK(NULL, "wdt1_ick", &wdt1_ick, CK_243X), CLK(NULL, "wdt1_ick", &wdt1_ick, CK_243X),
CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_243X), CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_243X),
...@@ -1975,9 +1975,9 @@ static struct omap_clk omap2430_clks[] = { ...@@ -1975,9 +1975,9 @@ static struct omap_clk omap2430_clks[] = {
CLK("omap_hdq.0", "ick", &hdq_ick, CK_243X), CLK("omap_hdq.0", "ick", &hdq_ick, CK_243X),
CLK("omap_hdq.1", "fck", &hdq_fck, CK_243X), CLK("omap_hdq.1", "fck", &hdq_fck, CK_243X),
CLK("omap_i2c.1", "ick", &i2c1_ick, CK_243X), CLK("omap_i2c.1", "ick", &i2c1_ick, CK_243X),
CLK("omap_i2c.1", "fck", &i2chs1_fck, CK_243X), CLK(NULL, "i2chs1_fck", &i2chs1_fck, CK_243X),
CLK("omap_i2c.2", "ick", &i2c2_ick, CK_243X), CLK("omap_i2c.2", "ick", &i2c2_ick, CK_243X),
CLK("omap_i2c.2", "fck", &i2chs2_fck, CK_243X), CLK(NULL, "i2chs2_fck", &i2chs2_fck, CK_243X),
CLK(NULL, "gpmc_fck", &gpmc_fck, CK_243X), CLK(NULL, "gpmc_fck", &gpmc_fck, CK_243X),
CLK(NULL, "sdma_fck", &sdma_fck, CK_243X), CLK(NULL, "sdma_fck", &sdma_fck, CK_243X),
CLK(NULL, "sdma_ick", &sdma_ick, CK_243X), CLK(NULL, "sdma_ick", &sdma_ick, CK_243X),
...@@ -1990,9 +1990,9 @@ static struct omap_clk omap2430_clks[] = { ...@@ -1990,9 +1990,9 @@ static struct omap_clk omap2430_clks[] = {
CLK(NULL, "usb_fck", &usb_fck, CK_243X), CLK(NULL, "usb_fck", &usb_fck, CK_243X),
CLK("musb-omap2430", "ick", &usbhs_ick, CK_243X), CLK("musb-omap2430", "ick", &usbhs_ick, CK_243X),
CLK("omap_hsmmc.0", "ick", &mmchs1_ick, CK_243X), CLK("omap_hsmmc.0", "ick", &mmchs1_ick, CK_243X),
CLK("omap_hsmmc.0", "fck", &mmchs1_fck, CK_243X), CLK(NULL, "mmchs1_fck", &mmchs1_fck, CK_243X),
CLK("omap_hsmmc.1", "ick", &mmchs2_ick, CK_243X), CLK("omap_hsmmc.1", "ick", &mmchs2_ick, CK_243X),
CLK("omap_hsmmc.1", "fck", &mmchs2_fck, CK_243X), CLK(NULL, "mmchs2_fck", &mmchs2_fck, CK_243X),
CLK(NULL, "gpio5_ick", &gpio5_ick, CK_243X), CLK(NULL, "gpio5_ick", &gpio5_ick, CK_243X),
CLK(NULL, "gpio5_fck", &gpio5_fck, CK_243X), CLK(NULL, "gpio5_fck", &gpio5_fck, CK_243X),
CLK(NULL, "mdm_intc_ick", &mdm_intc_ick, CK_243X), CLK(NULL, "mdm_intc_ick", &mdm_intc_ick, CK_243X),
......
...@@ -3289,20 +3289,20 @@ static struct omap_clk omap3xxx_clks[] = { ...@@ -3289,20 +3289,20 @@ static struct omap_clk omap3xxx_clks[] = {
CLK("omap-mcbsp.1", "prcm_fck", &core_96m_fck, CK_3XXX), CLK("omap-mcbsp.1", "prcm_fck", &core_96m_fck, CK_3XXX),
CLK("omap-mcbsp.5", "prcm_fck", &core_96m_fck, CK_3XXX), CLK("omap-mcbsp.5", "prcm_fck", &core_96m_fck, CK_3XXX),
CLK(NULL, "core_96m_fck", &core_96m_fck, CK_3XXX), CLK(NULL, "core_96m_fck", &core_96m_fck, CK_3XXX),
CLK("omap_hsmmc.2", "fck", &mmchs3_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), CLK(NULL, "mmchs3_fck", &mmchs3_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK("omap_hsmmc.1", "fck", &mmchs2_fck, CK_3XXX), CLK(NULL, "mmchs2_fck", &mmchs2_fck, CK_3XXX),
CLK(NULL, "mspro_fck", &mspro_fck, CK_34XX | CK_36XX), CLK(NULL, "mspro_fck", &mspro_fck, CK_34XX | CK_36XX),
CLK("omap_hsmmc.0", "fck", &mmchs1_fck, CK_3XXX), CLK(NULL, "mmchs1_fck", &mmchs1_fck, CK_3XXX),
CLK("omap_i2c.3", "fck", &i2c3_fck, CK_3XXX), CLK(NULL, "i2c3_fck", &i2c3_fck, CK_3XXX),
CLK("omap_i2c.2", "fck", &i2c2_fck, CK_3XXX), CLK(NULL, "i2c2_fck", &i2c2_fck, CK_3XXX),
CLK("omap_i2c.1", "fck", &i2c1_fck, CK_3XXX), CLK(NULL, "i2c1_fck", &i2c1_fck, CK_3XXX),
CLK("omap-mcbsp.5", "fck", &mcbsp5_fck, CK_3XXX), CLK(NULL, "mcbsp5_fck", &mcbsp5_fck, CK_3XXX),
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_3XXX), CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_3XXX),
CLK(NULL, "core_48m_fck", &core_48m_fck, CK_3XXX), CLK(NULL, "core_48m_fck", &core_48m_fck, CK_3XXX),
CLK("omap2_mcspi.4", "fck", &mcspi4_fck, CK_3XXX), CLK(NULL, "mcspi4_fck", &mcspi4_fck, CK_3XXX),
CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_3XXX), CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_3XXX),
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_3XXX), CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_3XXX),
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_3XXX), CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_3XXX),
CLK(NULL, "uart2_fck", &uart2_fck, CK_3XXX), CLK(NULL, "uart2_fck", &uart2_fck, CK_3XXX),
CLK(NULL, "uart1_fck", &uart1_fck, CK_3XXX), CLK(NULL, "uart1_fck", &uart1_fck, CK_3XXX),
CLK(NULL, "fshostusb_fck", &fshostusb_fck, CK_3430ES1), CLK(NULL, "fshostusb_fck", &fshostusb_fck, CK_3430ES1),
...@@ -3356,11 +3356,11 @@ static struct omap_clk omap3xxx_clks[] = { ...@@ -3356,11 +3356,11 @@ static struct omap_clk omap3xxx_clks[] = {
CLK("omap_rng", "ick", &rng_ick, CK_34XX | CK_36XX), CLK("omap_rng", "ick", &rng_ick, CK_34XX | CK_36XX),
CLK(NULL, "sha11_ick", &sha11_ick, CK_34XX | CK_36XX), CLK(NULL, "sha11_ick", &sha11_ick, CK_34XX | CK_36XX),
CLK(NULL, "des1_ick", &des1_ick, CK_34XX | CK_36XX), CLK(NULL, "des1_ick", &des1_ick, CK_34XX | CK_36XX),
CLK("omapdss_dss", "fck", &dss1_alwon_fck_3430es1, CK_3430ES1), CLK(NULL, "dss1_alwon_fck", &dss1_alwon_fck_3430es1, CK_3430ES1),
CLK("omapdss_dss", "fck", &dss1_alwon_fck_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), CLK(NULL, "dss1_alwon_fck", &dss1_alwon_fck_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK("omapdss_dss", "tv_clk", &dss_tv_fck, CK_3XXX), CLK(NULL, "dss_tv_fck", &dss_tv_fck, CK_3XXX),
CLK("omapdss_dss", "video_clk", &dss_96m_fck, CK_3XXX), CLK(NULL, "dss_96m_fck", &dss_96m_fck, CK_3XXX),
CLK("omapdss_dss", "sys_clk", &dss2_alwon_fck, CK_3XXX), CLK(NULL, "dss2_alwon_fck", &dss2_alwon_fck, CK_3XXX),
CLK("omapdss_dss", "ick", &dss_ick_3430es1, CK_3430ES1), CLK("omapdss_dss", "ick", &dss_ick_3430es1, CK_3430ES1),
CLK("omapdss_dss", "ick", &dss_ick_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), CLK("omapdss_dss", "ick", &dss_ick_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK(NULL, "cam_mclk", &cam_mclk, CK_34XX | CK_36XX), CLK(NULL, "cam_mclk", &cam_mclk, CK_34XX | CK_36XX),
...@@ -3385,7 +3385,7 @@ static struct omap_clk omap3xxx_clks[] = { ...@@ -3385,7 +3385,7 @@ static struct omap_clk omap3xxx_clks[] = {
CLK(NULL, "gpt1_fck", &gpt1_fck, CK_3XXX), CLK(NULL, "gpt1_fck", &gpt1_fck, CK_3XXX),
CLK(NULL, "wkup_32k_fck", &wkup_32k_fck, CK_3XXX), CLK(NULL, "wkup_32k_fck", &wkup_32k_fck, CK_3XXX),
CLK(NULL, "gpio1_dbck", &gpio1_dbck, CK_3XXX), CLK(NULL, "gpio1_dbck", &gpio1_dbck, CK_3XXX),
CLK("omap_wdt", "fck", &wdt2_fck, CK_3XXX), CLK(NULL, "wdt2_fck", &wdt2_fck, CK_3XXX),
CLK(NULL, "wkup_l4_ick", &wkup_l4_ick, CK_34XX | CK_36XX), CLK(NULL, "wkup_l4_ick", &wkup_l4_ick, CK_34XX | CK_36XX),
CLK(NULL, "usim_ick", &usim_ick, CK_3430ES2PLUS | CK_36XX), CLK(NULL, "usim_ick", &usim_ick, CK_3430ES2PLUS | CK_36XX),
CLK("omap_wdt", "ick", &wdt2_ick, CK_3XXX), CLK("omap_wdt", "ick", &wdt2_ick, CK_3XXX),
...@@ -3436,9 +3436,9 @@ static struct omap_clk omap3xxx_clks[] = { ...@@ -3436,9 +3436,9 @@ static struct omap_clk omap3xxx_clks[] = {
CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_3XXX), CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_3XXX),
CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_3XXX), CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_3XXX),
CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_3XXX), CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_3XXX),
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_3XXX), CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_3XXX),
CLK("omap-mcbsp.3", "fck", &mcbsp3_fck, CK_3XXX), CLK(NULL, "mcbsp3_fck", &mcbsp3_fck, CK_3XXX),
CLK("omap-mcbsp.4", "fck", &mcbsp4_fck, CK_3XXX), CLK(NULL, "mcbsp4_fck", &mcbsp4_fck, CK_3XXX),
CLK("etb", "emu_src_ck", &emu_src_ck, CK_3XXX), CLK("etb", "emu_src_ck", &emu_src_ck, CK_3XXX),
CLK(NULL, "pclk_fck", &pclk_fck, CK_3XXX), CLK(NULL, "pclk_fck", &pclk_fck, CK_3XXX),
CLK(NULL, "pclkx2_fck", &pclkx2_fck, CK_3XXX), CLK(NULL, "pclkx2_fck", &pclkx2_fck, CK_3XXX),
......
...@@ -2808,19 +2808,39 @@ static struct clk trace_clk_div_ck = { ...@@ -2808,19 +2808,39 @@ static struct clk trace_clk_div_ck = {
/* SCRM aux clk nodes */ /* SCRM aux clk nodes */
static const struct clksel auxclk_sel[] = { static const struct clksel auxclk_src_sel[] = {
{ .parent = &sys_clkin_ck, .rates = div_1_0_rates }, { .parent = &sys_clkin_ck, .rates = div_1_0_rates },
{ .parent = &dpll_core_m3x2_ck, .rates = div_1_1_rates }, { .parent = &dpll_core_m3x2_ck, .rates = div_1_1_rates },
{ .parent = &dpll_per_m3x2_ck, .rates = div_1_2_rates }, { .parent = &dpll_per_m3x2_ck, .rates = div_1_2_rates },
{ .parent = NULL }, { .parent = NULL },
}; };
static struct clk auxclk0_ck = { static const struct clksel_rate div16_1to16_rates[] = {
.name = "auxclk0_ck", { .div = 1, .val = 0, .flags = RATE_IN_4430 },
{ .div = 2, .val = 1, .flags = RATE_IN_4430 },
{ .div = 3, .val = 2, .flags = RATE_IN_4430 },
{ .div = 4, .val = 3, .flags = RATE_IN_4430 },
{ .div = 5, .val = 4, .flags = RATE_IN_4430 },
{ .div = 6, .val = 5, .flags = RATE_IN_4430 },
{ .div = 7, .val = 6, .flags = RATE_IN_4430 },
{ .div = 8, .val = 7, .flags = RATE_IN_4430 },
{ .div = 9, .val = 8, .flags = RATE_IN_4430 },
{ .div = 10, .val = 9, .flags = RATE_IN_4430 },
{ .div = 11, .val = 10, .flags = RATE_IN_4430 },
{ .div = 12, .val = 11, .flags = RATE_IN_4430 },
{ .div = 13, .val = 12, .flags = RATE_IN_4430 },
{ .div = 14, .val = 13, .flags = RATE_IN_4430 },
{ .div = 15, .val = 14, .flags = RATE_IN_4430 },
{ .div = 16, .val = 15, .flags = RATE_IN_4430 },
{ .div = 0 },
};
static struct clk auxclk0_src_ck = {
.name = "auxclk0_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK0, .clksel_reg = OMAP4_SCRM_AUXCLK0,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
...@@ -2828,12 +2848,29 @@ static struct clk auxclk0_ck = { ...@@ -2828,12 +2848,29 @@ static struct clk auxclk0_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static struct clk auxclk1_ck = { static const struct clksel auxclk0_sel[] = {
.name = "auxclk1_ck", { .parent = &auxclk0_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk0_ck = {
.name = "auxclk0_ck",
.parent = &auxclk0_src_ck,
.clksel = auxclk0_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK0,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static struct clk auxclk1_src_ck = {
.name = "auxclk1_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK1, .clksel_reg = OMAP4_SCRM_AUXCLK1,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
...@@ -2841,12 +2878,29 @@ static struct clk auxclk1_ck = { ...@@ -2841,12 +2878,29 @@ static struct clk auxclk1_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static struct clk auxclk2_ck = { static const struct clksel auxclk1_sel[] = {
.name = "auxclk2_ck", { .parent = &auxclk1_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk1_ck = {
.name = "auxclk1_ck",
.parent = &auxclk1_src_ck,
.clksel = auxclk1_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK1,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static struct clk auxclk2_src_ck = {
.name = "auxclk2_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK2, .clksel_reg = OMAP4_SCRM_AUXCLK2,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
...@@ -2854,12 +2908,29 @@ static struct clk auxclk2_ck = { ...@@ -2854,12 +2908,29 @@ static struct clk auxclk2_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static struct clk auxclk3_ck = { static const struct clksel auxclk2_sel[] = {
.name = "auxclk3_ck", { .parent = &auxclk2_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk2_ck = {
.name = "auxclk2_ck",
.parent = &auxclk2_src_ck,
.clksel = auxclk2_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK2,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static struct clk auxclk3_src_ck = {
.name = "auxclk3_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK3, .clksel_reg = OMAP4_SCRM_AUXCLK3,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
...@@ -2867,12 +2938,29 @@ static struct clk auxclk3_ck = { ...@@ -2867,12 +2938,29 @@ static struct clk auxclk3_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static struct clk auxclk4_ck = { static const struct clksel auxclk3_sel[] = {
.name = "auxclk4_ck", { .parent = &auxclk3_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk3_ck = {
.name = "auxclk3_ck",
.parent = &auxclk3_src_ck,
.clksel = auxclk3_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK3,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static struct clk auxclk4_src_ck = {
.name = "auxclk4_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK4, .clksel_reg = OMAP4_SCRM_AUXCLK4,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
...@@ -2880,12 +2968,29 @@ static struct clk auxclk4_ck = { ...@@ -2880,12 +2968,29 @@ static struct clk auxclk4_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static struct clk auxclk5_ck = { static const struct clksel auxclk4_sel[] = {
.name = "auxclk5_ck", { .parent = &auxclk4_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk4_ck = {
.name = "auxclk4_ck",
.parent = &auxclk4_src_ck,
.clksel = auxclk4_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK4,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static struct clk auxclk5_src_ck = {
.name = "auxclk5_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK5, .clksel_reg = OMAP4_SCRM_AUXCLK5,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
...@@ -2893,6 +2998,23 @@ static struct clk auxclk5_ck = { ...@@ -2893,6 +2998,23 @@ static struct clk auxclk5_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static const struct clksel auxclk5_sel[] = {
{ .parent = &auxclk5_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk5_ck = {
.name = "auxclk5_ck",
.parent = &auxclk5_src_ck,
.clksel = auxclk5_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK5,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static const struct clksel auxclkreq_sel[] = { static const struct clksel auxclkreq_sel[] = {
{ .parent = &auxclk0_ck, .rates = div_1_0_rates }, { .parent = &auxclk0_ck, .rates = div_1_0_rates },
{ .parent = &auxclk1_ck, .rates = div_1_1_rates }, { .parent = &auxclk1_ck, .rates = div_1_1_rates },
...@@ -3093,12 +3215,12 @@ static struct omap_clk omap44xx_clks[] = { ...@@ -3093,12 +3215,12 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "gpio6_ick", &gpio6_ick, CK_443X), CLK(NULL, "gpio6_ick", &gpio6_ick, CK_443X),
CLK(NULL, "gpmc_ick", &gpmc_ick, CK_443X), CLK(NULL, "gpmc_ick", &gpmc_ick, CK_443X),
CLK(NULL, "gpu_fck", &gpu_fck, CK_443X), CLK(NULL, "gpu_fck", &gpu_fck, CK_443X),
CLK("omap2_hdq.0", "fck", &hdq1w_fck, CK_443X), CLK(NULL, "hdq1w_fck", &hdq1w_fck, CK_443X),
CLK(NULL, "hsi_fck", &hsi_fck, CK_443X), CLK(NULL, "hsi_fck", &hsi_fck, CK_443X),
CLK("omap_i2c.1", "fck", &i2c1_fck, CK_443X), CLK(NULL, "i2c1_fck", &i2c1_fck, CK_443X),
CLK("omap_i2c.2", "fck", &i2c2_fck, CK_443X), CLK(NULL, "i2c2_fck", &i2c2_fck, CK_443X),
CLK("omap_i2c.3", "fck", &i2c3_fck, CK_443X), CLK(NULL, "i2c3_fck", &i2c3_fck, CK_443X),
CLK("omap_i2c.4", "fck", &i2c4_fck, CK_443X), CLK(NULL, "i2c4_fck", &i2c4_fck, CK_443X),
CLK(NULL, "ipu_fck", &ipu_fck, CK_443X), CLK(NULL, "ipu_fck", &ipu_fck, CK_443X),
CLK(NULL, "iss_ctrlclk", &iss_ctrlclk, CK_443X), CLK(NULL, "iss_ctrlclk", &iss_ctrlclk, CK_443X),
CLK(NULL, "iss_fck", &iss_fck, CK_443X), CLK(NULL, "iss_fck", &iss_fck, CK_443X),
...@@ -3109,23 +3231,23 @@ static struct omap_clk omap44xx_clks[] = { ...@@ -3109,23 +3231,23 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "mcasp_sync_mux_ck", &mcasp_sync_mux_ck, CK_443X), CLK(NULL, "mcasp_sync_mux_ck", &mcasp_sync_mux_ck, CK_443X),
CLK(NULL, "mcasp_fck", &mcasp_fck, CK_443X), CLK(NULL, "mcasp_fck", &mcasp_fck, CK_443X),
CLK(NULL, "mcbsp1_sync_mux_ck", &mcbsp1_sync_mux_ck, CK_443X), CLK(NULL, "mcbsp1_sync_mux_ck", &mcbsp1_sync_mux_ck, CK_443X),
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_443X), CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_443X),
CLK(NULL, "mcbsp2_sync_mux_ck", &mcbsp2_sync_mux_ck, CK_443X), CLK(NULL, "mcbsp2_sync_mux_ck", &mcbsp2_sync_mux_ck, CK_443X),
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_443X), CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_443X),
CLK(NULL, "mcbsp3_sync_mux_ck", &mcbsp3_sync_mux_ck, CK_443X), CLK(NULL, "mcbsp3_sync_mux_ck", &mcbsp3_sync_mux_ck, CK_443X),
CLK("omap-mcbsp.3", "fck", &mcbsp3_fck, CK_443X), CLK(NULL, "mcbsp3_fck", &mcbsp3_fck, CK_443X),
CLK(NULL, "mcbsp4_sync_mux_ck", &mcbsp4_sync_mux_ck, CK_443X), CLK(NULL, "mcbsp4_sync_mux_ck", &mcbsp4_sync_mux_ck, CK_443X),
CLK("omap-mcbsp.4", "fck", &mcbsp4_fck, CK_443X), CLK(NULL, "mcbsp4_fck", &mcbsp4_fck, CK_443X),
CLK(NULL, "mcpdm_fck", &mcpdm_fck, CK_443X), CLK(NULL, "mcpdm_fck", &mcpdm_fck, CK_443X),
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_443X), CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_443X),
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_443X), CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_443X),
CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_443X), CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_443X),
CLK("omap2_mcspi.4", "fck", &mcspi4_fck, CK_443X), CLK(NULL, "mcspi4_fck", &mcspi4_fck, CK_443X),
CLK("omap_hsmmc.0", "fck", &mmc1_fck, CK_443X), CLK(NULL, "mmc1_fck", &mmc1_fck, CK_443X),
CLK("omap_hsmmc.1", "fck", &mmc2_fck, CK_443X), CLK(NULL, "mmc2_fck", &mmc2_fck, CK_443X),
CLK("omap_hsmmc.2", "fck", &mmc3_fck, CK_443X), CLK(NULL, "mmc3_fck", &mmc3_fck, CK_443X),
CLK("omap_hsmmc.3", "fck", &mmc4_fck, CK_443X), CLK(NULL, "mmc4_fck", &mmc4_fck, CK_443X),
CLK("omap_hsmmc.4", "fck", &mmc5_fck, CK_443X), CLK(NULL, "mmc5_fck", &mmc5_fck, CK_443X),
CLK(NULL, "ocp2scp_usb_phy_phy_48m", &ocp2scp_usb_phy_phy_48m, CK_443X), CLK(NULL, "ocp2scp_usb_phy_phy_48m", &ocp2scp_usb_phy_phy_48m, CK_443X),
CLK(NULL, "ocp2scp_usb_phy_ick", &ocp2scp_usb_phy_ick, CK_443X), CLK(NULL, "ocp2scp_usb_phy_ick", &ocp2scp_usb_phy_ick, CK_443X),
CLK(NULL, "ocp_wp_noc_ick", &ocp_wp_noc_ick, CK_443X), CLK(NULL, "ocp_wp_noc_ick", &ocp_wp_noc_ick, CK_443X),
...@@ -3182,21 +3304,27 @@ static struct omap_clk omap44xx_clks[] = { ...@@ -3182,21 +3304,27 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "usim_ck", &usim_ck, CK_443X), CLK(NULL, "usim_ck", &usim_ck, CK_443X),
CLK(NULL, "usim_fclk", &usim_fclk, CK_443X), CLK(NULL, "usim_fclk", &usim_fclk, CK_443X),
CLK(NULL, "usim_fck", &usim_fck, CK_443X), CLK(NULL, "usim_fck", &usim_fck, CK_443X),
CLK("omap_wdt", "fck", &wd_timer2_fck, CK_443X), CLK(NULL, "wd_timer2_fck", &wd_timer2_fck, CK_443X),
CLK(NULL, "wd_timer3_fck", &wd_timer3_fck, CK_443X), CLK(NULL, "wd_timer3_fck", &wd_timer3_fck, CK_443X),
CLK(NULL, "stm_clk_div_ck", &stm_clk_div_ck, CK_443X), CLK(NULL, "stm_clk_div_ck", &stm_clk_div_ck, CK_443X),
CLK(NULL, "trace_clk_div_ck", &trace_clk_div_ck, CK_443X), CLK(NULL, "trace_clk_div_ck", &trace_clk_div_ck, CK_443X),
CLK(NULL, "auxclk0_src_ck", &auxclk0_src_ck, CK_443X),
CLK(NULL, "auxclk0_ck", &auxclk0_ck, CK_443X), CLK(NULL, "auxclk0_ck", &auxclk0_ck, CK_443X),
CLK(NULL, "auxclk1_ck", &auxclk1_ck, CK_443X),
CLK(NULL, "auxclk2_ck", &auxclk2_ck, CK_443X),
CLK(NULL, "auxclk3_ck", &auxclk3_ck, CK_443X),
CLK(NULL, "auxclk4_ck", &auxclk4_ck, CK_443X),
CLK(NULL, "auxclk5_ck", &auxclk5_ck, CK_443X),
CLK(NULL, "auxclkreq0_ck", &auxclkreq0_ck, CK_443X), CLK(NULL, "auxclkreq0_ck", &auxclkreq0_ck, CK_443X),
CLK(NULL, "auxclk1_src_ck", &auxclk1_src_ck, CK_443X),
CLK(NULL, "auxclk1_ck", &auxclk1_ck, CK_443X),
CLK(NULL, "auxclkreq1_ck", &auxclkreq1_ck, CK_443X), CLK(NULL, "auxclkreq1_ck", &auxclkreq1_ck, CK_443X),
CLK(NULL, "auxclk2_src_ck", &auxclk2_src_ck, CK_443X),
CLK(NULL, "auxclk2_ck", &auxclk2_ck, CK_443X),
CLK(NULL, "auxclkreq2_ck", &auxclkreq2_ck, CK_443X), CLK(NULL, "auxclkreq2_ck", &auxclkreq2_ck, CK_443X),
CLK(NULL, "auxclk3_src_ck", &auxclk3_src_ck, CK_443X),
CLK(NULL, "auxclk3_ck", &auxclk3_ck, CK_443X),
CLK(NULL, "auxclkreq3_ck", &auxclkreq3_ck, CK_443X), CLK(NULL, "auxclkreq3_ck", &auxclkreq3_ck, CK_443X),
CLK(NULL, "auxclk4_src_ck", &auxclk4_src_ck, CK_443X),
CLK(NULL, "auxclk4_ck", &auxclk4_ck, CK_443X),
CLK(NULL, "auxclkreq4_ck", &auxclkreq4_ck, CK_443X), CLK(NULL, "auxclkreq4_ck", &auxclkreq4_ck, CK_443X),
CLK(NULL, "auxclk5_src_ck", &auxclk5_src_ck, CK_443X),
CLK(NULL, "auxclk5_ck", &auxclk5_ck, CK_443X),
CLK(NULL, "auxclkreq5_ck", &auxclkreq5_ck, CK_443X), CLK(NULL, "auxclkreq5_ck", &auxclkreq5_ck, CK_443X),
CLK(NULL, "gpmc_ck", &dummy_ck, CK_443X), CLK(NULL, "gpmc_ck", &dummy_ck, CK_443X),
CLK(NULL, "gpt1_ick", &dummy_ck, CK_443X), CLK(NULL, "gpt1_ick", &dummy_ck, CK_443X),
...@@ -3251,6 +3379,7 @@ int __init omap4xxx_clk_init(void) ...@@ -3251,6 +3379,7 @@ int __init omap4xxx_clk_init(void)
} }
clk_init(&omap2_clk_functions); clk_init(&omap2_clk_functions);
omap2_clk_disable_clkdm_control();
for (c = omap44xx_clks; c < omap44xx_clks + ARRAY_SIZE(omap44xx_clks); for (c = omap44xx_clks; c < omap44xx_clks + ARRAY_SIZE(omap44xx_clks);
c++) c++)
......
/* /*
* OMAP2/3/4 clockdomain framework functions * OMAP2/3/4 clockdomain framework functions
* *
* Copyright (C) 2008-2010 Texas Instruments, Inc. * Copyright (C) 2008-2011 Texas Instruments, Inc.
* Copyright (C) 2008-2010 Nokia Corporation * Copyright (C) 2008-2011 Nokia Corporation
* *
* Written by Paul Walmsley and Jouni Högander * Written by Paul Walmsley and Jouni Högander
* Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com> * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
...@@ -92,6 +92,8 @@ static int _clkdm_register(struct clockdomain *clkdm) ...@@ -92,6 +92,8 @@ static int _clkdm_register(struct clockdomain *clkdm)
pwrdm_add_clkdm(pwrdm, clkdm); pwrdm_add_clkdm(pwrdm, clkdm);
spin_lock_init(&clkdm->lock);
pr_debug("clockdomain: registered %s\n", clkdm->name); pr_debug("clockdomain: registered %s\n", clkdm->name);
return 0; return 0;
...@@ -690,6 +692,9 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) ...@@ -690,6 +692,9 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
*/ */
int clkdm_sleep(struct clockdomain *clkdm) int clkdm_sleep(struct clockdomain *clkdm)
{ {
int ret;
unsigned long flags;
if (!clkdm) if (!clkdm)
return -EINVAL; return -EINVAL;
...@@ -704,7 +709,11 @@ int clkdm_sleep(struct clockdomain *clkdm) ...@@ -704,7 +709,11 @@ int clkdm_sleep(struct clockdomain *clkdm)
pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name); pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
return arch_clkdm->clkdm_sleep(clkdm); spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
ret = arch_clkdm->clkdm_sleep(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags);
return ret;
} }
/** /**
...@@ -718,6 +727,9 @@ int clkdm_sleep(struct clockdomain *clkdm) ...@@ -718,6 +727,9 @@ int clkdm_sleep(struct clockdomain *clkdm)
*/ */
int clkdm_wakeup(struct clockdomain *clkdm) int clkdm_wakeup(struct clockdomain *clkdm)
{ {
int ret;
unsigned long flags;
if (!clkdm) if (!clkdm)
return -EINVAL; return -EINVAL;
...@@ -732,7 +744,11 @@ int clkdm_wakeup(struct clockdomain *clkdm) ...@@ -732,7 +744,11 @@ int clkdm_wakeup(struct clockdomain *clkdm)
pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name); pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
return arch_clkdm->clkdm_wakeup(clkdm); spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
ret = arch_clkdm->clkdm_wakeup(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags);
return ret;
} }
/** /**
...@@ -747,6 +763,8 @@ int clkdm_wakeup(struct clockdomain *clkdm) ...@@ -747,6 +763,8 @@ int clkdm_wakeup(struct clockdomain *clkdm)
*/ */
void clkdm_allow_idle(struct clockdomain *clkdm) void clkdm_allow_idle(struct clockdomain *clkdm)
{ {
unsigned long flags;
if (!clkdm) if (!clkdm)
return; return;
...@@ -762,8 +780,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm) ...@@ -762,8 +780,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm)
pr_debug("clockdomain: enabling automatic idle transitions for %s\n", pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
clkdm->name); clkdm->name);
spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED;
arch_clkdm->clkdm_allow_idle(clkdm); arch_clkdm->clkdm_allow_idle(clkdm);
pwrdm_clkdm_state_switch(clkdm); pwrdm_clkdm_state_switch(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags);
} }
/** /**
...@@ -777,6 +798,8 @@ void clkdm_allow_idle(struct clockdomain *clkdm) ...@@ -777,6 +798,8 @@ void clkdm_allow_idle(struct clockdomain *clkdm)
*/ */
void clkdm_deny_idle(struct clockdomain *clkdm) void clkdm_deny_idle(struct clockdomain *clkdm)
{ {
unsigned long flags;
if (!clkdm) if (!clkdm)
return; return;
...@@ -792,11 +815,90 @@ void clkdm_deny_idle(struct clockdomain *clkdm) ...@@ -792,11 +815,90 @@ void clkdm_deny_idle(struct clockdomain *clkdm)
pr_debug("clockdomain: disabling automatic idle transitions for %s\n", pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
clkdm->name); clkdm->name);
spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
arch_clkdm->clkdm_deny_idle(clkdm); arch_clkdm->clkdm_deny_idle(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags);
}
/**
* clkdm_in_hwsup - is clockdomain @clkdm have hardware-supervised idle enabled?
* @clkdm: struct clockdomain *
*
* Returns true if clockdomain @clkdm currently has
* hardware-supervised idle enabled, or false if it does not or if
* @clkdm is NULL. It is only valid to call this function after
* clkdm_init() has been called. This function does not actually read
* bits from the hardware; it instead tests an in-memory flag that is
* changed whenever the clockdomain code changes the auto-idle mode.
*/
bool clkdm_in_hwsup(struct clockdomain *clkdm)
{
bool ret;
unsigned long flags;
if (!clkdm)
return false;
spin_lock_irqsave(&clkdm->lock, flags);
ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false;
spin_unlock_irqrestore(&clkdm->lock, flags);
return ret;
}
/* Clockdomain-to-clock/hwmod framework interface code */
static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)
{
unsigned long flags;
if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)
return -EINVAL;
/*
* For arch's with no autodeps, clkcm_clk_enable
* should be called for every clock instance or hwmod that is
* enabled, so the clkdm can be force woken up.
*/
if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps)
return 0;
spin_lock_irqsave(&clkdm->lock, flags);
arch_clkdm->clkdm_clk_enable(clkdm);
pwrdm_wait_transition(clkdm->pwrdm.ptr);
pwrdm_clkdm_state_switch(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags);
pr_debug("clockdomain: clkdm %s: enabled\n", clkdm->name);
return 0;
} }
static int _clkdm_clk_hwmod_disable(struct clockdomain *clkdm)
{
unsigned long flags;
if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
return -EINVAL;
if (atomic_read(&clkdm->usecount) == 0) {
WARN_ON(1); /* underflow */
return -ERANGE;
}
if (atomic_dec_return(&clkdm->usecount) > 0)
return 0;
spin_lock_irqsave(&clkdm->lock, flags);
arch_clkdm->clkdm_clk_disable(clkdm);
pwrdm_clkdm_state_switch(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags);
/* Clockdomain-to-clock framework interface code */ pr_debug("clockdomain: clkdm %s: disabled\n", clkdm->name);
return 0;
}
/** /**
* clkdm_clk_enable - add an enabled downstream clock to this clkdm * clkdm_clk_enable - add an enabled downstream clock to this clkdm
...@@ -819,25 +921,10 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) ...@@ -819,25 +921,10 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
* downstream clocks for debugging purposes? * downstream clocks for debugging purposes?
*/ */
if (!clkdm || !clk) if (!clk)
return -EINVAL; return -EINVAL;
if (!arch_clkdm || !arch_clkdm->clkdm_clk_enable) return _clkdm_clk_hwmod_enable(clkdm);
return -EINVAL;
if (atomic_inc_return(&clkdm->usecount) > 1)
return 0;
/* Clockdomain now has one enabled downstream clock */
pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name,
clk->name);
arch_clkdm->clkdm_clk_enable(clkdm);
pwrdm_wait_transition(clkdm->pwrdm.ptr);
pwrdm_clkdm_state_switch(clkdm);
return 0;
} }
/** /**
...@@ -850,9 +937,8 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) ...@@ -850,9 +937,8 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
* clockdomain usecount goes to 0, put the clockdomain to sleep * clockdomain usecount goes to 0, put the clockdomain to sleep
* (software-supervised mode) or remove the clkdm autodependencies * (software-supervised mode) or remove the clkdm autodependencies
* (hardware-supervised mode). Returns -EINVAL if passed null * (hardware-supervised mode). Returns -EINVAL if passed null
* pointers; -ERANGE if the @clkdm usecount underflows and debugging * pointers; -ERANGE if the @clkdm usecount underflows; or returns 0
* is enabled; or returns 0 upon success or if the clockdomain is in * upon success or if the clockdomain is in hwsup idle mode.
* hwsup idle mode.
*/ */
int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
{ {
...@@ -861,30 +947,72 @@ int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) ...@@ -861,30 +947,72 @@ int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
* downstream clocks for debugging purposes? * downstream clocks for debugging purposes?
*/ */
if (!clkdm || !clk) if (!clk)
return -EINVAL;
if (!arch_clkdm || !arch_clkdm->clkdm_clk_disable)
return -EINVAL; return -EINVAL;
#ifdef DEBUG return _clkdm_clk_hwmod_disable(clkdm);
if (atomic_read(&clkdm->usecount) == 0) { }
WARN_ON(1); /* underflow */
return -ERANGE;
}
#endif
if (atomic_dec_return(&clkdm->usecount) > 0) /**
* clkdm_hwmod_enable - add an enabled downstream hwmod to this clkdm
* @clkdm: struct clockdomain *
* @oh: struct omap_hwmod * of the enabled downstream hwmod
*
* Increment the usecount of the clockdomain @clkdm and ensure that it
* is awake before @oh is enabled. Intended to be called by
* module_enable() code.
* If the clockdomain is in software-supervised idle mode, force the
* clockdomain to wake. If the clockdomain is in hardware-supervised idle
* mode, add clkdm-pwrdm autodependencies, to ensure that devices in the
* clockdomain can be read from/written to by on-chip processors.
* Returns -EINVAL if passed null pointers;
* returns 0 upon success or if the clockdomain is in hwsup idle mode.
*/
int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh)
{
/* The clkdm attribute does not exist yet prior OMAP4 */
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return 0; return 0;
/* All downstream clocks of this clockdomain are now disabled */ /*
* XXX Rewrite this code to maintain a list of enabled
* downstream hwmods for debugging purposes?
*/
pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name, if (!oh)
clk->name); return -EINVAL;
arch_clkdm->clkdm_clk_disable(clkdm); return _clkdm_clk_hwmod_enable(clkdm);
pwrdm_clkdm_state_switch(clkdm); }
/**
* clkdm_hwmod_disable - remove an enabled downstream hwmod from this clkdm
* @clkdm: struct clockdomain *
* @oh: struct omap_hwmod * of the disabled downstream hwmod
*
* Decrement the usecount of this clockdomain @clkdm when @oh is
* disabled. Intended to be called by module_disable() code.
* If the clockdomain usecount goes to 0, put the clockdomain to sleep
* (software-supervised mode) or remove the clkdm autodependencies
* (hardware-supervised mode).
* Returns -EINVAL if passed null pointers; -ERANGE if the @clkdm usecount
* underflows; or returns 0 upon success or if the clockdomain is in hwsup
* idle mode.
*/
int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
{
/* The clkdm attribute does not exist yet prior OMAP4 */
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return 0; return 0;
/*
* XXX Rewrite this code to maintain a list of enabled
* downstream hwmods for debugging purposes?
*/
if (!oh)
return -EINVAL;
return _clkdm_clk_hwmod_disable(clkdm);
} }
...@@ -17,9 +17,11 @@ ...@@ -17,9 +17,11 @@
#define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H #define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H
#include <linux/init.h> #include <linux/init.h>
#include <linux/spinlock.h>
#include "powerdomain.h" #include "powerdomain.h"
#include <plat/clock.h> #include <plat/clock.h>
#include <plat/omap_hwmod.h>
#include <plat/cpu.h> #include <plat/cpu.h>
/* /*
...@@ -82,6 +84,9 @@ struct clkdm_dep { ...@@ -82,6 +84,9 @@ struct clkdm_dep {
const struct omap_chip_id omap_chip; const struct omap_chip_id omap_chip;
}; };
/* Possible flags for struct clockdomain._flags */
#define _CLKDM_FLAG_HWSUP_ENABLED BIT(0)
/** /**
* struct clockdomain - OMAP clockdomain * struct clockdomain - OMAP clockdomain
* @name: clockdomain name * @name: clockdomain name
...@@ -89,6 +94,7 @@ struct clkdm_dep { ...@@ -89,6 +94,7 @@ struct clkdm_dep {
* @clktrctrl_reg: CLKSTCTRL reg for the given clock domain * @clktrctrl_reg: CLKSTCTRL reg for the given clock domain
* @clktrctrl_mask: CLKTRCTRL/AUTOSTATE field mask in CM_CLKSTCTRL reg * @clktrctrl_mask: CLKTRCTRL/AUTOSTATE field mask in CM_CLKSTCTRL reg
* @flags: Clockdomain capability flags * @flags: Clockdomain capability flags
* @_flags: Flags for use only by internal clockdomain code
* @dep_bit: Bit shift of this clockdomain's PM_WKDEP/CM_SLEEPDEP bit * @dep_bit: Bit shift of this clockdomain's PM_WKDEP/CM_SLEEPDEP bit
* @prcm_partition: (OMAP4 only) PRCM partition ID for this clkdm's registers * @prcm_partition: (OMAP4 only) PRCM partition ID for this clkdm's registers
* @cm_inst: (OMAP4 only) CM instance register offset * @cm_inst: (OMAP4 only) CM instance register offset
...@@ -113,6 +119,7 @@ struct clockdomain { ...@@ -113,6 +119,7 @@ struct clockdomain {
} pwrdm; } pwrdm;
const u16 clktrctrl_mask; const u16 clktrctrl_mask;
const u8 flags; const u8 flags;
u8 _flags;
const u8 dep_bit; const u8 dep_bit;
const u8 prcm_partition; const u8 prcm_partition;
const s16 cm_inst; const s16 cm_inst;
...@@ -122,6 +129,7 @@ struct clockdomain { ...@@ -122,6 +129,7 @@ struct clockdomain {
const struct omap_chip_id omap_chip; const struct omap_chip_id omap_chip;
atomic_t usecount; atomic_t usecount;
struct list_head node; struct list_head node;
spinlock_t lock;
}; };
/** /**
...@@ -177,12 +185,15 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm); ...@@ -177,12 +185,15 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm);
void clkdm_allow_idle(struct clockdomain *clkdm); void clkdm_allow_idle(struct clockdomain *clkdm);
void clkdm_deny_idle(struct clockdomain *clkdm); void clkdm_deny_idle(struct clockdomain *clkdm);
bool clkdm_in_hwsup(struct clockdomain *clkdm);
int clkdm_wakeup(struct clockdomain *clkdm); int clkdm_wakeup(struct clockdomain *clkdm);
int clkdm_sleep(struct clockdomain *clkdm); int clkdm_sleep(struct clockdomain *clkdm);
int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk); int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk);
int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk); int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk);
int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh);
int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh);
extern void __init omap2xxx_clockdomains_init(void); extern void __init omap2xxx_clockdomains_init(void);
extern void __init omap3xxx_clockdomains_init(void); extern void __init omap3xxx_clockdomains_init(void);
......
...@@ -183,7 +183,8 @@ static int omap2_clkdm_clk_enable(struct clockdomain *clkdm) ...@@ -183,7 +183,8 @@ static int omap2_clkdm_clk_enable(struct clockdomain *clkdm)
_clkdm_add_autodeps(clkdm); _clkdm_add_autodeps(clkdm);
_enable_hwsup(clkdm); _enable_hwsup(clkdm);
} else { } else {
clkdm_wakeup(clkdm); if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
omap2_clkdm_wakeup(clkdm);
} }
return 0; return 0;
...@@ -205,7 +206,8 @@ static int omap2_clkdm_clk_disable(struct clockdomain *clkdm) ...@@ -205,7 +206,8 @@ static int omap2_clkdm_clk_disable(struct clockdomain *clkdm)
_clkdm_del_autodeps(clkdm); _clkdm_del_autodeps(clkdm);
_enable_hwsup(clkdm); _enable_hwsup(clkdm);
} else { } else {
clkdm_sleep(clkdm); if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
omap2_clkdm_sleep(clkdm);
} }
return 0; return 0;
......
...@@ -95,13 +95,8 @@ static void omap4_clkdm_deny_idle(struct clockdomain *clkdm) ...@@ -95,13 +95,8 @@ static void omap4_clkdm_deny_idle(struct clockdomain *clkdm)
static int omap4_clkdm_clk_enable(struct clockdomain *clkdm) static int omap4_clkdm_clk_enable(struct clockdomain *clkdm)
{ {
bool hwsup = false; if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
return omap4_clkdm_wakeup(clkdm);
hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition,
clkdm->cm_inst, clkdm->clkdm_offs);
if (!hwsup)
clkdm_wakeup(clkdm);
return 0; return 0;
} }
...@@ -113,8 +108,8 @@ static int omap4_clkdm_clk_disable(struct clockdomain *clkdm) ...@@ -113,8 +108,8 @@ static int omap4_clkdm_clk_disable(struct clockdomain *clkdm)
hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition, hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition,
clkdm->cm_inst, clkdm->clkdm_offs); clkdm->cm_inst, clkdm->clkdm_offs);
if (!hwsup) if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
clkdm_sleep(clkdm); omap4_clkdm_sleep(clkdm);
return 0; return 0;
} }
......
...@@ -565,7 +565,7 @@ static struct clockdomain ducati_44xx_clkdm = { ...@@ -565,7 +565,7 @@ static struct clockdomain ducati_44xx_clkdm = {
}; };
static struct clockdomain mpu_44xx_clkdm = { static struct clockdomain mpu_44xx_clkdm = {
.name = "mpu_clkdm", .name = "mpuss_clkdm",
.pwrdm = { .name = "mpu_pwrdm" }, .pwrdm = { .name = "mpu_pwrdm" },
.prcm_partition = OMAP4430_CM1_PARTITION, .prcm_partition = OMAP4430_CM1_PARTITION,
.cm_inst = OMAP4430_CM1_MPU_INST, .cm_inst = OMAP4430_CM1_MPU_INST,
......
/* /*
* OMAP4 Clock Management (CM) definitions * OMAP4 Clock Management (CM) definitions
* *
* Copyright (C) 2007-2009 Texas Instruments, Inc. * Copyright (C) 2007-2011 Texas Instruments, Inc.
* Copyright (C) 2007-2009 Nokia Corporation * Copyright (C) 2007-2009 Nokia Corporation
* *
* Written by Paul Walmsley * Written by Paul Walmsley
...@@ -23,10 +23,4 @@ ...@@ -23,10 +23,4 @@
#define OMAP4_CM_CLKSTCTRL 0x0000 #define OMAP4_CM_CLKSTCTRL 0x0000
#define OMAP4_CM_STATICDEP 0x0004 #define OMAP4_CM_STATICDEP 0x0004
/* Function prototypes */
# ifndef __ASSEMBLER__
extern int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg);
# endif
#endif #endif
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* OMAP4 CM instance functions * OMAP4 CM instance functions
* *
* Copyright (C) 2009 Nokia Corporation * Copyright (C) 2009 Nokia Corporation
* Copyright (C) 2011 Texas Instruments, Inc.
* Paul Walmsley * Paul Walmsley
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -32,6 +33,22 @@ ...@@ -32,6 +33,22 @@
#include "prm44xx.h" #include "prm44xx.h"
#include "prcm_mpu44xx.h" #include "prcm_mpu44xx.h"
/*
* CLKCTRL_IDLEST_*: possible values for the CM_*_CLKCTRL.IDLEST bitfield:
*
* 0x0 func: Module is fully functional, including OCP
* 0x1 trans: Module is performing transition: wakeup, or sleep, or sleep
* abortion
* 0x2 idle: Module is in Idle mode (only OCP part). It is functional if
* using separate functional clock
* 0x3 disabled: Module is disabled and cannot be accessed
*
*/
#define CLKCTRL_IDLEST_FUNCTIONAL 0x0
#define CLKCTRL_IDLEST_INTRANSITION 0x1
#define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2
#define CLKCTRL_IDLEST_DISABLED 0x3
static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = { static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = {
[OMAP4430_INVALID_PRCM_PARTITION] = 0, [OMAP4430_INVALID_PRCM_PARTITION] = 0,
[OMAP4430_PRM_PARTITION] = OMAP4430_PRM_BASE, [OMAP4430_PRM_PARTITION] = OMAP4430_PRM_BASE,
...@@ -41,6 +58,48 @@ static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = { ...@@ -41,6 +58,48 @@ static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = {
[OMAP4430_PRCM_MPU_PARTITION] = OMAP4430_PRCM_MPU_BASE, [OMAP4430_PRCM_MPU_PARTITION] = OMAP4430_PRCM_MPU_BASE,
}; };
/* Private functions */
/**
* _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
* @inst: CM instance register offset (*_INST macro)
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
*
* Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to
* bit 0.
*/
static u32 _clkctrl_idlest(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
{
u32 v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs);
v &= OMAP4430_IDLEST_MASK;
v >>= OMAP4430_IDLEST_SHIFT;
return v;
}
/**
* _is_module_ready - can module registers be accessed without causing an abort?
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
* @inst: CM instance register offset (*_INST macro)
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
*
* Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either
* *FUNCTIONAL or *INTERFACE_IDLE; false otherwise.
*/
static bool _is_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
{
u32 v;
v = _clkctrl_idlest(part, inst, cdoffs, clkctrl_offs);
return (v == CLKCTRL_IDLEST_FUNCTIONAL ||
v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false;
}
/* Public functions */
/* Read a register in a CM instance */ /* Read a register in a CM instance */
u32 omap4_cminst_read_inst_reg(u8 part, s16 inst, u16 idx) u32 omap4_cminst_read_inst_reg(u8 part, s16 inst, u16 idx)
{ {
...@@ -200,36 +259,93 @@ void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs) ...@@ -200,36 +259,93 @@ void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs)
*/ */
/** /**
* omap4_cm_wait_module_ready - wait for a module to be in 'func' state * omap4_cminst_wait_module_ready - wait for a module to be in 'func' state
* @clkctrl_reg: CLKCTRL module address * @part: PRCM partition ID that the CM_CLKCTRL register exists in
* @inst: CM instance register offset (*_INST macro)
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
* *
* Wait for the module IDLEST to be functional. If the idle state is in any * Wait for the module IDLEST to be functional. If the idle state is in any
* the non functional state (trans, idle or disabled), module and thus the * the non functional state (trans, idle or disabled), module and thus the
* sysconfig cannot be accessed and will probably lead to an "imprecise * sysconfig cannot be accessed and will probably lead to an "imprecise
* external abort" * external abort"
*/
int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs,
u16 clkctrl_offs)
{
int i = 0;
if (!clkctrl_offs)
return 0;
omap_test_timeout(_is_module_ready(part, inst, cdoffs, clkctrl_offs),
MAX_MODULE_READY_TIME, i);
return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
}
/**
* omap4_cminst_wait_module_idle - wait for a module to be in 'disabled'
* state
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
* @inst: CM instance register offset (*_INST macro)
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
* *
* Module idle state: * Wait for the module IDLEST to be disabled. Some PRCM transition,
* 0x0 func: Module is fully functional, including OCP * like reset assertion or parent clock de-activation must wait the
* 0x1 trans: Module is performing transition: wakeup, or sleep, or sleep * module to be fully disabled.
* abortion
* 0x2 idle: Module is in Idle mode (only OCP part). It is functional if
* using separate functional clock
* 0x3 disabled: Module is disabled and cannot be accessed
*
*/ */
int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg) int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
{ {
int i = 0; int i = 0;
if (!clkctrl_reg) if (!clkctrl_offs)
return 0; return 0;
omap_test_timeout(( omap_test_timeout((_clkctrl_idlest(part, inst, cdoffs, clkctrl_offs) ==
((__raw_readl(clkctrl_reg) & OMAP4430_IDLEST_MASK) == 0) || CLKCTRL_IDLEST_DISABLED),
(((__raw_readl(clkctrl_reg) & OMAP4430_IDLEST_MASK) >>
OMAP4430_IDLEST_SHIFT) == 0x2)),
MAX_MODULE_READY_TIME, i); MAX_MODULE_READY_TIME, i);
return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
} }
/**
* omap4_cminst_module_enable - Enable the modulemode inside CLKCTRL
* @mode: Module mode (SW or HW)
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
* @inst: CM instance register offset (*_INST macro)
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
*
* No return value.
*/
void omap4_cminst_module_enable(u8 mode, u8 part, u16 inst, s16 cdoffs,
u16 clkctrl_offs)
{
u32 v;
v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs);
v &= ~OMAP4430_MODULEMODE_MASK;
v |= mode << OMAP4430_MODULEMODE_SHIFT;
omap4_cminst_write_inst_reg(v, part, inst, clkctrl_offs);
}
/**
* omap4_cminst_module_disable - Disable the module inside CLKCTRL
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
* @inst: CM instance register offset (*_INST macro)
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
*
* No return value.
*/
void omap4_cminst_module_disable(u8 part, u16 inst, s16 cdoffs,
u16 clkctrl_offs)
{
u32 v;
v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs);
v &= ~OMAP4430_MODULEMODE_MASK;
omap4_cminst_write_inst_reg(v, part, inst, clkctrl_offs);
}
...@@ -17,6 +17,14 @@ extern void omap4_cminst_clkdm_disable_hwsup(u8 part, s16 inst, u16 cdoffs); ...@@ -17,6 +17,14 @@ extern void omap4_cminst_clkdm_disable_hwsup(u8 part, s16 inst, u16 cdoffs);
extern void omap4_cminst_clkdm_force_sleep(u8 part, s16 inst, u16 cdoffs); extern void omap4_cminst_clkdm_force_sleep(u8 part, s16 inst, u16 cdoffs);
extern void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs); extern void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs);
extern int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
extern int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
extern void omap4_cminst_module_enable(u8 mode, u8 part, u16 inst, s16 cdoffs,
u16 clkctrl_offs);
extern void omap4_cminst_module_disable(u8 part, u16 inst, s16 cdoffs,
u16 clkctrl_offs);
/* /*
* In an ideal world, we would not export these low-level functions, * In an ideal world, we would not export these low-level functions,
* but this will probably take some time to fix properly * but this will probably take some time to fix properly
...@@ -32,6 +40,4 @@ extern u32 omap4_cminst_clear_inst_reg_bits(u32 bits, u8 part, s16 inst, ...@@ -32,6 +40,4 @@ extern u32 omap4_cminst_clear_inst_reg_bits(u32 bits, u8 part, s16 inst,
extern u32 omap4_cminst_read_inst_reg_bits(u8 part, u16 inst, s16 idx, extern u32 omap4_cminst_read_inst_reg_bits(u8 part, u16 inst, s16 idx,
u32 mask); u32 mask);
extern int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg);
#endif #endif
...@@ -146,9 +146,10 @@ ...@@ -146,9 +146,10 @@
#include <plat/prcm.h> #include <plat/prcm.h>
#include "cm2xxx_3xxx.h" #include "cm2xxx_3xxx.h"
#include "cm44xx.h" #include "cminst44xx.h"
#include "prm2xxx_3xxx.h" #include "prm2xxx_3xxx.h"
#include "prm44xx.h" #include "prm44xx.h"
#include "prminst44xx.h"
#include "mux.h" #include "mux.h"
/* Maximum microseconds to wait for OMAP module to softreset */ /* Maximum microseconds to wait for OMAP module to softreset */
...@@ -678,6 +679,56 @@ static void _disable_optional_clocks(struct omap_hwmod *oh) ...@@ -678,6 +679,56 @@ static void _disable_optional_clocks(struct omap_hwmod *oh)
} }
} }
/**
* _enable_module - enable CLKCTRL modulemode on OMAP4
* @oh: struct omap_hwmod *
*
* Enables the PRCM module mode related to the hwmod @oh.
* No return value.
*/
static void _enable_module(struct omap_hwmod *oh)
{
/* The module mode does not exist prior OMAP4 */
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return;
if (!oh->clkdm || !oh->prcm.omap4.modulemode)
return;
pr_debug("omap_hwmod: %s: _enable_module: %d\n",
oh->name, oh->prcm.omap4.modulemode);
omap4_cminst_module_enable(oh->prcm.omap4.modulemode,
oh->clkdm->prcm_partition,
oh->clkdm->cm_inst,
oh->clkdm->clkdm_offs,
oh->prcm.omap4.clkctrl_offs);
}
/**
* _disable_module - enable CLKCTRL modulemode on OMAP4
* @oh: struct omap_hwmod *
*
* Disable the PRCM module mode related to the hwmod @oh.
* No return value.
*/
static void _disable_module(struct omap_hwmod *oh)
{
/* The module mode does not exist prior OMAP4 */
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return;
if (!oh->clkdm || !oh->prcm.omap4.modulemode)
return;
pr_debug("omap_hwmod: %s: _disable_module\n", oh->name);
omap4_cminst_module_disable(oh->clkdm->prcm_partition,
oh->clkdm->cm_inst,
oh->clkdm->clkdm_offs,
oh->prcm.omap4.clkctrl_offs);
}
/** /**
* _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh * _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh
* @oh: struct omap_hwmod *oh * @oh: struct omap_hwmod *oh
...@@ -990,9 +1041,40 @@ static struct omap_hwmod *_lookup(const char *name) ...@@ -990,9 +1041,40 @@ static struct omap_hwmod *_lookup(const char *name)
return oh; return oh;
} }
/**
* _init_clkdm - look up a clockdomain name, store pointer in omap_hwmod
* @oh: struct omap_hwmod *
*
* Convert a clockdomain name stored in a struct omap_hwmod into a
* clockdomain pointer, and save it into the struct omap_hwmod.
* return -EINVAL if clkdm_name does not exist or if the lookup failed.
*/
static int _init_clkdm(struct omap_hwmod *oh)
{
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return 0;
if (!oh->clkdm_name) {
pr_warning("omap_hwmod: %s: no clkdm_name\n", oh->name);
return -EINVAL;
}
oh->clkdm = clkdm_lookup(oh->clkdm_name);
if (!oh->clkdm) {
pr_warning("omap_hwmod: %s: could not associate to clkdm %s\n",
oh->name, oh->clkdm_name);
return -EINVAL;
}
pr_debug("omap_hwmod: %s: associated to clkdm %s\n",
oh->name, oh->clkdm_name);
return 0;
}
/** /**
* _init_clocks - clk_get() all clocks associated with this hwmod * _init_clocks - clk_get() all clocks associated with this hwmod. Retrieve as
* well the clockdomain.
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* @data: not used; pass NULL * @data: not used; pass NULL
* *
...@@ -1012,6 +1094,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data) ...@@ -1012,6 +1094,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data)
ret |= _init_main_clk(oh); ret |= _init_main_clk(oh);
ret |= _init_interface_clks(oh); ret |= _init_interface_clks(oh);
ret |= _init_opt_clks(oh); ret |= _init_opt_clks(oh);
ret |= _init_clkdm(oh);
if (!ret) if (!ret)
oh->_state = _HWMOD_STATE_CLKS_INITED; oh->_state = _HWMOD_STATE_CLKS_INITED;
...@@ -1028,7 +1111,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data) ...@@ -1028,7 +1111,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data)
* Wait for a module @oh to leave slave idle. Returns 0 if the module * Wait for a module @oh to leave slave idle. Returns 0 if the module
* does not have an IDLEST bit or if the module successfully leaves * does not have an IDLEST bit or if the module successfully leaves
* slave idle; otherwise, pass along the return value of the * slave idle; otherwise, pass along the return value of the
* appropriate *_cm_wait_module_ready() function. * appropriate *_cm*_wait_module_ready() function.
*/ */
static int _wait_target_ready(struct omap_hwmod *oh) static int _wait_target_ready(struct omap_hwmod *oh)
{ {
...@@ -1055,7 +1138,13 @@ static int _wait_target_ready(struct omap_hwmod *oh) ...@@ -1055,7 +1138,13 @@ static int _wait_target_ready(struct omap_hwmod *oh)
oh->prcm.omap2.idlest_reg_id, oh->prcm.omap2.idlest_reg_id,
oh->prcm.omap2.idlest_idle_bit); oh->prcm.omap2.idlest_idle_bit);
} else if (cpu_is_omap44xx()) { } else if (cpu_is_omap44xx()) {
ret = omap4_cm_wait_module_ready(oh->prcm.omap4.clkctrl_reg); if (!oh->clkdm)
return -EINVAL;
ret = omap4_cminst_wait_module_ready(oh->clkdm->prcm_partition,
oh->clkdm->cm_inst,
oh->clkdm->clkdm_offs,
oh->prcm.omap4.clkctrl_offs);
} else { } else {
BUG(); BUG();
}; };
...@@ -1063,6 +1152,36 @@ static int _wait_target_ready(struct omap_hwmod *oh) ...@@ -1063,6 +1152,36 @@ static int _wait_target_ready(struct omap_hwmod *oh)
return ret; return ret;
} }
/**
* _wait_target_disable - wait for a module to be disabled
* @oh: struct omap_hwmod *
*
* Wait for a module @oh to enter slave idle. Returns 0 if the module
* does not have an IDLEST bit or if the module successfully enters
* slave idle; otherwise, pass along the return value of the
* appropriate *_cm*_wait_module_idle() function.
*/
static int _wait_target_disable(struct omap_hwmod *oh)
{
/* TODO: For now just handle OMAP4+ */
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return 0;
if (!oh)
return -EINVAL;
if (oh->_int_flags & _HWMOD_NO_MPU_PORT)
return 0;
if (oh->flags & HWMOD_NO_IDLEST)
return 0;
return omap4_cminst_wait_module_idle(oh->clkdm->prcm_partition,
oh->clkdm->cm_inst,
oh->clkdm->clkdm_offs,
oh->prcm.omap4.clkctrl_offs);
}
/** /**
* _lookup_hardreset - fill register bit info for this hwmod/reset line * _lookup_hardreset - fill register bit info for this hwmod/reset line
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
...@@ -1119,8 +1238,10 @@ static int _assert_hardreset(struct omap_hwmod *oh, const char *name) ...@@ -1119,8 +1238,10 @@ static int _assert_hardreset(struct omap_hwmod *oh, const char *name)
return omap2_prm_assert_hardreset(oh->prcm.omap2.module_offs, return omap2_prm_assert_hardreset(oh->prcm.omap2.module_offs,
ohri.rst_shift); ohri.rst_shift);
else if (cpu_is_omap44xx()) else if (cpu_is_omap44xx())
return omap4_prm_assert_hardreset(oh->prcm.omap4.rstctrl_reg, return omap4_prminst_assert_hardreset(ohri.rst_shift,
ohri.rst_shift); oh->clkdm->pwrdm.ptr->prcm_partition,
oh->clkdm->pwrdm.ptr->prcm_offs,
oh->prcm.omap4.rstctrl_offs);
else else
return -EINVAL; return -EINVAL;
} }
...@@ -1155,8 +1276,10 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name) ...@@ -1155,8 +1276,10 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
if (ohri.st_shift) if (ohri.st_shift)
pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n", pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n",
oh->name, name); oh->name, name);
ret = omap4_prm_deassert_hardreset(oh->prcm.omap4.rstctrl_reg, ret = omap4_prminst_deassert_hardreset(ohri.rst_shift,
ohri.rst_shift); oh->clkdm->pwrdm.ptr->prcm_partition,
oh->clkdm->pwrdm.ptr->prcm_offs,
oh->prcm.omap4.rstctrl_offs);
} else { } else {
return -EINVAL; return -EINVAL;
} }
...@@ -1191,8 +1314,10 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name) ...@@ -1191,8 +1314,10 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name)
return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs, return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs,
ohri.st_shift); ohri.st_shift);
} else if (cpu_is_omap44xx()) { } else if (cpu_is_omap44xx()) {
return omap4_prm_is_hardreset_asserted(oh->prcm.omap4.rstctrl_reg, return omap4_prminst_is_hardreset_asserted(ohri.rst_shift,
ohri.rst_shift); oh->clkdm->pwrdm.ptr->prcm_partition,
oh->clkdm->pwrdm.ptr->prcm_offs,
oh->prcm.omap4.rstctrl_offs);
} else { } else {
return -EINVAL; return -EINVAL;
} }
...@@ -1312,6 +1437,7 @@ static int _reset(struct omap_hwmod *oh) ...@@ -1312,6 +1437,7 @@ static int _reset(struct omap_hwmod *oh)
static int _enable(struct omap_hwmod *oh) static int _enable(struct omap_hwmod *oh)
{ {
int r; int r;
int hwsup = 0;
pr_debug("omap_hwmod: %s: enabling\n", oh->name); pr_debug("omap_hwmod: %s: enabling\n", oh->name);
...@@ -1323,14 +1449,6 @@ static int _enable(struct omap_hwmod *oh) ...@@ -1323,14 +1449,6 @@ static int _enable(struct omap_hwmod *oh)
return -EINVAL; return -EINVAL;
} }
/* Mux pins for device runtime if populated */
if (oh->mux && (!oh->mux->enabled ||
((oh->_state == _HWMOD_STATE_IDLE) &&
oh->mux->pads_dynamic)))
omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
_add_initiator_dep(oh, mpu_oh);
_enable_clocks(oh);
/* /*
* If an IP contains only one HW reset line, then de-assert it in order * If an IP contains only one HW reset line, then de-assert it in order
...@@ -1341,14 +1459,40 @@ static int _enable(struct omap_hwmod *oh) ...@@ -1341,14 +1459,40 @@ static int _enable(struct omap_hwmod *oh)
oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1) oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1)
_deassert_hardreset(oh, oh->rst_lines[0].name); _deassert_hardreset(oh, oh->rst_lines[0].name);
r = _wait_target_ready(oh); /* Mux pins for device runtime if populated */
if (r) { if (oh->mux && (!oh->mux->enabled ||
pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n", ((oh->_state == _HWMOD_STATE_IDLE) &&
oh->name, r); oh->mux->pads_dynamic)))
_disable_clocks(oh); omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
_add_initiator_dep(oh, mpu_oh);
if (oh->clkdm) {
/*
* A clockdomain must be in SW_SUP before enabling
* completely the module. The clockdomain can be set
* in HW_AUTO only when the module become ready.
*/
hwsup = clkdm_in_hwsup(oh->clkdm);
r = clkdm_hwmod_enable(oh->clkdm, oh);
if (r) {
WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
oh->name, oh->clkdm->name, r);
return r; return r;
} }
}
_enable_clocks(oh);
_enable_module(oh);
r = _wait_target_ready(oh);
if (!r) {
/*
* Set the clockdomain to HW_AUTO only if the target is ready,
* assuming that the previous state was HW_AUTO
*/
if (oh->clkdm && hwsup)
clkdm_allow_idle(oh->clkdm);
oh->_state = _HWMOD_STATE_ENABLED; oh->_state = _HWMOD_STATE_ENABLED;
...@@ -1358,6 +1502,14 @@ static int _enable(struct omap_hwmod *oh) ...@@ -1358,6 +1502,14 @@ static int _enable(struct omap_hwmod *oh)
_update_sysc_cache(oh); _update_sysc_cache(oh);
_enable_sysc(oh); _enable_sysc(oh);
} }
} else {
_disable_clocks(oh);
pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
oh->name, r);
if (oh->clkdm)
clkdm_hwmod_disable(oh->clkdm, oh);
}
return r; return r;
} }
...@@ -1372,6 +1524,8 @@ static int _enable(struct omap_hwmod *oh) ...@@ -1372,6 +1524,8 @@ static int _enable(struct omap_hwmod *oh)
*/ */
static int _idle(struct omap_hwmod *oh) static int _idle(struct omap_hwmod *oh)
{ {
int ret;
pr_debug("omap_hwmod: %s: idling\n", oh->name); pr_debug("omap_hwmod: %s: idling\n", oh->name);
if (oh->_state != _HWMOD_STATE_ENABLED) { if (oh->_state != _HWMOD_STATE_ENABLED) {
...@@ -1383,7 +1537,20 @@ static int _idle(struct omap_hwmod *oh) ...@@ -1383,7 +1537,20 @@ static int _idle(struct omap_hwmod *oh)
if (oh->class->sysc) if (oh->class->sysc)
_idle_sysc(oh); _idle_sysc(oh);
_del_initiator_dep(oh, mpu_oh); _del_initiator_dep(oh, mpu_oh);
_disable_module(oh);
ret = _wait_target_disable(oh);
if (ret)
pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
oh->name);
/*
* The module must be in idle mode before disabling any parents
* clocks. Otherwise, the parent clock might be disabled before
* the module transition is done, and thus will prevent the
* transition to complete properly.
*/
_disable_clocks(oh); _disable_clocks(oh);
if (oh->clkdm)
clkdm_hwmod_disable(oh->clkdm, oh);
/* Mux pins for device idle if populated */ /* Mux pins for device idle if populated */
if (oh->mux && oh->mux->pads_dynamic) if (oh->mux && oh->mux->pads_dynamic)
...@@ -1475,7 +1642,14 @@ static int _shutdown(struct omap_hwmod *oh) ...@@ -1475,7 +1642,14 @@ static int _shutdown(struct omap_hwmod *oh)
if (oh->_state == _HWMOD_STATE_ENABLED) { if (oh->_state == _HWMOD_STATE_ENABLED) {
_del_initiator_dep(oh, mpu_oh); _del_initiator_dep(oh, mpu_oh);
/* XXX what about the other system initiators here? dma, dsp */ /* XXX what about the other system initiators here? dma, dsp */
_disable_module(oh);
ret = _wait_target_disable(oh);
if (ret)
pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
oh->name);
_disable_clocks(oh); _disable_clocks(oh);
if (oh->clkdm)
clkdm_hwmod_disable(oh->clkdm, oh);
} }
/* XXX Should this code also force-disable the optional clocks? */ /* XXX Should this code also force-disable the optional clocks? */
......
...@@ -108,6 +108,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state) ...@@ -108,6 +108,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
u32 cur_state; u32 cur_state;
int sleep_switch = -1; int sleep_switch = -1;
int ret = 0; int ret = 0;
int hwsup = 0;
if (pwrdm == NULL || IS_ERR(pwrdm)) if (pwrdm == NULL || IS_ERR(pwrdm))
return -EINVAL; return -EINVAL;
...@@ -127,6 +128,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state) ...@@ -127,6 +128,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) { (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) {
sleep_switch = LOWPOWERSTATE_SWITCH; sleep_switch = LOWPOWERSTATE_SWITCH;
} else { } else {
hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
clkdm_wakeup(pwrdm->pwrdm_clkdms[0]); clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);
pwrdm_wait_transition(pwrdm); pwrdm_wait_transition(pwrdm);
sleep_switch = FORCEWAKEUP_SWITCH; sleep_switch = FORCEWAKEUP_SWITCH;
...@@ -142,7 +144,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state) ...@@ -142,7 +144,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
switch (sleep_switch) { switch (sleep_switch) {
case FORCEWAKEUP_SWITCH: case FORCEWAKEUP_SWITCH:
if (pwrdm->pwrdm_clkdms[0]->flags & CLKDM_CAN_ENABLE_AUTO) if (hwsup)
clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]); clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);
else else
clkdm_sleep(pwrdm->pwrdm_clkdms[0]); clkdm_sleep(pwrdm->pwrdm_clkdms[0]);
......
...@@ -70,7 +70,7 @@ static void omap_prcm_arch_reset(char mode, const char *cmd) ...@@ -70,7 +70,7 @@ static void omap_prcm_arch_reset(char mode, const char *cmd)
prcm_offs = OMAP3430_GR_MOD; prcm_offs = OMAP3430_GR_MOD;
omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0)); omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0));
} else if (cpu_is_omap44xx()) { } else if (cpu_is_omap44xx()) {
omap4_prm_global_warm_sw_reset(); /* never returns */ omap4_prminst_global_warm_sw_reset(); /* never returns */
} else { } else {
WARN_ON(1); WARN_ON(1);
} }
......
/* /*
* OMAP4 PRM module functions * OMAP4 PRM module functions
* *
* Copyright (C) 2010 Texas Instruments, Inc. * Copyright (C) 2011 Texas Instruments, Inc.
* Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Nokia Corporation
* Benoît Cousson * Benoît Cousson
* Paul Walmsley * Paul Walmsley
...@@ -24,12 +24,6 @@ ...@@ -24,12 +24,6 @@
#include "prm44xx.h" #include "prm44xx.h"
#include "prm-regbits-44xx.h" #include "prm-regbits-44xx.h"
/*
* Address offset (in bytes) between the reset control and the reset
* status registers: 4 bytes on OMAP4
*/
#define OMAP4_RST_CTRL_ST_OFFSET 4
/* PRM low-level functions */ /* PRM low-level functions */
/* Read a register in a CM/PRM instance in the PRM module */ /* Read a register in a CM/PRM instance in the PRM module */
...@@ -56,140 +50,3 @@ u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 reg) ...@@ -56,140 +50,3 @@ u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 reg)
return v; return v;
} }
/* Read a PRM register, AND it, and shift the result down to bit 0 */
/* XXX deprecated */
u32 omap4_prm_read_bits_shift(void __iomem *reg, u32 mask)
{
u32 v;
v = __raw_readl(reg);
v &= mask;
v >>= __ffs(mask);
return v;
}
/* Read-modify-write a register in a PRM module. Caller must lock */
/* XXX deprecated */
u32 omap4_prm_rmw_reg_bits(u32 mask, u32 bits, void __iomem *reg)
{
u32 v;
v = __raw_readl(reg);
v &= ~mask;
v |= bits;
__raw_writel(v, reg);
return v;
}
u32 omap4_prm_set_inst_reg_bits(u32 bits, s16 inst, s16 reg)
{
return omap4_prm_rmw_inst_reg_bits(bits, bits, inst, reg);
}
u32 omap4_prm_clear_inst_reg_bits(u32 bits, s16 inst, s16 reg)
{
return omap4_prm_rmw_inst_reg_bits(bits, 0x0, inst, reg);
}
/**
* omap4_prm_is_hardreset_asserted - read the HW reset line state of
* submodules contained in the hwmod module
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to check
*
* Returns 1 if the (sub)module hardreset line is currently asserted,
* 0 if the (sub)module hardreset line is not currently asserted, or
* -EINVAL upon parameter error.
*/
int omap4_prm_is_hardreset_asserted(void __iomem *rstctrl_reg, u8 shift)
{
if (!cpu_is_omap44xx() || !rstctrl_reg)
return -EINVAL;
return omap4_prm_read_bits_shift(rstctrl_reg, (1 << shift));
}
/**
* omap4_prm_assert_hardreset - assert the HW reset line of a submodule
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to assert
*
* Some IPs like dsp, ipu or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* place the submodule into reset. Returns 0 upon success or -EINVAL
* upon an argument error.
*/
int omap4_prm_assert_hardreset(void __iomem *rstctrl_reg, u8 shift)
{
u32 mask;
if (!cpu_is_omap44xx() || !rstctrl_reg)
return -EINVAL;
mask = 1 << shift;
omap4_prm_rmw_reg_bits(mask, mask, rstctrl_reg);
return 0;
}
/**
* omap4_prm_deassert_hardreset - deassert a submodule hardreset line and wait
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to deassert
*
* Some IPs like dsp, ipu or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* take the submodule out of reset and wait until the PRCM indicates
* that the reset has completed before returning. Returns 0 upon success or
* -EINVAL upon an argument error, -EEXIST if the submodule was already out
* of reset, or -EBUSY if the submodule did not exit reset promptly.
*/
int omap4_prm_deassert_hardreset(void __iomem *rstctrl_reg, u8 shift)
{
u32 mask;
void __iomem *rstst_reg;
int c;
if (!cpu_is_omap44xx() || !rstctrl_reg)
return -EINVAL;
rstst_reg = rstctrl_reg + OMAP4_RST_CTRL_ST_OFFSET;
mask = 1 << shift;
/* Check the current status to avoid de-asserting the line twice */
if (omap4_prm_read_bits_shift(rstctrl_reg, mask) == 0)
return -EEXIST;
/* Clear the reset status by writing 1 to the status bit */
omap4_prm_rmw_reg_bits(0xffffffff, mask, rstst_reg);
/* de-assert the reset control line */
omap4_prm_rmw_reg_bits(mask, 0, rstctrl_reg);
/* wait the status to be set */
omap_test_timeout(omap4_prm_read_bits_shift(rstst_reg, mask),
MAX_MODULE_HARDRESET_WAIT, c);
return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
}
void omap4_prm_global_warm_sw_reset(void)
{
u32 v;
v = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
OMAP4_RM_RSTCTRL);
v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
omap4_prm_write_inst_reg(v, OMAP4430_PRM_DEVICE_INST,
OMAP4_RM_RSTCTRL);
/* OCP barrier */
v = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
OMAP4_RM_RSTCTRL);
}
...@@ -750,16 +750,6 @@ ...@@ -750,16 +750,6 @@
extern u32 omap4_prm_read_inst_reg(s16 inst, u16 idx); 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 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); extern u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
extern u32 omap4_prm_rmw_reg_bits(u32 mask, u32 bits, void __iomem *reg);
extern u32 omap4_prm_set_inst_reg_bits(u32 bits, s16 inst, s16 idx);
extern u32 omap4_prm_clear_inst_reg_bits(u32 bits, s16 inst, s16 idx);
extern u32 omap4_prm_read_bits_shift(void __iomem *reg, u32 mask);
extern int omap4_prm_is_hardreset_asserted(void __iomem *rstctrl_reg, u8 shift);
extern int omap4_prm_assert_hardreset(void __iomem *rstctrl_reg, u8 shift);
extern int omap4_prm_deassert_hardreset(void __iomem *rstctrl_reg, u8 shift);
extern void omap4_prm_global_warm_sw_reset(void);
# endif # endif
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* OMAP4 PRM instance functions * OMAP4 PRM instance functions
* *
* Copyright (C) 2009 Nokia Corporation * Copyright (C) 2009 Nokia Corporation
* Copyright (C) 2011 Texas Instruments, Inc.
* Paul Walmsley * Paul Walmsley
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -53,7 +54,7 @@ void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx) ...@@ -53,7 +54,7 @@ void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx)
/* Read-modify-write a register in PRM. Caller must lock */ /* Read-modify-write a register in PRM. Caller must lock */
u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst, u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst,
s16 idx) u16 idx)
{ {
u32 v; u32 v;
...@@ -64,3 +65,112 @@ u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst, ...@@ -64,3 +65,112 @@ u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst,
return v; return v;
} }
/*
* Address offset (in bytes) between the reset control and the reset
* status registers: 4 bytes on OMAP4
*/
#define OMAP4_RST_CTRL_ST_OFFSET 4
/**
* omap4_prminst_is_hardreset_asserted - read the HW reset line state of
* submodules contained in the hwmod module
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to check
*
* Returns 1 if the (sub)module hardreset line is currently asserted,
* 0 if the (sub)module hardreset line is not currently asserted, or
* -EINVAL upon parameter error.
*/
int omap4_prminst_is_hardreset_asserted(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs)
{
u32 v;
v = omap4_prminst_read_inst_reg(part, inst, rstctrl_offs);
v &= 1 << shift;
v >>= shift;
return v;
}
/**
* omap4_prminst_assert_hardreset - assert the HW reset line of a submodule
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to assert
*
* Some IPs like dsp, ipu or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* place the submodule into reset. Returns 0 upon success or -EINVAL
* upon an argument error.
*/
int omap4_prminst_assert_hardreset(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs)
{
u32 mask = 1 << shift;
omap4_prminst_rmw_inst_reg_bits(mask, mask, part, inst, rstctrl_offs);
return 0;
}
/**
* omap4_prminst_deassert_hardreset - deassert a submodule hardreset line and
* wait
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to deassert
*
* Some IPs like dsp, ipu or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* take the submodule out of reset and wait until the PRCM indicates
* that the reset has completed before returning. Returns 0 upon success or
* -EINVAL upon an argument error, -EEXIST if the submodule was already out
* of reset, or -EBUSY if the submodule did not exit reset promptly.
*/
int omap4_prminst_deassert_hardreset(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs)
{
int c;
u32 mask = 1 << shift;
u16 rstst_offs = rstctrl_offs + OMAP4_RST_CTRL_ST_OFFSET;
/* Check the current status to avoid de-asserting the line twice */
if (omap4_prminst_is_hardreset_asserted(shift, part, inst,
rstctrl_offs) == 0)
return -EEXIST;
/* Clear the reset status by writing 1 to the status bit */
omap4_prminst_rmw_inst_reg_bits(0xffffffff, mask, part, inst,
rstst_offs);
/* de-assert the reset control line */
omap4_prminst_rmw_inst_reg_bits(mask, 0, part, inst, rstctrl_offs);
/* wait the status to be set */
omap_test_timeout(omap4_prminst_is_hardreset_asserted(shift, part, inst,
rstst_offs),
MAX_MODULE_HARDRESET_WAIT, c);
return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
}
void omap4_prminst_global_warm_sw_reset(void)
{
u32 v;
v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_RSTCTRL_OFFSET);
v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
omap4_prminst_write_inst_reg(v, OMAP4430_PRM_PARTITION,
OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_RSTCTRL_OFFSET);
/* OCP barrier */
v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_RSTCTRL_OFFSET);
}
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* OMAP4 Power/Reset Management (PRM) function prototypes * OMAP4 Power/Reset Management (PRM) function prototypes
* *
* Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Nokia Corporation
* Copyright (C) 2011 Texas Instruments, Inc.
* Paul Walmsley * Paul Walmsley
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -18,8 +19,15 @@ ...@@ -18,8 +19,15 @@
extern u32 omap4_prminst_read_inst_reg(u8 part, s16 inst, u16 idx); extern u32 omap4_prminst_read_inst_reg(u8 part, s16 inst, u16 idx);
extern void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx); extern void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx);
extern u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, extern u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part,
s16 inst, s16 idx); s16 inst, u16 idx);
extern void omap4_prm_global_warm_sw_reset(void); extern void omap4_prminst_global_warm_sw_reset(void);
extern int omap4_prminst_is_hardreset_asserted(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs);
extern int omap4_prminst_assert_hardreset(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs);
extern int omap4_prminst_deassert_hardreset(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs);
#endif #endif
...@@ -80,11 +80,11 @@ static struct twl4030_madc_platform_data omap3_madc_pdata = { ...@@ -80,11 +80,11 @@ static struct twl4030_madc_platform_data omap3_madc_pdata = {
.irq_line = 1, .irq_line = 1,
}; };
static struct twl4030_codec_audio_data omap3_audio; static struct twl4030_codec_data omap3_codec;
static struct twl4030_codec_data omap3_codec_pdata = { static struct twl4030_audio_data omap3_audio_pdata = {
.audio_mclk = 26000000, .audio_mclk = 26000000,
.audio = &omap3_audio, .codec = &omap3_codec,
}; };
static struct regulator_consumer_supply omap3_vdda_dac_supplies[] = { static struct regulator_consumer_supply omap3_vdda_dac_supplies[] = {
...@@ -292,8 +292,8 @@ void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data, ...@@ -292,8 +292,8 @@ void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data,
if (pdata_flags & TWL_COMMON_PDATA_MADC && !pmic_data->madc) if (pdata_flags & TWL_COMMON_PDATA_MADC && !pmic_data->madc)
pmic_data->madc = &omap3_madc_pdata; pmic_data->madc = &omap3_madc_pdata;
if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->codec) if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->audio)
pmic_data->codec = &omap3_codec_pdata; pmic_data->audio = &omap3_audio_pdata;
/* Common regulator configurations */ /* Common regulator configurations */
if (regulators_flags & TWL_COMMON_REGULATOR_VDAC && !pmic_data->vdac) if (regulators_flags & TWL_COMMON_REGULATOR_VDAC && !pmic_data->vdac)
......
...@@ -475,8 +475,41 @@ int __init clk_init(struct clk_functions * custom_clocks) ...@@ -475,8 +475,41 @@ int __init clk_init(struct clk_functions * custom_clocks)
/* /*
* debugfs support to trace clock tree hierarchy and attributes * debugfs support to trace clock tree hierarchy and attributes
*/ */
#include <linux/debugfs.h>
#include <linux/seq_file.h>
static struct dentry *clk_debugfs_root; static struct dentry *clk_debugfs_root;
static int clk_dbg_show_summary(struct seq_file *s, void *unused)
{
struct clk *c;
struct clk *pa;
seq_printf(s, "%-30s %-30s %-10s %s\n",
"clock-name", "parent-name", "rate", "use-count");
list_for_each_entry(c, &clocks, node) {
pa = c->parent;
seq_printf(s, "%-30s %-30s %-10lu %d\n",
c->name, pa ? pa->name : "none", c->rate, c->usecount);
}
return 0;
}
static int clk_dbg_open(struct inode *inode, struct file *file)
{
return single_open(file, clk_dbg_show_summary, inode->i_private);
}
static const struct file_operations debug_clock_fops = {
.open = clk_dbg_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int clk_debugfs_register_one(struct clk *c) static int clk_debugfs_register_one(struct clk *c)
{ {
int err; int err;
...@@ -545,6 +578,12 @@ static int __init clk_debugfs_init(void) ...@@ -545,6 +578,12 @@ static int __init clk_debugfs_init(void)
if (err) if (err)
goto err_out; goto err_out;
} }
d = debugfs_create_file("summary", S_IRUGO,
d, NULL, &debug_clock_fops);
if (!d)
return -ENOMEM;
return 0; return 0;
err_out: err_out:
debugfs_remove_recursive(clk_debugfs_root); debugfs_remove_recursive(clk_debugfs_root);
......
...@@ -407,11 +407,19 @@ ...@@ -407,11 +407,19 @@
#endif #endif
#define TWL6030_IRQ_END (TWL6030_IRQ_BASE + TWL6030_BASE_NR_IRQS) #define TWL6030_IRQ_END (TWL6030_IRQ_BASE + TWL6030_BASE_NR_IRQS)
#define TWL6040_CODEC_IRQ_BASE TWL6030_IRQ_END
#ifdef CONFIG_TWL6040_CODEC
#define TWL6040_CODEC_NR_IRQS 6
#else
#define TWL6040_CODEC_NR_IRQS 0
#endif
#define TWL6040_CODEC_IRQ_END (TWL6040_CODEC_IRQ_BASE + TWL6040_CODEC_NR_IRQS)
/* Total number of interrupts depends on the enabled blocks above */ /* Total number of interrupts depends on the enabled blocks above */
#if (TWL4030_GPIO_IRQ_END > TWL6030_IRQ_END) #if (TWL4030_GPIO_IRQ_END > TWL6040_CODEC_IRQ_END)
#define TWL_IRQ_END TWL4030_GPIO_IRQ_END #define TWL_IRQ_END TWL4030_GPIO_IRQ_END
#else #else
#define TWL_IRQ_END TWL6030_IRQ_END #define TWL_IRQ_END TWL6040_CODEC_IRQ_END
#endif #endif
/* GPMC related */ /* GPMC related */
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* omap_hwmod macros, structures * omap_hwmod macros, structures
* *
* Copyright (C) 2009-2011 Nokia Corporation * Copyright (C) 2009-2011 Nokia Corporation
* Copyright (C) 2011 Texas Instruments, Inc.
* Paul Walmsley * Paul Walmsley
* *
* Created in collaboration with (alphabetical order): Benoît Cousson, * Created in collaboration with (alphabetical order): Benoît Cousson,
...@@ -79,6 +80,11 @@ extern struct omap_hwmod_sysc_fields omap_hwmod_sysc_type2; ...@@ -79,6 +80,11 @@ extern struct omap_hwmod_sysc_fields omap_hwmod_sysc_type2;
#define HWMOD_IDLEMODE_SMART (1 << 2) #define HWMOD_IDLEMODE_SMART (1 << 2)
#define HWMOD_IDLEMODE_SMART_WKUP (1 << 3) #define HWMOD_IDLEMODE_SMART_WKUP (1 << 3)
/* modulemode control type (SW or HW) */
#define MODULEMODE_HWCTRL 1
#define MODULEMODE_SWCTRL 2
/** /**
* struct omap_hwmod_mux_info - hwmod specific mux configuration * struct omap_hwmod_mux_info - hwmod specific mux configuration
* @pads: array of omap_device_pad entries * @pads: array of omap_device_pad entries
...@@ -360,9 +366,11 @@ struct omap_hwmod_omap2_prcm { ...@@ -360,9 +366,11 @@ struct omap_hwmod_omap2_prcm {
* @submodule_wkdep_bit: bit shift of the WKDEP range * @submodule_wkdep_bit: bit shift of the WKDEP range
*/ */
struct omap_hwmod_omap4_prcm { struct omap_hwmod_omap4_prcm {
void __iomem *clkctrl_reg; u16 clkctrl_offs;
void __iomem *rstctrl_reg; u16 rstctrl_offs;
u16 context_offs;
u8 submodule_wkdep_bit; u8 submodule_wkdep_bit;
u8 modulemode;
}; };
...@@ -515,6 +523,8 @@ struct omap_hwmod { ...@@ -515,6 +523,8 @@ struct omap_hwmod {
const char *main_clk; const char *main_clk;
struct clk *_clk; struct clk *_clk;
struct omap_hwmod_opt_clk *opt_clks; struct omap_hwmod_opt_clk *opt_clks;
char *clkdm_name;
struct clockdomain *clkdm;
char *vdd_name; char *vdd_name;
struct voltagedomain *voltdm; struct voltagedomain *voltdm;
struct omap_hwmod_ocp_if **masters; /* connect to *_IA */ struct omap_hwmod_ocp_if **masters; /* connect to *_IA */
......
...@@ -236,56 +236,71 @@ static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat) ...@@ -236,56 +236,71 @@ static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat)
return 0; return 0;
} }
static void _add_clkdev(struct omap_device *od, const char *clk_alias,
const char *clk_name)
{
struct clk *r;
struct clk_lookup *l;
if (!clk_alias || !clk_name)
return;
pr_debug("omap_device: %s: Creating %s -> %s\n",
dev_name(&od->pdev.dev), clk_alias, clk_name);
r = clk_get_sys(dev_name(&od->pdev.dev), clk_alias);
if (!IS_ERR(r)) {
pr_warning("omap_device: %s: alias %s already exists\n",
dev_name(&od->pdev.dev), clk_alias);
clk_put(r);
return;
}
r = omap_clk_get_by_name(clk_name);
if (IS_ERR(r)) {
pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
dev_name(&od->pdev.dev), clk_name);
return;
}
l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev.dev));
if (!l) {
pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
dev_name(&od->pdev.dev), clk_alias);
return;
}
clkdev_add(l);
}
/** /**
* _add_optional_clock_clkdev - Add clkdev entry for hwmod optional clocks * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks
* and main clock
* @od: struct omap_device *od * @od: struct omap_device *od
* @oh: struct omap_hwmod *oh
* *
* For every optional clock present per hwmod per omap_device, this function * For the main clock and every optional clock present per hwmod per
* adds an entry in the clkdev table of the form <dev-id=dev_name, con-id=role> * omap_device, this function adds an entry in the clkdev table of the
* if it does not exist already. * form <dev-id=dev_name, con-id=role> if it does not exist already.
* *
* The function is called from inside omap_device_build_ss(), after * The function is called from inside omap_device_build_ss(), after
* omap_device_register. * omap_device_register.
* *
* This allows drivers to get a pointer to its optional clocks based on its role * This allows drivers to get a pointer to its optional clocks based on its role
* by calling clk_get(<dev*>, <role>). * by calling clk_get(<dev*>, <role>).
* In the case of the main clock, a "fck" alias is used.
* *
* No return value. * No return value.
*/ */
static void _add_optional_clock_clkdev(struct omap_device *od, static void _add_hwmod_clocks_clkdev(struct omap_device *od,
struct omap_hwmod *oh) struct omap_hwmod *oh)
{ {
int i; int i;
for (i = 0; i < oh->opt_clks_cnt; i++) { _add_clkdev(od, "fck", oh->main_clk);
struct omap_hwmod_opt_clk *oc;
struct clk *r;
struct clk_lookup *l;
oc = &oh->opt_clks[i];
if (!oc->_clk)
continue;
r = clk_get_sys(dev_name(&od->pdev.dev), oc->role); for (i = 0; i < oh->opt_clks_cnt; i++)
if (!IS_ERR(r)) _add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk);
continue; /* clkdev entry exists */
r = omap_clk_get_by_name((char *)oc->clk);
if (IS_ERR(r)) {
pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
dev_name(&od->pdev.dev), oc->clk);
continue;
}
l = clkdev_alloc(r, oc->role, dev_name(&od->pdev.dev));
if (!l) {
pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
dev_name(&od->pdev.dev), oc->role);
return;
}
clkdev_add(l);
}
} }
...@@ -492,7 +507,7 @@ struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id, ...@@ -492,7 +507,7 @@ struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id,
for (i = 0; i < oh_cnt; i++) { for (i = 0; i < oh_cnt; i++) {
hwmods[i]->od = od; hwmods[i]->od = od;
_add_optional_clock_clkdev(od, hwmods[i]); _add_hwmod_clocks_clkdev(od, hwmods[i]);
} }
if (ret) if (ret)
......
...@@ -267,7 +267,7 @@ config INPUT_TWL4030_PWRBUTTON ...@@ -267,7 +267,7 @@ config INPUT_TWL4030_PWRBUTTON
config INPUT_TWL4030_VIBRA config INPUT_TWL4030_VIBRA
tristate "Support for TWL4030 Vibrator" tristate "Support for TWL4030 Vibrator"
depends on TWL4030_CORE depends on TWL4030_CORE
select TWL4030_CODEC select MFD_TWL4030_AUDIO
select INPUT_FF_MEMLESS select INPUT_FF_MEMLESS
help help
This option enables support for TWL4030 Vibrator Driver. This option enables support for TWL4030 Vibrator Driver.
...@@ -275,6 +275,17 @@ config INPUT_TWL4030_VIBRA ...@@ -275,6 +275,17 @@ config INPUT_TWL4030_VIBRA
To compile this driver as a module, choose M here. The module will To compile this driver as a module, choose M here. The module will
be called twl4030_vibra. be called twl4030_vibra.
config INPUT_TWL6040_VIBRA
tristate "Support for TWL6040 Vibrator"
depends on TWL4030_CORE
select TWL6040_CORE
select INPUT_FF_MEMLESS
help
This option enables support for TWL6040 Vibrator Driver.
To compile this driver as a module, choose M here. The module will
be called twl6040_vibra.
config INPUT_UINPUT config INPUT_UINPUT
tristate "User level driver support" tristate "User level driver support"
help help
......
...@@ -40,6 +40,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o ...@@ -40,6 +40,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/i2c/twl.h> #include <linux/i2c/twl.h>
#include <linux/mfd/twl4030-codec.h> #include <linux/mfd/twl4030-audio.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -67,7 +67,7 @@ static void vibra_enable(struct vibra_info *info) ...@@ -67,7 +67,7 @@ static void vibra_enable(struct vibra_info *info)
{ {
u8 reg; u8 reg;
twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
/* turn H-Bridge on */ /* turn H-Bridge on */
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
...@@ -75,7 +75,7 @@ static void vibra_enable(struct vibra_info *info) ...@@ -75,7 +75,7 @@ static void vibra_enable(struct vibra_info *info)
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
(reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL); twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL);
info->enabled = true; info->enabled = true;
} }
...@@ -90,8 +90,8 @@ static void vibra_disable(struct vibra_info *info) ...@@ -90,8 +90,8 @@ static void vibra_disable(struct vibra_info *info)
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
(reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL);
twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
info->enabled = false; info->enabled = false;
} }
...@@ -196,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, ...@@ -196,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
static int __devinit twl4030_vibra_probe(struct platform_device *pdev) static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
{ {
struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data; struct twl4030_vibra_data *pdata = pdev->dev.platform_data;
struct vibra_info *info; struct vibra_info *info;
int ret; int ret;
......
/*
* twl6040-vibra.c - TWL6040 Vibrator driver
*
* Author: Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
* Author: Misael Lopez Cruz <misael.lopez@ti.com>
*
* Copyright: (C) 2011 Texas Instruments, Inc.
*
* Based on twl4030-vibra.c by Henrik Saari <henrik.saari@nokia.com>
* Felipe Balbi <felipe.balbi@nokia.com>
* Jari Vanhala <ext-javi.vanhala@nokia.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/i2c/twl.h>
#include <linux/mfd/twl6040.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#define EFFECT_DIR_180_DEG 0x8000
/* Recommended modulation index 85% */
#define TWL6040_VIBRA_MOD 85
#define TWL6040_NUM_SUPPLIES 2
struct vibra_info {
struct device *dev;
struct input_dev *input_dev;
struct workqueue_struct *workqueue;
struct work_struct play_work;
struct mutex mutex;
int irq;
bool enabled;
int weak_speed;
int strong_speed;
int direction;
unsigned int vibldrv_res;
unsigned int vibrdrv_res;
unsigned int viblmotor_res;
unsigned int vibrmotor_res;
struct regulator_bulk_data supplies[TWL6040_NUM_SUPPLIES];
struct twl6040 *twl6040;
};
static irqreturn_t twl6040_vib_irq_handler(int irq, void *data)
{
struct vibra_info *info = data;
struct twl6040 *twl6040 = info->twl6040;
u8 status;
status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
if (status & TWL6040_VIBLOCDET) {
dev_warn(info->dev, "Left Vibrator overcurrent detected\n");
twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLL,
TWL6040_VIBENAL);
}
if (status & TWL6040_VIBROCDET) {
dev_warn(info->dev, "Right Vibrator overcurrent detected\n");
twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLR,
TWL6040_VIBENAR);
}
return IRQ_HANDLED;
}
static void twl6040_vibra_enable(struct vibra_info *info)
{
struct twl6040 *twl6040 = info->twl6040;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(info->supplies), info->supplies);
if (ret) {
dev_err(info->dev, "failed to enable regulators %d\n", ret);
return;
}
twl6040_power(info->twl6040, 1);
if (twl6040->rev <= TWL6040_REV_ES1_1) {
/*
* ERRATA: Disable overcurrent protection for at least
* 3ms when enabling vibrator drivers to avoid false
* overcurrent detection
*/
twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL,
TWL6040_VIBENAL | TWL6040_VIBCTRLL);
twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR,
TWL6040_VIBENAR | TWL6040_VIBCTRLR);
usleep_range(3000, 3500);
}
twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL,
TWL6040_VIBENAL);
twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR,
TWL6040_VIBENAR);
info->enabled = true;
}
static void twl6040_vibra_disable(struct vibra_info *info)
{
struct twl6040 *twl6040 = info->twl6040;
twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, 0x00);
twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, 0x00);
twl6040_power(info->twl6040, 0);
regulator_bulk_disable(ARRAY_SIZE(info->supplies), info->supplies);
info->enabled = false;
}
static u8 twl6040_vibra_code(int vddvib, int vibdrv_res, int motor_res,
int speed, int direction)
{
int vpk, max_code;
u8 vibdat;
/* output swing */
vpk = (vddvib * motor_res * TWL6040_VIBRA_MOD) /
(100 * (vibdrv_res + motor_res));
/* 50mV per VIBDAT code step */
max_code = vpk / 50;
if (max_code > TWL6040_VIBDAT_MAX)
max_code = TWL6040_VIBDAT_MAX;
/* scale speed to max allowed code */
vibdat = (u8)((speed * max_code) / USHRT_MAX);
/* 2's complement for direction > 180 degrees */
vibdat *= direction;
return vibdat;
}
static void twl6040_vibra_set_effect(struct vibra_info *info)
{
struct twl6040 *twl6040 = info->twl6040;
u8 vibdatl, vibdatr;
int volt;
/* weak motor */
volt = regulator_get_voltage(info->supplies[0].consumer) / 1000;
vibdatl = twl6040_vibra_code(volt, info->vibldrv_res,
info->viblmotor_res,
info->weak_speed, info->direction);
/* strong motor */
volt = regulator_get_voltage(info->supplies[1].consumer) / 1000;
vibdatr = twl6040_vibra_code(volt, info->vibrdrv_res,
info->vibrmotor_res,
info->strong_speed, info->direction);
twl6040_reg_write(twl6040, TWL6040_REG_VIBDATL, vibdatl);
twl6040_reg_write(twl6040, TWL6040_REG_VIBDATR, vibdatr);
}
static void vibra_play_work(struct work_struct *work)
{
struct vibra_info *info = container_of(work,
struct vibra_info, play_work);
mutex_lock(&info->mutex);
if (info->weak_speed || info->strong_speed) {
if (!info->enabled)
twl6040_vibra_enable(info);
twl6040_vibra_set_effect(info);
} else if (info->enabled)
twl6040_vibra_disable(info);
mutex_unlock(&info->mutex);
}
static int vibra_play(struct input_dev *input, void *data,
struct ff_effect *effect)
{
struct vibra_info *info = input_get_drvdata(input);
int ret;
info->weak_speed = effect->u.rumble.weak_magnitude;
info->strong_speed = effect->u.rumble.strong_magnitude;
info->direction = effect->direction < EFFECT_DIR_180_DEG ? 1 : -1;
ret = queue_work(info->workqueue, &info->play_work);
if (!ret) {
dev_info(&input->dev, "work is already on queue\n");
return ret;
}
return 0;
}
static void twl6040_vibra_close(struct input_dev *input)
{
struct vibra_info *info = input_get_drvdata(input);
cancel_work_sync(&info->play_work);
mutex_lock(&info->mutex);
if (info->enabled)
twl6040_vibra_disable(info);
mutex_unlock(&info->mutex);
}
#if CONFIG_PM_SLEEP
static int twl6040_vibra_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct vibra_info *info = platform_get_drvdata(pdev);
mutex_lock(&info->mutex);
if (info->enabled)
twl6040_vibra_disable(info);
mutex_unlock(&info->mutex);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL);
static int __devinit twl6040_vibra_probe(struct platform_device *pdev)
{
struct twl4030_vibra_data *pdata = pdev->dev.platform_data;
struct vibra_info *info;
int ret;
if (!pdata) {
dev_err(&pdev->dev, "platform_data not available\n");
return -EINVAL;
}
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "couldn't allocate memory\n");
return -ENOMEM;
}
info->dev = &pdev->dev;
info->twl6040 = dev_get_drvdata(pdev->dev.parent);
info->vibldrv_res = pdata->vibldrv_res;
info->vibrdrv_res = pdata->vibrdrv_res;
info->viblmotor_res = pdata->viblmotor_res;
info->vibrmotor_res = pdata->vibrmotor_res;
if ((!info->vibldrv_res && !info->viblmotor_res) ||
(!info->vibrdrv_res && !info->vibrmotor_res)) {
dev_err(info->dev, "invalid vibra driver/motor resistance\n");
ret = -EINVAL;
goto err_kzalloc;
}
info->irq = platform_get_irq(pdev, 0);
if (info->irq < 0) {
dev_err(info->dev, "invalid irq\n");
ret = -EINVAL;
goto err_kzalloc;
}
mutex_init(&info->mutex);
info->input_dev = input_allocate_device();
if (info->input_dev == NULL) {
dev_err(info->dev, "couldn't allocate input device\n");
ret = -ENOMEM;
goto err_kzalloc;
}
input_set_drvdata(info->input_dev, info);
info->input_dev->name = "twl6040:vibrator";
info->input_dev->id.version = 1;
info->input_dev->dev.parent = pdev->dev.parent;
info->input_dev->close = twl6040_vibra_close;
__set_bit(FF_RUMBLE, info->input_dev->ffbit);
ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
if (ret < 0) {
dev_err(info->dev, "couldn't register vibrator to FF\n");
goto err_ialloc;
}
ret = input_register_device(info->input_dev);
if (ret < 0) {
dev_err(info->dev, "couldn't register input device\n");
goto err_iff;
}
platform_set_drvdata(pdev, info);
ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0,
"twl6040_irq_vib", info);
if (ret) {
dev_err(info->dev, "VIB IRQ request failed: %d\n", ret);
goto err_irq;
}
info->supplies[0].supply = "vddvibl";
info->supplies[1].supply = "vddvibr";
ret = regulator_bulk_get(info->dev, ARRAY_SIZE(info->supplies),
info->supplies);
if (ret) {
dev_err(info->dev, "couldn't get regulators %d\n", ret);
goto err_regulator;
}
if (pdata->vddvibl_uV) {
ret = regulator_set_voltage(info->supplies[0].consumer,
pdata->vddvibl_uV,
pdata->vddvibl_uV);
if (ret) {
dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
ret);
goto err_voltage;
}
}
if (pdata->vddvibr_uV) {
ret = regulator_set_voltage(info->supplies[1].consumer,
pdata->vddvibr_uV,
pdata->vddvibr_uV);
if (ret) {
dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
ret);
goto err_voltage;
}
}
info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0);
if (info->workqueue == NULL) {
dev_err(info->dev, "couldn't create workqueue\n");
ret = -ENOMEM;
goto err_voltage;
}
INIT_WORK(&info->play_work, vibra_play_work);
return 0;
err_voltage:
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
err_regulator:
free_irq(info->irq, info);
err_irq:
input_unregister_device(info->input_dev);
info->input_dev = NULL;
err_iff:
if (info->input_dev)
input_ff_destroy(info->input_dev);
err_ialloc:
input_free_device(info->input_dev);
err_kzalloc:
kfree(info);
return ret;
}
static int __devexit twl6040_vibra_remove(struct platform_device *pdev)
{
struct vibra_info *info = platform_get_drvdata(pdev);
input_unregister_device(info->input_dev);
free_irq(info->irq, info);
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
destroy_workqueue(info->workqueue);
kfree(info);
return 0;
}
static struct platform_driver twl6040_vibra_driver = {
.probe = twl6040_vibra_probe,
.remove = __devexit_p(twl6040_vibra_remove),
.driver = {
.name = "twl6040-vibra",
.owner = THIS_MODULE,
.pm = &twl6040_vibra_pm_ops,
},
};
static int __init twl6040_vibra_init(void)
{
return platform_driver_register(&twl6040_vibra_driver);
}
module_init(twl6040_vibra_init);
static void __exit twl6040_vibra_exit(void)
{
platform_driver_unregister(&twl6040_vibra_driver);
}
module_exit(twl6040_vibra_exit);
MODULE_ALIAS("platform:twl6040-vibra");
MODULE_DESCRIPTION("TWL6040 Vibra driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
...@@ -218,7 +218,7 @@ config TWL4030_POWER ...@@ -218,7 +218,7 @@ config TWL4030_POWER
and load scripts controlling which resources are switched off/on and load scripts controlling which resources are switched off/on
or reset when a sleep, wakeup or warm reset event occurs. or reset when a sleep, wakeup or warm reset event occurs.
config TWL4030_CODEC config MFD_TWL4030_AUDIO
bool bool
depends on TWL4030_CORE depends on TWL4030_CORE
select MFD_CORE select MFD_CORE
...@@ -233,6 +233,12 @@ config TWL6030_PWM ...@@ -233,6 +233,12 @@ config TWL6030_PWM
Say yes here if you want support for TWL6030 PWM. Say yes here if you want support for TWL6030 PWM.
This is used to control charging LED brightness. This is used to control charging LED brightness.
config TWL6040_CORE
bool
depends on TWL4030_CORE && GENERIC_HARDIRQS
select MFD_CORE
default n
config MFD_STMPE config MFD_STMPE
bool "Support STMicroelectronics STMPE" bool "Support STMicroelectronics STMPE"
depends on I2C=y && GENERIC_HARDIRQS depends on I2C=y && GENERIC_HARDIRQS
......
...@@ -40,8 +40,9 @@ obj-$(CONFIG_MENELAUS) += menelaus.o ...@@ -40,8 +40,9 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
......
...@@ -110,7 +110,7 @@ ...@@ -110,7 +110,7 @@
#endif #endif
#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\ #if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
defined(CONFIG_SND_SOC_TWL6040) || defined(CONFIG_SND_SOC_TWL6040_MODULE) defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE)
#define twl_has_codec() true #define twl_has_codec() true
#else #else
#define twl_has_codec() false #define twl_has_codec() false
...@@ -815,20 +815,19 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) ...@@ -815,20 +815,19 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child); return PTR_ERR(child);
} }
if (twl_has_codec() && pdata->codec && twl_class_is_4030()) { if (twl_has_codec() && pdata->audio && twl_class_is_4030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
child = add_child(sub_chip_id, "twl4030-audio", child = add_child(sub_chip_id, "twl4030-audio",
pdata->codec, sizeof(*pdata->codec), pdata->audio, sizeof(*pdata->audio),
false, 0, 0); false, 0, 0);
if (IS_ERR(child)) if (IS_ERR(child))
return PTR_ERR(child); return PTR_ERR(child);
} }
/* Phoenix codec driver is probed directly atm */ if (twl_has_codec() && pdata->audio && twl_class_is_6030()) {
if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
child = add_child(sub_chip_id, "twl6040-codec", child = add_child(sub_chip_id, "twl6040",
pdata->codec, sizeof(*pdata->codec), pdata->audio, sizeof(*pdata->audio),
false, 0, 0); false, 0, 0);
if (IS_ERR(child)) if (IS_ERR(child))
return PTR_ERR(child); return PTR_ERR(child);
......
/* /*
* MFD driver for twl4030 codec submodule * MFD driver for twl4030 audio submodule, which contains an audio codec, and
* the vibra control.
* *
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com> * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
...@@ -29,55 +30,55 @@ ...@@ -29,55 +30,55 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/i2c/twl.h> #include <linux/i2c/twl.h>
#include <linux/mfd/core.h> #include <linux/mfd/core.h>
#include <linux/mfd/twl4030-codec.h> #include <linux/mfd/twl4030-audio.h>
#define TWL4030_CODEC_CELLS 2 #define TWL4030_AUDIO_CELLS 2
static struct platform_device *twl4030_codec_dev; static struct platform_device *twl4030_audio_dev;
struct twl4030_codec_resource { struct twl4030_audio_resource {
int request_count; int request_count;
u8 reg; u8 reg;
u8 mask; u8 mask;
}; };
struct twl4030_codec { struct twl4030_audio {
unsigned int audio_mclk; unsigned int audio_mclk;
struct mutex mutex; struct mutex mutex;
struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX]; struct twl4030_audio_resource resource[TWL4030_AUDIO_RES_MAX];
struct mfd_cell cells[TWL4030_CODEC_CELLS]; struct mfd_cell cells[TWL4030_AUDIO_CELLS];
}; };
/* /*
* Modify the resource, the function returns the content of the register * Modify the resource, the function returns the content of the register
* after the modification. * after the modification.
*/ */
static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable) static int twl4030_audio_set_resource(enum twl4030_audio_res id, int enable)
{ {
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
u8 val; u8 val;
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
codec->resource[id].reg); audio->resource[id].reg);
if (enable) if (enable)
val |= codec->resource[id].mask; val |= audio->resource[id].mask;
else else
val &= ~codec->resource[id].mask; val &= ~audio->resource[id].mask;
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
val, codec->resource[id].reg); val, audio->resource[id].reg);
return val; return val;
} }
static inline int twl4030_codec_get_resource(enum twl4030_codec_res id) static inline int twl4030_audio_get_resource(enum twl4030_audio_res id)
{ {
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
u8 val; u8 val;
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
codec->resource[id].reg); audio->resource[id].reg);
return val; return val;
} }
...@@ -86,79 +87,79 @@ static inline int twl4030_codec_get_resource(enum twl4030_codec_res id) ...@@ -86,79 +87,79 @@ static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
* Enable the resource. * Enable the resource.
* The function returns with error or the content of the register * The function returns with error or the content of the register
*/ */
int twl4030_codec_enable_resource(enum twl4030_codec_res id) int twl4030_audio_enable_resource(enum twl4030_audio_res id)
{ {
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
int val; int val;
if (id >= TWL4030_CODEC_RES_MAX) { if (id >= TWL4030_AUDIO_RES_MAX) {
dev_err(&twl4030_codec_dev->dev, dev_err(&twl4030_audio_dev->dev,
"Invalid resource ID (%u)\n", id); "Invalid resource ID (%u)\n", id);
return -EINVAL; return -EINVAL;
} }
mutex_lock(&codec->mutex); mutex_lock(&audio->mutex);
if (!codec->resource[id].request_count) if (!audio->resource[id].request_count)
/* Resource was disabled, enable it */ /* Resource was disabled, enable it */
val = twl4030_codec_set_resource(id, 1); val = twl4030_audio_set_resource(id, 1);
else else
val = twl4030_codec_get_resource(id); val = twl4030_audio_get_resource(id);
codec->resource[id].request_count++; audio->resource[id].request_count++;
mutex_unlock(&codec->mutex); mutex_unlock(&audio->mutex);
return val; return val;
} }
EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource); EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource);
/* /*
* Disable the resource. * Disable the resource.
* The function returns with error or the content of the register * The function returns with error or the content of the register
*/ */
int twl4030_codec_disable_resource(unsigned id) int twl4030_audio_disable_resource(unsigned id)
{ {
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
int val; int val;
if (id >= TWL4030_CODEC_RES_MAX) { if (id >= TWL4030_AUDIO_RES_MAX) {
dev_err(&twl4030_codec_dev->dev, dev_err(&twl4030_audio_dev->dev,
"Invalid resource ID (%u)\n", id); "Invalid resource ID (%u)\n", id);
return -EINVAL; return -EINVAL;
} }
mutex_lock(&codec->mutex); mutex_lock(&audio->mutex);
if (!codec->resource[id].request_count) { if (!audio->resource[id].request_count) {
dev_err(&twl4030_codec_dev->dev, dev_err(&twl4030_audio_dev->dev,
"Resource has been disabled already (%u)\n", id); "Resource has been disabled already (%u)\n", id);
mutex_unlock(&codec->mutex); mutex_unlock(&audio->mutex);
return -EPERM; return -EPERM;
} }
codec->resource[id].request_count--; audio->resource[id].request_count--;
if (!codec->resource[id].request_count) if (!audio->resource[id].request_count)
/* Resource can be disabled now */ /* Resource can be disabled now */
val = twl4030_codec_set_resource(id, 0); val = twl4030_audio_set_resource(id, 0);
else else
val = twl4030_codec_get_resource(id); val = twl4030_audio_get_resource(id);
mutex_unlock(&codec->mutex); mutex_unlock(&audio->mutex);
return val; return val;
} }
EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource); EXPORT_SYMBOL_GPL(twl4030_audio_disable_resource);
unsigned int twl4030_codec_get_mclk(void) unsigned int twl4030_audio_get_mclk(void)
{ {
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
return codec->audio_mclk; return audio->audio_mclk;
} }
EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk); EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk);
static int __devinit twl4030_codec_probe(struct platform_device *pdev) static int __devinit twl4030_audio_probe(struct platform_device *pdev)
{ {
struct twl4030_codec *codec; struct twl4030_audio *audio;
struct twl4030_codec_data *pdata = pdev->dev.platform_data; struct twl4030_audio_data *pdata = pdev->dev.platform_data;
struct mfd_cell *cell = NULL; struct mfd_cell *cell = NULL;
int ret, childs = 0; int ret, childs = 0;
u8 val; u8 val;
...@@ -187,33 +188,33 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) ...@@ -187,33 +188,33 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
val, TWL4030_REG_APLL_CTL); val, TWL4030_REG_APLL_CTL);
codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL); audio = kzalloc(sizeof(struct twl4030_audio), GFP_KERNEL);
if (!codec) if (!audio)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, codec); platform_set_drvdata(pdev, audio);
twl4030_codec_dev = pdev; twl4030_audio_dev = pdev;
mutex_init(&codec->mutex); mutex_init(&audio->mutex);
codec->audio_mclk = pdata->audio_mclk; audio->audio_mclk = pdata->audio_mclk;
/* Codec power */ /* Codec power */
codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE; audio->resource[TWL4030_AUDIO_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ; audio->resource[TWL4030_AUDIO_RES_POWER].mask = TWL4030_CODECPDZ;
/* PLL */ /* PLL */
codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL; audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL;
codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN; audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN;
if (pdata->audio) { if (pdata->codec) {
cell = &codec->cells[childs]; cell = &audio->cells[childs];
cell->name = "twl4030-codec"; cell->name = "twl4030-codec";
cell->platform_data = pdata->audio; cell->platform_data = pdata->codec;
cell->pdata_size = sizeof(*pdata->audio); cell->pdata_size = sizeof(*pdata->codec);
childs++; childs++;
} }
if (pdata->vibra) { if (pdata->vibra) {
cell = &codec->cells[childs]; cell = &audio->cells[childs];
cell->name = "twl4030-vibra"; cell->name = "twl4030-vibra";
cell->platform_data = pdata->vibra; cell->platform_data = pdata->vibra;
cell->pdata_size = sizeof(*pdata->vibra); cell->pdata_size = sizeof(*pdata->vibra);
...@@ -221,7 +222,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) ...@@ -221,7 +222,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
} }
if (childs) if (childs)
ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells, ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells,
childs, NULL, 0); childs, NULL, 0);
else { else {
dev_err(&pdev->dev, "No platform data found for childs\n"); dev_err(&pdev->dev, "No platform data found for childs\n");
...@@ -232,46 +233,45 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) ...@@ -232,46 +233,45 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
return 0; return 0;
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(codec); kfree(audio);
twl4030_codec_dev = NULL; twl4030_audio_dev = NULL;
return ret; return ret;
} }
static int __devexit twl4030_codec_remove(struct platform_device *pdev) static int __devexit twl4030_audio_remove(struct platform_device *pdev)
{ {
struct twl4030_codec *codec = platform_get_drvdata(pdev); struct twl4030_audio *audio = platform_get_drvdata(pdev);
mfd_remove_devices(&pdev->dev); mfd_remove_devices(&pdev->dev);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(codec); kfree(audio);
twl4030_codec_dev = NULL; twl4030_audio_dev = NULL;
return 0; return 0;
} }
MODULE_ALIAS("platform:twl4030-audio"); MODULE_ALIAS("platform:twl4030-audio");
static struct platform_driver twl4030_codec_driver = { static struct platform_driver twl4030_audio_driver = {
.probe = twl4030_codec_probe, .probe = twl4030_audio_probe,
.remove = __devexit_p(twl4030_codec_remove), .remove = __devexit_p(twl4030_audio_remove),
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "twl4030-audio", .name = "twl4030-audio",
}, },
}; };
static int __devinit twl4030_codec_init(void) static int __devinit twl4030_audio_init(void)
{ {
return platform_driver_register(&twl4030_codec_driver); return platform_driver_register(&twl4030_audio_driver);
} }
module_init(twl4030_codec_init); module_init(twl4030_audio_init);
static void __devexit twl4030_codec_exit(void) static void __devexit twl4030_audio_exit(void)
{ {
platform_driver_unregister(&twl4030_codec_driver); platform_driver_unregister(&twl4030_audio_driver);
} }
module_exit(twl4030_codec_exit); module_exit(twl4030_audio_exit);
MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/*
* MFD driver for TWL6040 audio device
*
* Authors: Misael Lopez Cruz <misael.lopez@ti.com>
* Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
* Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/i2c/twl.h>
#include <linux/mfd/core.h>
#include <linux/mfd/twl6040.h>
static struct platform_device *twl6040_dev;
int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
{
int ret;
u8 val = 0;
mutex_lock(&twl6040->io_mutex);
ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg);
if (ret < 0) {
mutex_unlock(&twl6040->io_mutex);
return ret;
}
mutex_unlock(&twl6040->io_mutex);
return val;
}
EXPORT_SYMBOL(twl6040_reg_read);
int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
{
int ret;
mutex_lock(&twl6040->io_mutex);
ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg);
mutex_unlock(&twl6040->io_mutex);
return ret;
}
EXPORT_SYMBOL(twl6040_reg_write);
int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
{
int ret;
u8 val;
mutex_lock(&twl6040->io_mutex);
ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg);
if (ret)
goto out;
val |= mask;
ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg);
out:
mutex_unlock(&twl6040->io_mutex);
return ret;
}
EXPORT_SYMBOL(twl6040_set_bits);
int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
{
int ret;
u8 val;
mutex_lock(&twl6040->io_mutex);
ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg);
if (ret)
goto out;
val &= ~mask;
ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg);
out:
mutex_unlock(&twl6040->io_mutex);
return ret;
}
EXPORT_SYMBOL(twl6040_clear_bits);
/* twl6040 codec manual power-up sequence */
static int twl6040_power_up(struct twl6040 *twl6040)
{
u8 ldoctl, ncpctl, lppllctl;
int ret;
/* enable high-side LDO, reference system and internal oscillator */
ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA;
ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
if (ret)
return ret;
usleep_range(10000, 10500);
/* enable negative charge pump */
ncpctl = TWL6040_NCPENA;
ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
if (ret)
goto ncp_err;
usleep_range(1000, 1500);
/* enable low-side LDO */
ldoctl |= TWL6040_LSLDOENA;
ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
if (ret)
goto lsldo_err;
usleep_range(1000, 1500);
/* enable low-power PLL */
lppllctl = TWL6040_LPLLENA;
ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
if (ret)
goto lppll_err;
usleep_range(5000, 5500);
/* disable internal oscillator */
ldoctl &= ~TWL6040_OSCENA;
ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
if (ret)
goto osc_err;
return 0;
osc_err:
lppllctl &= ~TWL6040_LPLLENA;
twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
lppll_err:
ldoctl &= ~TWL6040_LSLDOENA;
twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
lsldo_err:
ncpctl &= ~TWL6040_NCPENA;
twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
ncp_err:
ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
return ret;
}
/* twl6040 manual power-down sequence */
static void twl6040_power_down(struct twl6040 *twl6040)
{
u8 ncpctl, ldoctl, lppllctl;
ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL);
ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL);
lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
/* enable internal oscillator */
ldoctl |= TWL6040_OSCENA;
twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
usleep_range(1000, 1500);
/* disable low-power PLL */
lppllctl &= ~TWL6040_LPLLENA;
twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
/* disable low-side LDO */
ldoctl &= ~TWL6040_LSLDOENA;
twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
/* disable negative charge pump */
ncpctl &= ~TWL6040_NCPENA;
twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
/* disable high-side LDO, reference system and internal oscillator */
ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
}
static irqreturn_t twl6040_naudint_handler(int irq, void *data)
{
struct twl6040 *twl6040 = data;
u8 intid, status;
intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
if (intid & TWL6040_READYINT)
complete(&twl6040->ready);
if (intid & TWL6040_THINT) {
status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
if (status & TWL6040_TSHUTDET) {
dev_warn(&twl6040_dev->dev,
"Thermal shutdown, powering-off");
twl6040_power(twl6040, 0);
} else {
dev_warn(&twl6040_dev->dev,
"Leaving thermal shutdown, powering-on");
twl6040_power(twl6040, 1);
}
}
return IRQ_HANDLED;
}
static int twl6040_power_up_completion(struct twl6040 *twl6040,
int naudint)
{
int time_left;
u8 intid;
time_left = wait_for_completion_timeout(&twl6040->ready,
msecs_to_jiffies(144));
if (!time_left) {
intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
if (!(intid & TWL6040_READYINT)) {
dev_err(&twl6040_dev->dev,
"timeout waiting for READYINT\n");
return -ETIMEDOUT;
}
}
return 0;
}
int twl6040_power(struct twl6040 *twl6040, int on)
{
int audpwron = twl6040->audpwron;
int naudint = twl6040->irq;
int ret = 0;
mutex_lock(&twl6040->mutex);
if (on) {
/* already powered-up */
if (twl6040->power_count++)
goto out;
if (gpio_is_valid(audpwron)) {
/* use AUDPWRON line */
gpio_set_value(audpwron, 1);
/* wait for power-up completion */
ret = twl6040_power_up_completion(twl6040, naudint);
if (ret) {
dev_err(&twl6040_dev->dev,
"automatic power-down failed\n");
twl6040->power_count = 0;
goto out;
}
} else {
/* use manual power-up sequence */
ret = twl6040_power_up(twl6040);
if (ret) {
dev_err(&twl6040_dev->dev,
"manual power-up failed\n");
twl6040->power_count = 0;
goto out;
}
}
/* Default PLL configuration after power up */
twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
twl6040->sysclk = 19200000;
} else {
/* already powered-down */
if (!twl6040->power_count) {
dev_err(&twl6040_dev->dev,
"device is already powered-off\n");
ret = -EPERM;
goto out;
}
if (--twl6040->power_count)
goto out;
if (gpio_is_valid(audpwron)) {
/* use AUDPWRON line */
gpio_set_value(audpwron, 0);
/* power-down sequence latency */
usleep_range(500, 700);
} else {
/* use manual power-down sequence */
twl6040_power_down(twl6040);
}
twl6040->sysclk = 0;
}
out:
mutex_unlock(&twl6040->mutex);
return ret;
}
EXPORT_SYMBOL(twl6040_power);
int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
unsigned int freq_in, unsigned int freq_out)
{
u8 hppllctl, lppllctl;
int ret = 0;
mutex_lock(&twl6040->mutex);
hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL);
lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
switch (pll_id) {
case TWL6040_SYSCLK_SEL_LPPLL:
/* low-power PLL divider */
switch (freq_out) {
case 17640000:
lppllctl |= TWL6040_LPLLFIN;
break;
case 19200000:
lppllctl &= ~TWL6040_LPLLFIN;
break;
default:
dev_err(&twl6040_dev->dev,
"freq_out %d not supported\n", freq_out);
ret = -EINVAL;
goto pll_out;
}
twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
switch (freq_in) {
case 32768:
lppllctl |= TWL6040_LPLLENA;
twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
lppllctl);
mdelay(5);
lppllctl &= ~TWL6040_HPLLSEL;
twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
lppllctl);
hppllctl &= ~TWL6040_HPLLENA;
twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
hppllctl);
break;
default:
dev_err(&twl6040_dev->dev,
"freq_in %d not supported\n", freq_in);
ret = -EINVAL;
goto pll_out;
}
break;
case TWL6040_SYSCLK_SEL_HPPLL:
/* high-performance PLL can provide only 19.2 MHz */
if (freq_out != 19200000) {
dev_err(&twl6040_dev->dev,
"freq_out %d not supported\n", freq_out);
ret = -EINVAL;
goto pll_out;
}
hppllctl &= ~TWL6040_MCLK_MSK;
switch (freq_in) {
case 12000000:
/* PLL enabled, active mode */
hppllctl |= TWL6040_MCLK_12000KHZ |
TWL6040_HPLLENA;
break;
case 19200000:
/*
* PLL disabled
* (enable PLL if MCLK jitter quality
* doesn't meet specification)
*/
hppllctl |= TWL6040_MCLK_19200KHZ;
break;
case 26000000:
/* PLL enabled, active mode */
hppllctl |= TWL6040_MCLK_26000KHZ |
TWL6040_HPLLENA;
break;
case 38400000:
/* PLL enabled, active mode */
hppllctl |= TWL6040_MCLK_38400KHZ |
TWL6040_HPLLENA;
break;
default:
dev_err(&twl6040_dev->dev,
"freq_in %d not supported\n", freq_in);
ret = -EINVAL;
goto pll_out;
}
/* enable clock slicer to ensure input waveform is square */
hppllctl |= TWL6040_HPLLSQRENA;
twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, hppllctl);
usleep_range(500, 700);
lppllctl |= TWL6040_HPLLSEL;
twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
lppllctl &= ~TWL6040_LPLLENA;
twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
break;
default:
dev_err(&twl6040_dev->dev, "unknown pll id %d\n", pll_id);
ret = -EINVAL;
goto pll_out;
}
twl6040->sysclk = freq_out;
twl6040->pll = pll_id;
pll_out:
mutex_unlock(&twl6040->mutex);
return ret;
}
EXPORT_SYMBOL(twl6040_set_pll);
int twl6040_get_pll(struct twl6040 *twl6040)
{
if (twl6040->power_count)
return twl6040->pll;
else
return -ENODEV;
}
EXPORT_SYMBOL(twl6040_get_pll);
unsigned int twl6040_get_sysclk(struct twl6040 *twl6040)
{
return twl6040->sysclk;
}
EXPORT_SYMBOL(twl6040_get_sysclk);
static struct resource twl6040_vibra_rsrc[] = {
{
.flags = IORESOURCE_IRQ,
},
};
static struct resource twl6040_codec_rsrc[] = {
{
.flags = IORESOURCE_IRQ,
},
};
static int __devinit twl6040_probe(struct platform_device *pdev)
{
struct twl4030_audio_data *pdata = pdev->dev.platform_data;
struct twl6040 *twl6040;
struct mfd_cell *cell = NULL;
int ret, children = 0;
if (!pdata) {
dev_err(&pdev->dev, "Platform data is missing\n");
return -EINVAL;
}
/* In order to operate correctly we need valid interrupt config */
if (!pdata->naudint_irq || !pdata->irq_base) {
dev_err(&pdev->dev, "Invalid IRQ configuration\n");
return -EINVAL;
}
twl6040 = kzalloc(sizeof(struct twl6040), GFP_KERNEL);
if (!twl6040)
return -ENOMEM;
platform_set_drvdata(pdev, twl6040);
twl6040_dev = pdev;
twl6040->dev = &pdev->dev;
twl6040->audpwron = pdata->audpwron_gpio;
twl6040->irq = pdata->naudint_irq;
twl6040->irq_base = pdata->irq_base;
mutex_init(&twl6040->mutex);
mutex_init(&twl6040->io_mutex);
init_completion(&twl6040->ready);
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
if (gpio_is_valid(twl6040->audpwron)) {
ret = gpio_request(twl6040->audpwron, "audpwron");
if (ret)
goto gpio1_err;
ret = gpio_direction_output(twl6040->audpwron, 0);
if (ret)
goto gpio2_err;
}
/* ERRATA: Automatic power-up is not possible in ES1.0 */
if (twl6040->rev == TWL6040_REV_ES1_0)
twl6040->audpwron = -EINVAL;
/* codec interrupt */
ret = twl6040_irq_init(twl6040);
if (ret)
goto gpio2_err;
ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY,
NULL, twl6040_naudint_handler, 0,
"twl6040_irq_ready", twl6040);
if (ret) {
dev_err(twl6040->dev, "READY IRQ request failed: %d\n",
ret);
goto irq_err;
}
/* dual-access registers controlled by I2C only */
twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
if (pdata->codec) {
int irq = twl6040->irq_base + TWL6040_IRQ_PLUG;
cell = &twl6040->cells[children];
cell->name = "twl6040-codec";
twl6040_codec_rsrc[0].start = irq;
twl6040_codec_rsrc[0].end = irq;
cell->resources = twl6040_codec_rsrc;
cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
cell->platform_data = pdata->codec;
cell->pdata_size = sizeof(*pdata->codec);
children++;
}
if (pdata->vibra) {
int irq = twl6040->irq_base + TWL6040_IRQ_VIB;
cell = &twl6040->cells[children];
cell->name = "twl6040-vibra";
twl6040_vibra_rsrc[0].start = irq;
twl6040_vibra_rsrc[0].end = irq;
cell->resources = twl6040_vibra_rsrc;
cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
cell->platform_data = pdata->vibra;
cell->pdata_size = sizeof(*pdata->vibra);
children++;
}
if (children) {
ret = mfd_add_devices(&pdev->dev, pdev->id, twl6040->cells,
children, NULL, 0);
if (ret)
goto mfd_err;
} else {
dev_err(&pdev->dev, "No platform data found for children\n");
ret = -ENODEV;
goto mfd_err;
}
return 0;
mfd_err:
free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
irq_err:
twl6040_irq_exit(twl6040);
gpio2_err:
if (gpio_is_valid(twl6040->audpwron))
gpio_free(twl6040->audpwron);
gpio1_err:
platform_set_drvdata(pdev, NULL);
kfree(twl6040);
twl6040_dev = NULL;
return ret;
}
static int __devexit twl6040_remove(struct platform_device *pdev)
{
struct twl6040 *twl6040 = platform_get_drvdata(pdev);
if (twl6040->power_count)
twl6040_power(twl6040, 0);
if (gpio_is_valid(twl6040->audpwron))
gpio_free(twl6040->audpwron);
free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
twl6040_irq_exit(twl6040);
mfd_remove_devices(&pdev->dev);
platform_set_drvdata(pdev, NULL);
kfree(twl6040);
twl6040_dev = NULL;
return 0;
}
static struct platform_driver twl6040_driver = {
.probe = twl6040_probe,
.remove = __devexit_p(twl6040_remove),
.driver = {
.owner = THIS_MODULE,
.name = "twl6040",
},
};
static int __devinit twl6040_init(void)
{
return platform_driver_register(&twl6040_driver);
}
module_init(twl6040_init);
static void __devexit twl6040_exit(void)
{
platform_driver_unregister(&twl6040_driver);
}
module_exit(twl6040_exit);
MODULE_DESCRIPTION("TWL6040 MFD");
MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:twl6040");
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
#include <plat/mcbsp.h> #include <plat/mcbsp.h>
/* Register descriptions for twl4030 codec part */ /* Register descriptions for twl4030 codec part */
#include <linux/mfd/twl4030-codec.h> #include <linux/mfd/twl4030-audio.h>
#include "omap-mcbsp.h" #include "omap-mcbsp.h"
#include "omap-pcm.h" #include "omap-pcm.h"
......
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册