diff options
51 files changed, 3575 insertions, 1380 deletions
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index a7c0b31fd084..c7cef44c75d4 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -345,11 +345,40 @@ static struct platform_device sdp4430_lcd_device = { .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 = { &sdp4430_lcd_device, &sdp4430_gpio_keys_device, &sdp4430_leds_gpio, &sdp4430_leds_pwm, + &sdp4430_vbat, }; static struct omap_lcd_config sdp4430_lcd_config __initdata = { @@ -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 = { + .audio = &twl6040_audio, /* Regulators */ .vusim = &sdp4430_vusim, .vaux1 = &sdp4430_vaux1, diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index 0c9e0f28ed07..cc503aa89c5e 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -864,11 +864,11 @@ static struct twl4030_power_data rx51_t2scripts_data __initdata = { .resource_config = twl4030_rconfig, }; -struct twl4030_codec_vibra_data rx51_vibra_data __initdata = { +struct twl4030_vibra_data rx51_vibra_data __initdata = { .coexist = 0, }; -struct twl4030_codec_data rx51_codec_data __initdata = { +struct twl4030_audio_data rx51_audio_data __initdata = { .audio_mclk = 26000000, .vibra = &rx51_vibra_data, }; @@ -878,7 +878,7 @@ static struct twl4030_platform_data rx51_twldata __initdata = { .gpio = &rx51_gpio_data, .keypad = &rx51_kp_data, .power = &rx51_t2scripts_data, - .codec = &rx51_codec_data, + .audio = &rx51_audio_data, .vaux1 = &rx51_vaux1, .vaux2 = &rx51_vaux2, diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c index 13a644233667..6d0aa4fcb7c3 100644 --- a/arch/arm/mach-omap2/board-zoom-peripherals.c +++ b/arch/arm/mach-omap2/board-zoom-peripherals.c @@ -274,12 +274,12 @@ static int __init omap_i2c_init(void) TWL_COMMON_REGULATOR_VDAC | TWL_COMMON_REGULATOR_VPLL2); if (machine_is_omap_zoom2()) { - struct twl4030_codec_audio_data *audio_data; - audio_data = zoom_twldata.codec->audio; + struct twl4030_codec_data *codec_data; + codec_data = zoom_twldata.audio->codec; - audio_data->ramp_delay_value = 3; /* 161 ms */ - audio_data->hs_extmute = 1; - audio_data->set_hs_extmute = zoom2_set_hs_extmute; + codec_data->ramp_delay_value = 3; /* 161 ms */ + codec_data->hs_extmute = 1; + codec_data->set_hs_extmute = zoom2_set_hs_extmute; } omap_pmic_init(1, 2400, "twl5030", INT_34XX_SYS_NIRQ, &zoom_twldata); omap_register_i2c_bus(2, 400, NULL, 0); diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index bf9c36c7dffd..1f3481f8d695 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c @@ -38,6 +38,14 @@ 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 */ @@ -100,6 +108,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 * @clk: struct clk * to find the companion clock of * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in @@ -268,7 +289,7 @@ void omap2_clk_disable(struct clk *clk) clk->ops->disable(clk); } - if (clk->clkdm) + if (clkdm_control && clk->clkdm) clkdm_clk_disable(clk->clkdm, clk); if (clk->parent) @@ -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); if (ret) { WARN(1, "clock: %s: could not enable clockdomain %s: " @@ -330,7 +351,7 @@ int omap2_clk_enable(struct clk *clk) return 0; oce_err3: - if (clk->clkdm) + if (clkdm_control && clk->clkdm) clkdm_clk_disable(clk->clkdm, clk); oce_err2: if (clk->parent) diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index e10ff2b54844..48ac568881bd 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h @@ -16,6 +16,8 @@ #ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H #define __ARCH_ARM_MACH_OMAP2_CLOCK_H +#include <linux/kernel.h> + #include <plat/clock.h> /* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */ @@ -72,6 +74,7 @@ void omap2_clk_disable_unused(struct clk *clk); #endif void omap2_init_clk_clkdm(struct clk *clk); +void __init omap2_clk_disable_clkdm_control(void); /* clkt_clksel.c public functions */ u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, diff --git a/arch/arm/mach-omap2/clock2420_data.c b/arch/arm/mach-omap2/clock2420_data.c index 2926d028b6e9..debc040872f1 100644 --- a/arch/arm/mach-omap2/clock2420_data.c +++ b/arch/arm/mach-omap2/clock2420_data.c @@ -1805,9 +1805,9 @@ static struct omap_clk omap2420_clks[] = { CLK(NULL, "gfx_ick", &gfx_ick, CK_242X), /* DSS domain clocks */ CLK("omapdss_dss", "ick", &dss_ick, CK_242X), - CLK("omapdss_dss", "fck", &dss1_fck, CK_242X), - CLK("omapdss_dss", "sys_clk", &dss2_fck, CK_242X), - CLK("omapdss_dss", "tv_clk", &dss_54m_fck, CK_242X), + CLK(NULL, "dss1_fck", &dss1_fck, CK_242X), + CLK(NULL, "dss2_fck", &dss2_fck, CK_242X), + CLK(NULL, "dss_54m_fck", &dss_54m_fck, CK_242X), /* L3 domain clocks */ CLK(NULL, "core_l3_ck", &core_l3_ck, CK_242X), CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_242X), @@ -1844,13 +1844,13 @@ static struct omap_clk omap2420_clks[] = { CLK(NULL, "gpt12_ick", &gpt12_ick, CK_242X), CLK(NULL, "gpt12_fck", &gpt12_fck, 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", "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", "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", "fck", &mcspi2_fck, CK_242X), + CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_242X), CLK(NULL, "uart1_ick", &uart1_ick, CK_242X), CLK(NULL, "uart1_fck", &uart1_fck, CK_242X), CLK(NULL, "uart2_ick", &uart2_ick, CK_242X), @@ -1860,7 +1860,7 @@ static struct omap_clk omap2420_clks[] = { CLK(NULL, "gpios_ick", &gpios_ick, CK_242X), CLK(NULL, "gpios_fck", &gpios_fck, 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, "wdt1_ick", &wdt1_ick, CK_242X), CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_242X), @@ -1880,11 +1880,11 @@ static struct omap_clk omap2420_clks[] = { CLK(NULL, "eac_ick", &eac_ick, CK_242X), CLK(NULL, "eac_fck", &eac_fck, 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", "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", "fck", &i2c2_fck, CK_242X), + CLK(NULL, "i2c2_fck", &i2c2_fck, CK_242X), CLK(NULL, "gpmc_fck", &gpmc_fck, CK_242X), CLK(NULL, "sdma_fck", &sdma_fck, CK_242X), CLK(NULL, "sdma_ick", &sdma_ick, CK_242X), diff --git a/arch/arm/mach-omap2/clock2430_data.c b/arch/arm/mach-omap2/clock2430_data.c index 0c79d39e3021..96a942e42db1 100644 --- a/arch/arm/mach-omap2/clock2430_data.c +++ b/arch/arm/mach-omap2/clock2430_data.c @@ -1895,9 +1895,9 @@ static struct omap_clk omap2430_clks[] = { CLK(NULL, "mdm_osc_ck", &mdm_osc_ck, CK_243X), /* DSS domain clocks */ CLK("omapdss_dss", "ick", &dss_ick, CK_243X), - CLK("omapdss_dss", "fck", &dss1_fck, CK_243X), - CLK("omapdss_dss", "sys_clk", &dss2_fck, CK_243X), - CLK("omapdss_dss", "tv_clk", &dss_54m_fck, CK_243X), + CLK(NULL, "dss1_fck", &dss1_fck, CK_243X), + CLK(NULL, "dss2_fck", &dss2_fck, CK_243X), + CLK(NULL, "dss_54m_fck", &dss_54m_fck, CK_243X), /* L3 domain clocks */ CLK(NULL, "core_l3_ck", &core_l3_ck, CK_243X), CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_243X), @@ -1934,21 +1934,21 @@ static struct omap_clk omap2430_clks[] = { CLK(NULL, "gpt12_ick", &gpt12_ick, CK_243X), CLK(NULL, "gpt12_fck", &gpt12_fck, 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", "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", "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", "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", "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", "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", "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", "fck", &mcspi3_fck, CK_243X), + CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_243X), CLK(NULL, "uart1_ick", &uart1_ick, CK_243X), CLK(NULL, "uart1_fck", &uart1_fck, CK_243X), CLK(NULL, "uart2_ick", &uart2_ick, CK_243X), @@ -1958,7 +1958,7 @@ static struct omap_clk omap2430_clks[] = { CLK(NULL, "gpios_ick", &gpios_ick, CK_243X), CLK(NULL, "gpios_fck", &gpios_fck, 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, "wdt1_ick", &wdt1_ick, CK_243X), CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_243X), @@ -1975,9 +1975,9 @@ static struct omap_clk omap2430_clks[] = { CLK("omap_hdq.0", "ick", &hdq_ick, CK_243X), CLK("omap_hdq.1", "fck", &hdq_fck, 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", "fck", &i2chs2_fck, CK_243X), + CLK(NULL, "i2chs2_fck", &i2chs2_fck, CK_243X), CLK(NULL, "gpmc_fck", &gpmc_fck, CK_243X), CLK(NULL, "sdma_fck", &sdma_fck, CK_243X), CLK(NULL, "sdma_ick", &sdma_ick, CK_243X), @@ -1990,9 +1990,9 @@ static struct omap_clk omap2430_clks[] = { CLK(NULL, "usb_fck", &usb_fck, CK_243X), CLK("musb-omap2430", "ick", &usbhs_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", "fck", &mmchs2_fck, CK_243X), + CLK(NULL, "mmchs2_fck", &mmchs2_fck, CK_243X), CLK(NULL, "gpio5_ick", &gpio5_ick, CK_243X), CLK(NULL, "gpio5_fck", &gpio5_fck, CK_243X), CLK(NULL, "mdm_intc_ick", &mdm_intc_ick, CK_243X), diff --git a/arch/arm/mach-omap2/clock3xxx_data.c b/arch/arm/mach-omap2/clock3xxx_data.c index 75b119bd9cda..ffd55b1c4396 100644 --- a/arch/arm/mach-omap2/clock3xxx_data.c +++ b/arch/arm/mach-omap2/clock3xxx_data.c @@ -3289,25 +3289,25 @@ static struct omap_clk omap3xxx_clks[] = { CLK("omap-mcbsp.1", "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("omap_hsmmc.2", "fck", &mmchs3_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), - CLK("omap_hsmmc.1", "fck", &mmchs2_fck, CK_3XXX), + CLK(NULL, "mmchs3_fck", &mmchs3_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), + CLK(NULL, "mmchs2_fck", &mmchs2_fck, CK_3XXX), CLK(NULL, "mspro_fck", &mspro_fck, CK_34XX | CK_36XX), - CLK("omap_hsmmc.0", "fck", &mmchs1_fck, CK_3XXX), - CLK("omap_i2c.3", "fck", &i2c3_fck, CK_3XXX), - CLK("omap_i2c.2", "fck", &i2c2_fck, CK_3XXX), - CLK("omap_i2c.1", "fck", &i2c1_fck, CK_3XXX), - CLK("omap-mcbsp.5", "fck", &mcbsp5_fck, CK_3XXX), - CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_3XXX), + CLK(NULL, "mmchs1_fck", &mmchs1_fck, CK_3XXX), + CLK(NULL, "i2c3_fck", &i2c3_fck, CK_3XXX), + CLK(NULL, "i2c2_fck", &i2c2_fck, CK_3XXX), + CLK(NULL, "i2c1_fck", &i2c1_fck, CK_3XXX), + CLK(NULL, "mcbsp5_fck", &mcbsp5_fck, CK_3XXX), + CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_3XXX), CLK(NULL, "core_48m_fck", &core_48m_fck, CK_3XXX), - CLK("omap2_mcspi.4", "fck", &mcspi4_fck, CK_3XXX), - CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_3XXX), - CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_3XXX), - CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_3XXX), + CLK(NULL, "mcspi4_fck", &mcspi4_fck, CK_3XXX), + CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_3XXX), + CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_3XXX), + CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_3XXX), CLK(NULL, "uart2_fck", &uart2_fck, CK_3XXX), CLK(NULL, "uart1_fck", &uart1_fck, CK_3XXX), CLK(NULL, "fshostusb_fck", &fshostusb_fck, CK_3430ES1), CLK(NULL, "core_12m_fck", &core_12m_fck, CK_3XXX), - CLK("omap_hdq.0", "fck", &hdq_fck, CK_3XXX), + CLK("omap_hdq.0", "fck", &hdq_fck, CK_3XXX), CLK(NULL, "ssi_ssr_fck", &ssi_ssr_fck_3430es1, CK_3430ES1), CLK(NULL, "ssi_ssr_fck", &ssi_ssr_fck_3430es2, CK_3430ES2PLUS | CK_36XX), CLK(NULL, "ssi_sst_fck", &ssi_sst_fck_3430es1, CK_3430ES1), @@ -3356,11 +3356,11 @@ static struct omap_clk omap3xxx_clks[] = { CLK("omap_rng", "ick", &rng_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("omapdss_dss", "fck", &dss1_alwon_fck_3430es1, CK_3430ES1), - CLK("omapdss_dss", "fck", &dss1_alwon_fck_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), - CLK("omapdss_dss", "tv_clk", &dss_tv_fck, CK_3XXX), - CLK("omapdss_dss", "video_clk", &dss_96m_fck, CK_3XXX), - CLK("omapdss_dss", "sys_clk", &dss2_alwon_fck, CK_3XXX), + CLK(NULL, "dss1_alwon_fck", &dss1_alwon_fck_3430es1, CK_3430ES1), + CLK(NULL, "dss1_alwon_fck", &dss1_alwon_fck_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), + CLK(NULL, "dss_tv_fck", &dss_tv_fck, CK_3XXX), + CLK(NULL, "dss_96m_fck", &dss_96m_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_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), CLK(NULL, "cam_mclk", &cam_mclk, CK_34XX | CK_36XX), @@ -3385,7 +3385,7 @@ static struct omap_clk omap3xxx_clks[] = { CLK(NULL, "gpt1_fck", &gpt1_fck, CK_3XXX), CLK(NULL, "wkup_32k_fck", &wkup_32k_fck, 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, "usim_ick", &usim_ick, CK_3430ES2PLUS | CK_36XX), CLK("omap_wdt", "ick", &wdt2_ick, CK_3XXX), @@ -3436,9 +3436,9 @@ static struct omap_clk omap3xxx_clks[] = { CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_3XXX), CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_3XXX), CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_3XXX), - CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_3XXX), - CLK("omap-mcbsp.3", "fck", &mcbsp3_fck, CK_3XXX), - CLK("omap-mcbsp.4", "fck", &mcbsp4_fck, CK_3XXX), + CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_3XXX), + CLK(NULL, "mcbsp3_fck", &mcbsp3_fck, CK_3XXX), + CLK(NULL, "mcbsp4_fck", &mcbsp4_fck, CK_3XXX), CLK("etb", "emu_src_ck", &emu_src_ck, CK_3XXX), CLK(NULL, "pclk_fck", &pclk_fck, CK_3XXX), CLK(NULL, "pclkx2_fck", &pclkx2_fck, CK_3XXX), diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c index 25473a1db3e7..2af0e3f00ce1 100644 --- a/arch/arm/mach-omap2/clock44xx_data.c +++ b/arch/arm/mach-omap2/clock44xx_data.c @@ -2808,19 +2808,39 @@ static struct clk trace_clk_div_ck = { /* 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 = &dpll_core_m3x2_ck, .rates = div_1_1_rates }, { .parent = &dpll_per_m3x2_ck, .rates = div_1_2_rates }, { .parent = NULL }, }; -static struct clk auxclk0_ck = { - .name = "auxclk0_ck", +static const struct clksel_rate div16_1to16_rates[] = { + { .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, .init = &omap2_init_clksel_parent, .ops = &clkops_omap2_dflt, - .clksel = auxclk_sel, + .clksel = auxclk_src_sel, .clksel_reg = OMAP4_SCRM_AUXCLK0, .clksel_mask = OMAP4_SRCSELECT_MASK, .recalc = &omap2_clksel_recalc, @@ -2828,12 +2848,29 @@ static struct clk auxclk0_ck = { .enable_bit = OMAP4_ENABLE_SHIFT, }; -static struct clk auxclk1_ck = { - .name = "auxclk1_ck", +static const struct clksel auxclk0_sel[] = { + { .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, .init = &omap2_init_clksel_parent, .ops = &clkops_omap2_dflt, - .clksel = auxclk_sel, + .clksel = auxclk_src_sel, .clksel_reg = OMAP4_SCRM_AUXCLK1, .clksel_mask = OMAP4_SRCSELECT_MASK, .recalc = &omap2_clksel_recalc, @@ -2841,12 +2878,29 @@ static struct clk auxclk1_ck = { .enable_bit = OMAP4_ENABLE_SHIFT, }; -static struct clk auxclk2_ck = { - .name = "auxclk2_ck", +static const struct clksel auxclk1_sel[] = { + { .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, .init = &omap2_init_clksel_parent, .ops = &clkops_omap2_dflt, - .clksel = auxclk_sel, + .clksel = auxclk_src_sel, .clksel_reg = OMAP4_SCRM_AUXCLK2, .clksel_mask = OMAP4_SRCSELECT_MASK, .recalc = &omap2_clksel_recalc, @@ -2854,12 +2908,29 @@ static struct clk auxclk2_ck = { .enable_bit = OMAP4_ENABLE_SHIFT, }; -static struct clk auxclk3_ck = { - .name = "auxclk3_ck", +static const struct clksel auxclk2_sel[] = { + { .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, .init = &omap2_init_clksel_parent, .ops = &clkops_omap2_dflt, - .clksel = auxclk_sel, + .clksel = auxclk_src_sel, .clksel_reg = OMAP4_SCRM_AUXCLK3, .clksel_mask = OMAP4_SRCSELECT_MASK, .recalc = &omap2_clksel_recalc, @@ -2867,12 +2938,29 @@ static struct clk auxclk3_ck = { .enable_bit = OMAP4_ENABLE_SHIFT, }; -static struct clk auxclk4_ck = { - .name = "auxclk4_ck", +static const struct clksel auxclk3_sel[] = { + { .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, .init = &omap2_init_clksel_parent, .ops = &clkops_omap2_dflt, - .clksel = auxclk_sel, + .clksel = auxclk_src_sel, .clksel_reg = OMAP4_SCRM_AUXCLK4, .clksel_mask = OMAP4_SRCSELECT_MASK, .recalc = &omap2_clksel_recalc, @@ -2880,12 +2968,29 @@ static struct clk auxclk4_ck = { .enable_bit = OMAP4_ENABLE_SHIFT, }; -static struct clk auxclk5_ck = { - .name = "auxclk5_ck", +static const struct clksel auxclk4_sel[] = { + { .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, .init = &omap2_init_clksel_parent, .ops = &clkops_omap2_dflt, - .clksel = auxclk_sel, + .clksel = auxclk_src_sel, .clksel_reg = OMAP4_SCRM_AUXCLK5, .clksel_mask = OMAP4_SRCSELECT_MASK, .recalc = &omap2_clksel_recalc, @@ -2893,6 +2998,23 @@ static struct clk auxclk5_ck = { .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[] = { { .parent = &auxclk0_ck, .rates = div_1_0_rates }, { .parent = &auxclk1_ck, .rates = div_1_1_rates }, @@ -3093,12 +3215,12 @@ static struct omap_clk omap44xx_clks[] = { CLK(NULL, "gpio6_ick", &gpio6_ick, CK_443X), CLK(NULL, "gpmc_ick", &gpmc_ick, 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("omap_i2c.1", "fck", &i2c1_fck, CK_443X), - CLK("omap_i2c.2", "fck", &i2c2_fck, CK_443X), - CLK("omap_i2c.3", "fck", &i2c3_fck, CK_443X), - CLK("omap_i2c.4", "fck", &i2c4_fck, CK_443X), + CLK(NULL, "i2c1_fck", &i2c1_fck, CK_443X), + CLK(NULL, "i2c2_fck", &i2c2_fck, CK_443X), + CLK(NULL, "i2c3_fck", &i2c3_fck, CK_443X), + CLK(NULL, "i2c4_fck", &i2c4_fck, CK_443X), CLK(NULL, "ipu_fck", &ipu_fck, CK_443X), CLK(NULL, "iss_ctrlclk", &iss_ctrlclk, CK_443X), CLK(NULL, "iss_fck", &iss_fck, CK_443X), @@ -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_fck", &mcasp_fck, 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("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("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("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("omap2_mcspi.1", "fck", &mcspi1_fck, CK_443X), - CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_443X), - CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_443X), - CLK("omap2_mcspi.4", "fck", &mcspi4_fck, CK_443X), - CLK("omap_hsmmc.0", "fck", &mmc1_fck, CK_443X), - CLK("omap_hsmmc.1", "fck", &mmc2_fck, CK_443X), - CLK("omap_hsmmc.2", "fck", &mmc3_fck, CK_443X), - CLK("omap_hsmmc.3", "fck", &mmc4_fck, CK_443X), - CLK("omap_hsmmc.4", "fck", &mmc5_fck, CK_443X), + CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_443X), + CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_443X), + CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_443X), + CLK(NULL, "mcspi4_fck", &mcspi4_fck, CK_443X), + CLK(NULL, "mmc1_fck", &mmc1_fck, CK_443X), + CLK(NULL, "mmc2_fck", &mmc2_fck, CK_443X), + CLK(NULL, "mmc3_fck", &mmc3_fck, CK_443X), + CLK(NULL, "mmc4_fck", &mmc4_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_ick", &ocp2scp_usb_phy_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[] = { CLK(NULL, "usim_ck", &usim_ck, CK_443X), CLK(NULL, "usim_fclk", &usim_fclk, 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, "stm_clk_div_ck", &stm_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, "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, "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, "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, "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, "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, "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, "gpmc_ck", &dummy_ck, CK_443X), CLK(NULL, "gpt1_ick", &dummy_ck, CK_443X), @@ -3251,6 +3379,7 @@ int __init omap4xxx_clk_init(void) } clk_init(&omap2_clk_functions); + omap2_clk_disable_clkdm_control(); for (c = omap44xx_clks; c < omap44xx_clks + ARRAY_SIZE(omap44xx_clks); c++) diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index 6cb6c03293df..ab7db083f97f 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c @@ -1,8 +1,8 @@ /* * OMAP2/3/4 clockdomain framework functions * - * Copyright (C) 2008-2010 Texas Instruments, Inc. - * Copyright (C) 2008-2010 Nokia Corporation + * Copyright (C) 2008-2011 Texas Instruments, Inc. + * Copyright (C) 2008-2011 Nokia Corporation * * Written by Paul Walmsley and Jouni Högander * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com> @@ -92,6 +92,8 @@ static int _clkdm_register(struct clockdomain *clkdm) pwrdm_add_clkdm(pwrdm, clkdm); + spin_lock_init(&clkdm->lock); + pr_debug("clockdomain: registered %s\n", clkdm->name); return 0; @@ -690,6 +692,9 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) */ int clkdm_sleep(struct clockdomain *clkdm) { + int ret; + unsigned long flags; + if (!clkdm) return -EINVAL; @@ -704,7 +709,11 @@ int clkdm_sleep(struct clockdomain *clkdm) 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) */ int clkdm_wakeup(struct clockdomain *clkdm) { + int ret; + unsigned long flags; + if (!clkdm) return -EINVAL; @@ -732,7 +744,11 @@ int clkdm_wakeup(struct clockdomain *clkdm) 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) */ void clkdm_allow_idle(struct clockdomain *clkdm) { + unsigned long flags; + if (!clkdm) return; @@ -762,8 +780,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm) pr_debug("clockdomain: enabling automatic idle transitions for %s\n", clkdm->name); + spin_lock_irqsave(&clkdm->lock, flags); + clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED; arch_clkdm->clkdm_allow_idle(clkdm); pwrdm_clkdm_state_switch(clkdm); + spin_unlock_irqrestore(&clkdm->lock, flags); } /** @@ -777,6 +798,8 @@ void clkdm_allow_idle(struct clockdomain *clkdm) */ void clkdm_deny_idle(struct clockdomain *clkdm) { + unsigned long flags; + if (!clkdm) return; @@ -792,11 +815,90 @@ void clkdm_deny_idle(struct clockdomain *clkdm) pr_debug("clockdomain: disabling automatic idle transitions for %s\n", clkdm->name); + spin_lock_irqsave(&clkdm->lock, flags); + clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; 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 @@ -819,25 +921,10 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) * downstream clocks for debugging purposes? */ - if (!clkdm || !clk) + if (!clk) return -EINVAL; - if (!arch_clkdm || !arch_clkdm->clkdm_clk_enable) - 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; + return _clkdm_clk_hwmod_enable(clkdm); } /** @@ -850,9 +937,8 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) * 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 and debugging - * is enabled; or returns 0 upon success or if the clockdomain is in - * hwsup idle mode. + * pointers; -ERANGE if the @clkdm usecount underflows; or returns 0 + * upon success or if the clockdomain is in hwsup idle mode. */ 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? */ - if (!clkdm || !clk) + if (!clk) return -EINVAL; - if (!arch_clkdm || !arch_clkdm->clkdm_clk_disable) + return _clkdm_clk_hwmod_disable(clkdm); +} + +/** + * 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; + + /* + * XXX Rewrite this code to maintain a list of enabled + * downstream hwmods for debugging purposes? + */ + + if (!oh) return -EINVAL; -#ifdef DEBUG - if (atomic_read(&clkdm->usecount) == 0) { - WARN_ON(1); /* underflow */ - return -ERANGE; - } -#endif + return _clkdm_clk_hwmod_enable(clkdm); +} - if (atomic_dec_return(&clkdm->usecount) > 0) +/** + * 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; - /* All downstream clocks of this clockdomain are now disabled */ - - pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name, - clk->name); + /* + * XXX Rewrite this code to maintain a list of enabled + * downstream hwmods for debugging purposes? + */ - arch_clkdm->clkdm_clk_disable(clkdm); - pwrdm_clkdm_state_switch(clkdm); + if (!oh) + return -EINVAL; - return 0; + return _clkdm_clk_hwmod_disable(clkdm); } diff --git a/arch/arm/mach-omap2/clockdomain.h b/arch/arm/mach-omap2/clockdomain.h index 5823584d9cd7..1e50c88b8a07 100644 --- a/arch/arm/mach-omap2/clockdomain.h +++ b/arch/arm/mach-omap2/clockdomain.h @@ -17,9 +17,11 @@ #define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H #include <linux/init.h> +#include <linux/spinlock.h> #include "powerdomain.h" #include <plat/clock.h> +#include <plat/omap_hwmod.h> #include <plat/cpu.h> /* @@ -82,6 +84,9 @@ struct clkdm_dep { const struct omap_chip_id omap_chip; }; +/* Possible flags for struct clockdomain._flags */ +#define _CLKDM_FLAG_HWSUP_ENABLED BIT(0) + /** * struct clockdomain - OMAP clockdomain * @name: clockdomain name @@ -89,6 +94,7 @@ struct clkdm_dep { * @clktrctrl_reg: CLKSTCTRL reg for the given clock domain * @clktrctrl_mask: CLKTRCTRL/AUTOSTATE field mask in CM_CLKSTCTRL reg * @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 * @prcm_partition: (OMAP4 only) PRCM partition ID for this clkdm's registers * @cm_inst: (OMAP4 only) CM instance register offset @@ -113,6 +119,7 @@ struct clockdomain { } pwrdm; const u16 clktrctrl_mask; const u8 flags; + u8 _flags; const u8 dep_bit; const u8 prcm_partition; const s16 cm_inst; @@ -122,6 +129,7 @@ struct clockdomain { const struct omap_chip_id omap_chip; atomic_t usecount; struct list_head node; + spinlock_t lock; }; /** @@ -177,12 +185,15 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm); void clkdm_allow_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_sleep(struct clockdomain *clkdm); int clkdm_clk_enable(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 omap3xxx_clockdomains_init(void); diff --git a/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c b/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c index 48d0db7e6069..f740edb111f4 100644 --- a/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c +++ b/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c @@ -183,7 +183,8 @@ static int omap2_clkdm_clk_enable(struct clockdomain *clkdm) _clkdm_add_autodeps(clkdm); _enable_hwsup(clkdm); } else { - clkdm_wakeup(clkdm); + if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) + omap2_clkdm_wakeup(clkdm); } return 0; @@ -205,7 +206,8 @@ static int omap2_clkdm_clk_disable(struct clockdomain *clkdm) _clkdm_del_autodeps(clkdm); _enable_hwsup(clkdm); } else { - clkdm_sleep(clkdm); + if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP) + omap2_clkdm_sleep(clkdm); } return 0; diff --git a/arch/arm/mach-omap2/clockdomain44xx.c b/arch/arm/mach-omap2/clockdomain44xx.c index a1a4ecd26544..b43706aa08bd 100644 --- a/arch/arm/mach-omap2/clockdomain44xx.c +++ b/arch/arm/mach-omap2/clockdomain44xx.c @@ -95,13 +95,8 @@ static void omap4_clkdm_deny_idle(struct clockdomain *clkdm) static int omap4_clkdm_clk_enable(struct clockdomain *clkdm) { - bool hwsup = false; - - hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition, - clkdm->cm_inst, clkdm->clkdm_offs); - - if (!hwsup) - clkdm_wakeup(clkdm); + if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) + return omap4_clkdm_wakeup(clkdm); return 0; } @@ -113,8 +108,8 @@ static int omap4_clkdm_clk_disable(struct clockdomain *clkdm) hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition, clkdm->cm_inst, clkdm->clkdm_offs); - if (!hwsup) - clkdm_sleep(clkdm); + if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) + omap4_clkdm_sleep(clkdm); return 0; } diff --git a/arch/arm/mach-omap2/clockdomains44xx_data.c b/arch/arm/mach-omap2/clockdomains44xx_data.c index 66090f2676ce..dccc651fa0d0 100644 --- a/arch/arm/mach-omap2/clockdomains44xx_data.c +++ b/arch/arm/mach-omap2/clockdomains44xx_data.c @@ -565,7 +565,7 @@ static struct clockdomain ducati_44xx_clkdm = { }; static struct clockdomain mpu_44xx_clkdm = { - .name = "mpu_clkdm", + .name = "mpuss_clkdm", .pwrdm = { .name = "mpu_pwrdm" }, .prcm_partition = OMAP4430_CM1_PARTITION, .cm_inst = OMAP4430_CM1_MPU_INST, diff --git a/arch/arm/mach-omap2/cm44xx.h b/arch/arm/mach-omap2/cm44xx.h index 0b87ec82b41c..3380beeace6e 100644 --- a/arch/arm/mach-omap2/cm44xx.h +++ b/arch/arm/mach-omap2/cm44xx.h @@ -1,7 +1,7 @@ /* * 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 * * Written by Paul Walmsley @@ -23,10 +23,4 @@ #define OMAP4_CM_CLKSTCTRL 0x0000 #define OMAP4_CM_STATICDEP 0x0004 -/* Function prototypes */ -# ifndef __ASSEMBLER__ - -extern int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg); - -# endif #endif diff --git a/arch/arm/mach-omap2/cminst44xx.c b/arch/arm/mach-omap2/cminst44xx.c index a482bfa0a954..eb2a472bbf46 100644 --- a/arch/arm/mach-omap2/cminst44xx.c +++ b/arch/arm/mach-omap2/cminst44xx.c @@ -2,6 +2,7 @@ * OMAP4 CM instance functions * * Copyright (C) 2009 Nokia Corporation + * Copyright (C) 2011 Texas Instruments, Inc. * Paul Walmsley * * This program is free software; you can redistribute it and/or modify @@ -32,6 +33,22 @@ #include "prm44xx.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] = { [OMAP4430_INVALID_PRCM_PARTITION] = 0, [OMAP4430_PRM_PARTITION] = OMAP4430_PRM_BASE, @@ -41,6 +58,48 @@ static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = { [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 */ 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) */ /** - * omap4_cm_wait_module_ready - wait for a module to be in 'func' state - * @clkctrl_reg: CLKCTRL module address + * omap4_cminst_wait_module_ready - wait for a module to be in 'func' 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) * * 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 * sysconfig cannot be accessed and will probably lead to an "imprecise * 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: - * 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 - * + * Wait for the module IDLEST to be disabled. Some PRCM transition, + * like reset assertion or parent clock de-activation must wait the + * module to be fully disabled. */ -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; - if (!clkctrl_reg) + if (!clkctrl_offs) return 0; - omap_test_timeout(( - ((__raw_readl(clkctrl_reg) & OMAP4430_IDLEST_MASK) == 0) || - (((__raw_readl(clkctrl_reg) & OMAP4430_IDLEST_MASK) >> - OMAP4430_IDLEST_SHIFT) == 0x2)), - MAX_MODULE_READY_TIME, i); + omap_test_timeout((_clkctrl_idlest(part, inst, cdoffs, clkctrl_offs) == + CLKCTRL_IDLEST_DISABLED), + MAX_MODULE_READY_TIME, i); 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); +} diff --git a/arch/arm/mach-omap2/cminst44xx.h b/arch/arm/mach-omap2/cminst44xx.h index 2b32c181a2ee..f2ea6453ade0 100644 --- a/arch/arm/mach-omap2/cminst44xx.h +++ b/arch/arm/mach-omap2/cminst44xx.h @@ -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_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, * 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, extern u32 omap4_cminst_read_inst_reg_bits(u8 part, u16 inst, s16 idx, u32 mask); -extern int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg); - #endif diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 02b6016393a8..84cc0bdda3ae 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -146,9 +146,10 @@ #include <plat/prcm.h> #include "cm2xxx_3xxx.h" -#include "cm44xx.h" +#include "cminst44xx.h" #include "prm2xxx_3xxx.h" #include "prm44xx.h" +#include "prminst44xx.h" #include "mux.h" /* Maximum microseconds to wait for OMAP module to softreset */ @@ -679,6 +680,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 * @oh: struct omap_hwmod *oh * @@ -990,9 +1041,40 @@ static struct omap_hwmod *_lookup(const char *name) 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 * * @data: not used; pass NULL * @@ -1012,6 +1094,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data) ret |= _init_main_clk(oh); ret |= _init_interface_clks(oh); ret |= _init_opt_clks(oh); + ret |= _init_clkdm(oh); if (!ret) oh->_state = _HWMOD_STATE_CLKS_INITED; @@ -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 * does not have an IDLEST bit or if the module successfully leaves * 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) { @@ -1055,7 +1138,13 @@ static int _wait_target_ready(struct omap_hwmod *oh) oh->prcm.omap2.idlest_reg_id, oh->prcm.omap2.idlest_idle_bit); } 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 { BUG(); }; @@ -1064,6 +1153,36 @@ static int _wait_target_ready(struct omap_hwmod *oh) } /** + * _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 * @oh: struct omap_hwmod * * @name: name of the reset line in the context of this hwmod @@ -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, ohri.rst_shift); else if (cpu_is_omap44xx()) - return omap4_prm_assert_hardreset(oh->prcm.omap4.rstctrl_reg, - ohri.rst_shift); + return omap4_prminst_assert_hardreset(ohri.rst_shift, + oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.rstctrl_offs); else return -EINVAL; } @@ -1155,8 +1276,10 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name) if (ohri.st_shift) pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n", oh->name, name); - ret = omap4_prm_deassert_hardreset(oh->prcm.omap4.rstctrl_reg, - ohri.rst_shift); + ret = omap4_prminst_deassert_hardreset(ohri.rst_shift, + oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.rstctrl_offs); } else { return -EINVAL; } @@ -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, ohri.st_shift); } else if (cpu_is_omap44xx()) { - return omap4_prm_is_hardreset_asserted(oh->prcm.omap4.rstctrl_reg, - ohri.rst_shift); + return omap4_prminst_is_hardreset_asserted(ohri.rst_shift, + oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.rstctrl_offs); } else { return -EINVAL; } @@ -1312,6 +1437,7 @@ static int _reset(struct omap_hwmod *oh) static int _enable(struct omap_hwmod *oh) { int r; + int hwsup = 0; pr_debug("omap_hwmod: %s: enabling\n", oh->name); @@ -1323,14 +1449,6 @@ static int _enable(struct omap_hwmod *oh) 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 @@ -1341,22 +1459,56 @@ static int _enable(struct omap_hwmod *oh) oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1) _deassert_hardreset(oh, oh->rst_lines[0].name); - r = _wait_target_ready(oh); - if (r) { - pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n", - oh->name, r); - _disable_clocks(oh); + /* 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); - return r; + _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; + } } - oh->_state = _HWMOD_STATE_ENABLED; + _enable_clocks(oh); + _enable_module(oh); - /* Access the sysconfig only if the target is ready */ - if (oh->class->sysc) { - if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) - _update_sysc_cache(oh); - _enable_sysc(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; + + /* Access the sysconfig only if the target is ready */ + if (oh->class->sysc) { + if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) + _update_sysc_cache(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; @@ -1372,6 +1524,8 @@ static int _enable(struct omap_hwmod *oh) */ static int _idle(struct omap_hwmod *oh) { + int ret; + pr_debug("omap_hwmod: %s: idling\n", oh->name); if (oh->_state != _HWMOD_STATE_ENABLED) { @@ -1383,7 +1537,20 @@ static int _idle(struct omap_hwmod *oh) if (oh->class->sysc) _idle_sysc(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); + if (oh->clkdm) + clkdm_hwmod_disable(oh->clkdm, oh); /* Mux pins for device idle if populated */ if (oh->mux && oh->mux->pads_dynamic) @@ -1475,7 +1642,14 @@ static int _shutdown(struct omap_hwmod *oh) if (oh->_state == _HWMOD_STATE_ENABLED) { _del_initiator_dep(oh, mpu_oh); /* 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); + if (oh->clkdm) + clkdm_hwmod_disable(oh->clkdm, oh); } /* XXX Should this code also force-disable the optional clocks? */ diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 5d5df49749df..6201422c0606 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -123,9 +123,16 @@ static struct omap_hwmod_ocp_if *omap44xx_dmm_slaves[] = { static struct omap_hwmod omap44xx_dmm_hwmod = { .name = "dmm", .class = &omap44xx_dmm_hwmod_class, - .mpu_irqs = omap44xx_dmm_irqs, + .clkdm_name = "l3_emif_clkdm", + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM_MEMIF_DMM_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_MEMIF_DMM_CONTEXT_OFFSET, + }, + }, .slaves = omap44xx_dmm_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_dmm_slaves), + .mpu_irqs = omap44xx_dmm_irqs, .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), }; @@ -173,6 +180,13 @@ static struct omap_hwmod_ocp_if *omap44xx_emif_fw_slaves[] = { static struct omap_hwmod omap44xx_emif_fw_hwmod = { .name = "emif_fw", .class = &omap44xx_emif_fw_hwmod_class, + .clkdm_name = "l3_emif_clkdm", + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM_MEMIF_EMIF_FW_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_MEMIF_EMIF_FW_CONTEXT_OFFSET, + }, + }, .slaves = omap44xx_emif_fw_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_emif_fw_slaves), .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -212,6 +226,14 @@ static struct omap_hwmod_ocp_if *omap44xx_l3_instr_slaves[] = { static struct omap_hwmod omap44xx_l3_instr_hwmod = { .name = "l3_instr", .class = &omap44xx_l3_hwmod_class, + .clkdm_name = "l3_instr_clkdm", + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM_L3INSTR_L3_INSTR_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L3INSTR_L3_INSTR_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, + }, + }, .slaves = omap44xx_l3_instr_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_l3_instr_slaves), .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -304,7 +326,14 @@ static struct omap_hwmod_ocp_if *omap44xx_l3_main_1_slaves[] = { static struct omap_hwmod omap44xx_l3_main_1_hwmod = { .name = "l3_main_1", .class = &omap44xx_l3_hwmod_class, + .clkdm_name = "l3_1_clkdm", .mpu_irqs = omap44xx_l3_main_1_irqs, + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM_L3_1_L3_1_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L3_1_L3_1_CONTEXT_OFFSET, + }, + }, .slaves = omap44xx_l3_main_1_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_l3_main_1_slaves), .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -400,6 +429,13 @@ static struct omap_hwmod_ocp_if *omap44xx_l3_main_2_slaves[] = { static struct omap_hwmod omap44xx_l3_main_2_hwmod = { .name = "l3_main_2", .class = &omap44xx_l3_hwmod_class, + .clkdm_name = "l3_2_clkdm", + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM_L3_2_L3_2_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L3_2_L3_2_CONTEXT_OFFSET, + }, + }, .slaves = omap44xx_l3_main_2_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_l3_main_2_slaves), .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -450,6 +486,14 @@ static struct omap_hwmod_ocp_if *omap44xx_l3_main_3_slaves[] = { static struct omap_hwmod omap44xx_l3_main_3_hwmod = { .name = "l3_main_3", .class = &omap44xx_l3_hwmod_class, + .clkdm_name = "l3_instr_clkdm", + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM_L3INSTR_L3_3_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L3INSTR_L3_3_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, + }, + }, .slaves = omap44xx_l3_main_3_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_l3_main_3_slaves), .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -507,6 +551,12 @@ static struct omap_hwmod_ocp_if *omap44xx_l4_abe_slaves[] = { static struct omap_hwmod omap44xx_l4_abe_hwmod = { .name = "l4_abe", .class = &omap44xx_l4_hwmod_class, + .clkdm_name = "abe_clkdm", + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM1_ABE_L4ABE_CLKCTRL_OFFSET, + }, + }, .slaves = omap44xx_l4_abe_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_l4_abe_slaves), .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -529,6 +579,13 @@ static struct omap_hwmod_ocp_if *omap44xx_l4_cfg_slaves[] = { static struct omap_hwmod omap44xx_l4_cfg_hwmod = { .name = "l4_cfg", .class = &omap44xx_l4_hwmod_class, + .clkdm_name = "l4_cfg_clkdm", + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM_L4CFG_L4_CFG_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4CFG_L4_CFG_CONTEXT_OFFSET, + }, + }, .slaves = omap44xx_l4_cfg_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_l4_cfg_slaves), .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -551,6 +608,13 @@ static struct omap_hwmod_ocp_if *omap44xx_l4_per_slaves[] = { static struct omap_hwmod omap44xx_l4_per_hwmod = { .name = "l4_per", .class = &omap44xx_l4_hwmod_class, + .clkdm_name = "l4_per_clkdm", + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM_L4PER_L4PER_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_L4_PER_CONTEXT_OFFSET, + }, + }, .slaves = omap44xx_l4_per_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_l4_per_slaves), .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -573,6 +637,13 @@ static struct omap_hwmod_ocp_if *omap44xx_l4_wkup_slaves[] = { static struct omap_hwmod omap44xx_l4_wkup_hwmod = { .name = "l4_wkup", .class = &omap44xx_l4_hwmod_class, + .clkdm_name = "l4_wkup_clkdm", + .prcm = { + .omap4 = { + .clkctrl_offs = OMAP4_CM_WKUP_L4WKUP_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_WKUP_L4WKUP_CONTEXT_OFFSET, + }, + }, .slaves = omap44xx_l4_wkup_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_l4_wkup_slaves), .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -603,6 +674,7 @@ static struct omap_hwmod_ocp_if *omap44xx_mpu_private_slaves[] = { static struct omap_hwmod omap44xx_mpu_private_hwmod = { .name = "mpu_private", .class = &omap44xx_mpu_bus_hwmod_class, + .clkdm_name = "mpuss_clkdm", .slaves = omap44xx_mpu_private_slaves, .slaves_cnt = ARRAY_SIZE(omap44xx_mpu_private_slaves), .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -741,12 +813,15 @@ static struct omap_hwmod_ocp_if *omap44xx_aess_slaves[] = { static struct omap_hwmod omap44xx_aess_hwmod = { .name = "aess", .class = &omap44xx_aess_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_aess_irqs, .sdma_reqs = omap44xx_aess_sdma_reqs, .main_clk = "aess_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_AESS_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_AESS_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_AESS_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_aess_slaves, @@ -773,9 +848,10 @@ static struct omap_hwmod_opt_clk bandgap_opt_clks[] = { static struct omap_hwmod omap44xx_bandgap_hwmod = { .name = "bandgap", .class = &omap44xx_bandgap_hwmod_class, + .clkdm_name = "l4_wkup_clkdm", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_WKUP_BANDGAP_CLKCTRL, + .clkctrl_offs = OMAP4_CM_WKUP_BANDGAP_CLKCTRL_OFFSET, }, }, .opt_clks = bandgap_opt_clks, @@ -830,11 +906,13 @@ static struct omap_hwmod_ocp_if *omap44xx_counter_32k_slaves[] = { static struct omap_hwmod omap44xx_counter_32k_hwmod = { .name = "counter_32k", .class = &omap44xx_counter_hwmod_class, + .clkdm_name = "l4_wkup_clkdm", .flags = HWMOD_SWSUP_SIDLE, .main_clk = "sys_32k_ck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_WKUP_SYNCTIMER_CLKCTRL, + .clkctrl_offs = OMAP4_CM_WKUP_SYNCTIMER_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_WKUP_SYNCTIMER_CONTEXT_OFFSET, }, }, .slaves = omap44xx_counter_32k_slaves, @@ -913,11 +991,13 @@ static struct omap_hwmod_ocp_if *omap44xx_dma_system_slaves[] = { static struct omap_hwmod omap44xx_dma_system_hwmod = { .name = "dma_system", .class = &omap44xx_dma_hwmod_class, + .clkdm_name = "l3_dma_clkdm", .mpu_irqs = omap44xx_dma_system_irqs, .main_clk = "l3_div_ck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_SDMA_SDMA_CLKCTRL, + .clkctrl_offs = OMAP4_CM_SDMA_SDMA_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_SDMA_SDMA_CONTEXT_OFFSET, }, }, .dev_attr = &dma_dev_attr, @@ -1005,12 +1085,15 @@ static struct omap_hwmod_ocp_if *omap44xx_dmic_slaves[] = { static struct omap_hwmod omap44xx_dmic_hwmod = { .name = "dmic", .class = &omap44xx_dmic_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_dmic_irqs, .sdma_reqs = omap44xx_dmic_sdma_reqs, .main_clk = "dmic_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_DMIC_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_DMIC_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_DMIC_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_dmic_slaves, @@ -1072,12 +1155,13 @@ static struct omap_hwmod_ocp_if *omap44xx_dsp_slaves[] = { static struct omap_hwmod omap44xx_dsp_c0_hwmod = { .name = "dsp_c0", .class = &omap44xx_dsp_hwmod_class, + .clkdm_name = "tesla_clkdm", .flags = HWMOD_INIT_NO_RESET, .rst_lines = omap44xx_dsp_c0_resets, .rst_lines_cnt = ARRAY_SIZE(omap44xx_dsp_c0_resets), .prcm = { .omap4 = { - .rstctrl_reg = OMAP4430_RM_TESLA_RSTCTRL, + .rstctrl_offs = OMAP4_RM_TESLA_RSTCTRL_OFFSET, }, }, .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -1086,14 +1170,17 @@ static struct omap_hwmod omap44xx_dsp_c0_hwmod = { static struct omap_hwmod omap44xx_dsp_hwmod = { .name = "dsp", .class = &omap44xx_dsp_hwmod_class, + .clkdm_name = "tesla_clkdm", .mpu_irqs = omap44xx_dsp_irqs, .rst_lines = omap44xx_dsp_resets, .rst_lines_cnt = ARRAY_SIZE(omap44xx_dsp_resets), .main_clk = "dsp_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_TESLA_TESLA_CLKCTRL, - .rstctrl_reg = OMAP4430_RM_TESLA_RSTCTRL, + .clkctrl_offs = OMAP4_CM_TESLA_TESLA_CLKCTRL_OFFSET, + .rstctrl_offs = OMAP4_RM_TESLA_RSTCTRL_OFFSET, + .context_offs = OMAP4_RM_TESLA_TESLA_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .slaves = omap44xx_dsp_slaves, @@ -1177,10 +1264,12 @@ static struct omap_hwmod_opt_clk dss_opt_clks[] = { static struct omap_hwmod omap44xx_dss_hwmod = { .name = "dss_core", .class = &omap44xx_dss_hwmod_class, + .clkdm_name = "l3_dss_clkdm", .main_clk = "dss_dss_clk", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL, + .clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET, }, }, .opt_clks = dss_opt_clks, @@ -1278,13 +1367,14 @@ static struct omap_hwmod_opt_clk dss_dispc_opt_clks[] = { static struct omap_hwmod omap44xx_dss_dispc_hwmod = { .name = "dss_dispc", .class = &omap44xx_dispc_hwmod_class, - .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .clkdm_name = "l3_dss_clkdm", .mpu_irqs = omap44xx_dss_dispc_irqs, .sdma_reqs = omap44xx_dss_dispc_sdma_reqs, .main_clk = "dss_dss_clk", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL, + .clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET, }, }, .opt_clks = dss_dispc_opt_clks, @@ -1376,12 +1466,14 @@ static struct omap_hwmod_opt_clk dss_dsi1_opt_clks[] = { static struct omap_hwmod omap44xx_dss_dsi1_hwmod = { .name = "dss_dsi1", .class = &omap44xx_dsi_hwmod_class, + .clkdm_name = "l3_dss_clkdm", .mpu_irqs = omap44xx_dss_dsi1_irqs, .sdma_reqs = omap44xx_dss_dsi1_sdma_reqs, .main_clk = "dss_dss_clk", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL, + .clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET, }, }, .opt_clks = dss_dsi1_opt_clks, @@ -1452,12 +1544,14 @@ static struct omap_hwmod_opt_clk dss_dsi2_opt_clks[] = { static struct omap_hwmod omap44xx_dss_dsi2_hwmod = { .name = "dss_dsi2", .class = &omap44xx_dsi_hwmod_class, + .clkdm_name = "l3_dss_clkdm", .mpu_irqs = omap44xx_dss_dsi2_irqs, .sdma_reqs = omap44xx_dss_dsi2_sdma_reqs, .main_clk = "dss_dss_clk", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL, + .clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET, }, }, .opt_clks = dss_dsi2_opt_clks, @@ -1548,12 +1642,14 @@ static struct omap_hwmod_opt_clk dss_hdmi_opt_clks[] = { static struct omap_hwmod omap44xx_dss_hdmi_hwmod = { .name = "dss_hdmi", .class = &omap44xx_hdmi_hwmod_class, + .clkdm_name = "l3_dss_clkdm", .mpu_irqs = omap44xx_dss_hdmi_irqs, .sdma_reqs = omap44xx_dss_hdmi_sdma_reqs, .main_clk = "dss_dss_clk", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL, + .clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET, }, }, .opt_clks = dss_hdmi_opt_clks, @@ -1639,11 +1735,13 @@ static struct omap_hwmod_opt_clk dss_rfbi_opt_clks[] = { static struct omap_hwmod omap44xx_dss_rfbi_hwmod = { .name = "dss_rfbi", .class = &omap44xx_rfbi_hwmod_class, + .clkdm_name = "l3_dss_clkdm", .sdma_reqs = omap44xx_dss_rfbi_sdma_reqs, .main_clk = "dss_dss_clk", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL, + .clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET, }, }, .opt_clks = dss_rfbi_opt_clks, @@ -1709,10 +1807,12 @@ static struct omap_hwmod_ocp_if *omap44xx_dss_venc_slaves[] = { static struct omap_hwmod omap44xx_dss_venc_hwmod = { .name = "dss_venc", .class = &omap44xx_venc_hwmod_class, + .clkdm_name = "l3_dss_clkdm", .main_clk = "dss_dss_clk", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_DSS_DSS_CLKCTRL, + .clkctrl_offs = OMAP4_CM_DSS_DSS_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_DSS_DSS_CONTEXT_OFFSET, }, }, .slaves = omap44xx_dss_venc_slaves, @@ -1786,11 +1886,14 @@ static struct omap_hwmod_opt_clk gpio1_opt_clks[] = { static struct omap_hwmod omap44xx_gpio1_hwmod = { .name = "gpio1", .class = &omap44xx_gpio_hwmod_class, + .clkdm_name = "l4_wkup_clkdm", .mpu_irqs = omap44xx_gpio1_irqs, .main_clk = "gpio1_ick", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_WKUP_GPIO1_CLKCTRL, + .clkctrl_offs = OMAP4_CM_WKUP_GPIO1_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_WKUP_GPIO1_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .opt_clks = gpio1_opt_clks, @@ -1838,12 +1941,15 @@ static struct omap_hwmod_opt_clk gpio2_opt_clks[] = { static struct omap_hwmod omap44xx_gpio2_hwmod = { .name = "gpio2", .class = &omap44xx_gpio_hwmod_class, + .clkdm_name = "l4_per_clkdm", .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, .mpu_irqs = omap44xx_gpio2_irqs, .main_clk = "gpio2_ick", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_GPIO2_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_GPIO2_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_GPIO2_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .opt_clks = gpio2_opt_clks, @@ -1891,12 +1997,15 @@ static struct omap_hwmod_opt_clk gpio3_opt_clks[] = { static struct omap_hwmod omap44xx_gpio3_hwmod = { .name = "gpio3", .class = &omap44xx_gpio_hwmod_class, + .clkdm_name = "l4_per_clkdm", .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, .mpu_irqs = omap44xx_gpio3_irqs, .main_clk = "gpio3_ick", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_GPIO3_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_GPIO3_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_GPIO3_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .opt_clks = gpio3_opt_clks, @@ -1944,12 +2053,15 @@ static struct omap_hwmod_opt_clk gpio4_opt_clks[] = { static struct omap_hwmod omap44xx_gpio4_hwmod = { .name = "gpio4", .class = &omap44xx_gpio_hwmod_class, + .clkdm_name = "l4_per_clkdm", .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, .mpu_irqs = omap44xx_gpio4_irqs, .main_clk = "gpio4_ick", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_GPIO4_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_GPIO4_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_GPIO4_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .opt_clks = gpio4_opt_clks, @@ -1997,12 +2109,15 @@ static struct omap_hwmod_opt_clk gpio5_opt_clks[] = { static struct omap_hwmod omap44xx_gpio5_hwmod = { .name = "gpio5", .class = &omap44xx_gpio_hwmod_class, + .clkdm_name = "l4_per_clkdm", .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, .mpu_irqs = omap44xx_gpio5_irqs, .main_clk = "gpio5_ick", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_GPIO5_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_GPIO5_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_GPIO5_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .opt_clks = gpio5_opt_clks, @@ -2050,12 +2165,15 @@ static struct omap_hwmod_opt_clk gpio6_opt_clks[] = { static struct omap_hwmod omap44xx_gpio6_hwmod = { .name = "gpio6", .class = &omap44xx_gpio_hwmod_class, + .clkdm_name = "l4_per_clkdm", .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, .mpu_irqs = omap44xx_gpio6_irqs, .main_clk = "gpio6_ick", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_GPIO6_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_GPIO6_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_GPIO6_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .opt_clks = gpio6_opt_clks, @@ -2129,11 +2247,14 @@ static struct omap_hwmod_ocp_if *omap44xx_hsi_slaves[] = { static struct omap_hwmod omap44xx_hsi_hwmod = { .name = "hsi", .class = &omap44xx_hsi_hwmod_class, + .clkdm_name = "l3_init_clkdm", .mpu_irqs = omap44xx_hsi_irqs, .main_clk = "hsi_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L3INIT_HSI_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L3INIT_HSI_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L3INIT_HSI_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .slaves = omap44xx_hsi_slaves, @@ -2209,13 +2330,16 @@ static struct omap_hwmod_ocp_if *omap44xx_i2c1_slaves[] = { static struct omap_hwmod omap44xx_i2c1_hwmod = { .name = "i2c1", .class = &omap44xx_i2c_hwmod_class, + .clkdm_name = "l4_per_clkdm", .flags = HWMOD_16BIT_REG, .mpu_irqs = omap44xx_i2c1_irqs, .sdma_reqs = omap44xx_i2c1_sdma_reqs, .main_clk = "i2c1_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_I2C1_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_I2C1_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_I2C1_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_i2c1_slaves, @@ -2263,13 +2387,16 @@ static struct omap_hwmod_ocp_if *omap44xx_i2c2_slaves[] = { static struct omap_hwmod omap44xx_i2c2_hwmod = { .name = "i2c2", .class = &omap44xx_i2c_hwmod_class, + .clkdm_name = "l4_per_clkdm", .flags = HWMOD_16BIT_REG, .mpu_irqs = omap44xx_i2c2_irqs, .sdma_reqs = omap44xx_i2c2_sdma_reqs, .main_clk = "i2c2_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_I2C2_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_I2C2_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_I2C2_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_i2c2_slaves, @@ -2317,13 +2444,16 @@ static struct omap_hwmod_ocp_if *omap44xx_i2c3_slaves[] = { static struct omap_hwmod omap44xx_i2c3_hwmod = { .name = "i2c3", .class = &omap44xx_i2c_hwmod_class, + .clkdm_name = "l4_per_clkdm", .flags = HWMOD_16BIT_REG, .mpu_irqs = omap44xx_i2c3_irqs, .sdma_reqs = omap44xx_i2c3_sdma_reqs, .main_clk = "i2c3_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_I2C3_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_I2C3_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_I2C3_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_i2c3_slaves, @@ -2371,13 +2501,16 @@ static struct omap_hwmod_ocp_if *omap44xx_i2c4_slaves[] = { static struct omap_hwmod omap44xx_i2c4_hwmod = { .name = "i2c4", .class = &omap44xx_i2c_hwmod_class, + .clkdm_name = "l4_per_clkdm", .flags = HWMOD_16BIT_REG, .mpu_irqs = omap44xx_i2c4_irqs, .sdma_reqs = omap44xx_i2c4_sdma_reqs, .main_clk = "i2c4_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_I2C4_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_I2C4_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_I2C4_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_i2c4_slaves, @@ -2435,12 +2568,13 @@ static struct omap_hwmod_ocp_if *omap44xx_ipu_slaves[] = { static struct omap_hwmod omap44xx_ipu_c0_hwmod = { .name = "ipu_c0", .class = &omap44xx_ipu_hwmod_class, + .clkdm_name = "ducati_clkdm", .flags = HWMOD_INIT_NO_RESET, .rst_lines = omap44xx_ipu_c0_resets, .rst_lines_cnt = ARRAY_SIZE(omap44xx_ipu_c0_resets), .prcm = { .omap4 = { - .rstctrl_reg = OMAP4430_RM_DUCATI_RSTCTRL, + .rstctrl_offs = OMAP4_RM_DUCATI_RSTCTRL_OFFSET, }, }, .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -2450,12 +2584,13 @@ static struct omap_hwmod omap44xx_ipu_c0_hwmod = { static struct omap_hwmod omap44xx_ipu_c1_hwmod = { .name = "ipu_c1", .class = &omap44xx_ipu_hwmod_class, + .clkdm_name = "ducati_clkdm", .flags = HWMOD_INIT_NO_RESET, .rst_lines = omap44xx_ipu_c1_resets, .rst_lines_cnt = ARRAY_SIZE(omap44xx_ipu_c1_resets), .prcm = { .omap4 = { - .rstctrl_reg = OMAP4430_RM_DUCATI_RSTCTRL, + .rstctrl_offs = OMAP4_RM_DUCATI_RSTCTRL_OFFSET, }, }, .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -2464,14 +2599,17 @@ static struct omap_hwmod omap44xx_ipu_c1_hwmod = { static struct omap_hwmod omap44xx_ipu_hwmod = { .name = "ipu", .class = &omap44xx_ipu_hwmod_class, + .clkdm_name = "ducati_clkdm", .mpu_irqs = omap44xx_ipu_irqs, .rst_lines = omap44xx_ipu_resets, .rst_lines_cnt = ARRAY_SIZE(omap44xx_ipu_resets), .main_clk = "ipu_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_DUCATI_DUCATI_CLKCTRL, - .rstctrl_reg = OMAP4430_RM_DUCATI_RSTCTRL, + .clkctrl_offs = OMAP4_CM_DUCATI_DUCATI_CLKCTRL_OFFSET, + .rstctrl_offs = OMAP4_RM_DUCATI_RSTCTRL_OFFSET, + .context_offs = OMAP4_RM_DUCATI_DUCATI_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .slaves = omap44xx_ipu_slaves, @@ -2551,12 +2689,15 @@ static struct omap_hwmod_opt_clk iss_opt_clks[] = { static struct omap_hwmod omap44xx_iss_hwmod = { .name = "iss", .class = &omap44xx_iss_hwmod_class, + .clkdm_name = "iss_clkdm", .mpu_irqs = omap44xx_iss_irqs, .sdma_reqs = omap44xx_iss_sdma_reqs, .main_clk = "iss_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_CAM_ISS_CLKCTRL, + .clkctrl_offs = OMAP4_CM_CAM_ISS_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_CAM_ISS_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .opt_clks = iss_opt_clks, @@ -2631,12 +2772,13 @@ static struct omap_hwmod_ocp_if *omap44xx_iva_slaves[] = { static struct omap_hwmod omap44xx_iva_seq0_hwmod = { .name = "iva_seq0", .class = &omap44xx_iva_hwmod_class, + .clkdm_name = "ivahd_clkdm", .flags = HWMOD_INIT_NO_RESET, .rst_lines = omap44xx_iva_seq0_resets, .rst_lines_cnt = ARRAY_SIZE(omap44xx_iva_seq0_resets), .prcm = { .omap4 = { - .rstctrl_reg = OMAP4430_RM_IVAHD_RSTCTRL, + .rstctrl_offs = OMAP4_RM_IVAHD_RSTCTRL_OFFSET, }, }, .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -2646,12 +2788,13 @@ static struct omap_hwmod omap44xx_iva_seq0_hwmod = { static struct omap_hwmod omap44xx_iva_seq1_hwmod = { .name = "iva_seq1", .class = &omap44xx_iva_hwmod_class, + .clkdm_name = "ivahd_clkdm", .flags = HWMOD_INIT_NO_RESET, .rst_lines = omap44xx_iva_seq1_resets, .rst_lines_cnt = ARRAY_SIZE(omap44xx_iva_seq1_resets), .prcm = { .omap4 = { - .rstctrl_reg = OMAP4430_RM_IVAHD_RSTCTRL, + .rstctrl_offs = OMAP4_RM_IVAHD_RSTCTRL_OFFSET, }, }, .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP4430), @@ -2660,14 +2803,17 @@ static struct omap_hwmod omap44xx_iva_seq1_hwmod = { static struct omap_hwmod omap44xx_iva_hwmod = { .name = "iva", .class = &omap44xx_iva_hwmod_class, + .clkdm_name = "ivahd_clkdm", .mpu_irqs = omap44xx_iva_irqs, .rst_lines = omap44xx_iva_resets, .rst_lines_cnt = ARRAY_SIZE(omap44xx_iva_resets), .main_clk = "iva_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_IVAHD_IVAHD_CLKCTRL, - .rstctrl_reg = OMAP4430_RM_IVAHD_RSTCTRL, + .clkctrl_offs = OMAP4_CM_IVAHD_IVAHD_CLKCTRL_OFFSET, + .rstctrl_offs = OMAP4_RM_IVAHD_RSTCTRL_OFFSET, + .context_offs = OMAP4_RM_IVAHD_IVAHD_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .slaves = omap44xx_iva_slaves, @@ -2732,11 +2878,14 @@ static struct omap_hwmod_ocp_if *omap44xx_kbd_slaves[] = { static struct omap_hwmod omap44xx_kbd_hwmod = { .name = "kbd", .class = &omap44xx_kbd_hwmod_class, + .clkdm_name = "l4_wkup_clkdm", .mpu_irqs = omap44xx_kbd_irqs, .main_clk = "kbd_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_WKUP_KEYBOARD_CLKCTRL, + .clkctrl_offs = OMAP4_CM_WKUP_KEYBOARD_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_WKUP_KEYBOARD_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_kbd_slaves, @@ -2797,10 +2946,12 @@ static struct omap_hwmod_ocp_if *omap44xx_mailbox_slaves[] = { static struct omap_hwmod omap44xx_mailbox_hwmod = { .name = "mailbox", .class = &omap44xx_mailbox_hwmod_class, + .clkdm_name = "l4_cfg_clkdm", .mpu_irqs = omap44xx_mailbox_irqs, .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4CFG_MAILBOX_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4CFG_MAILBOX_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4CFG_MAILBOX_CONTEXT_OFFSET, }, }, .slaves = omap44xx_mailbox_slaves, @@ -2887,12 +3038,15 @@ static struct omap_hwmod_ocp_if *omap44xx_mcbsp1_slaves[] = { static struct omap_hwmod omap44xx_mcbsp1_hwmod = { .name = "mcbsp1", .class = &omap44xx_mcbsp_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_mcbsp1_irqs, .sdma_reqs = omap44xx_mcbsp1_sdma_reqs, .main_clk = "mcbsp1_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_MCBSP1_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_MCBSP1_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_MCBSP1_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_mcbsp1_slaves, @@ -2960,12 +3114,15 @@ static struct omap_hwmod_ocp_if *omap44xx_mcbsp2_slaves[] = { static struct omap_hwmod omap44xx_mcbsp2_hwmod = { .name = "mcbsp2", .class = &omap44xx_mcbsp_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_mcbsp2_irqs, .sdma_reqs = omap44xx_mcbsp2_sdma_reqs, .main_clk = "mcbsp2_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_MCBSP2_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_MCBSP2_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_MCBSP2_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_mcbsp2_slaves, @@ -3033,12 +3190,15 @@ static struct omap_hwmod_ocp_if *omap44xx_mcbsp3_slaves[] = { static struct omap_hwmod omap44xx_mcbsp3_hwmod = { .name = "mcbsp3", .class = &omap44xx_mcbsp_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_mcbsp3_irqs, .sdma_reqs = omap44xx_mcbsp3_sdma_reqs, .main_clk = "mcbsp3_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_MCBSP3_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_MCBSP3_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_MCBSP3_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_mcbsp3_slaves, @@ -3085,12 +3245,15 @@ static struct omap_hwmod_ocp_if *omap44xx_mcbsp4_slaves[] = { static struct omap_hwmod omap44xx_mcbsp4_hwmod = { .name = "mcbsp4", .class = &omap44xx_mcbsp_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_mcbsp4_irqs, .sdma_reqs = omap44xx_mcbsp4_sdma_reqs, .main_clk = "mcbsp4_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_MCBSP4_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_MCBSP4_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_MCBSP4_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_mcbsp4_slaves, @@ -3177,12 +3340,15 @@ static struct omap_hwmod_ocp_if *omap44xx_mcpdm_slaves[] = { static struct omap_hwmod omap44xx_mcpdm_hwmod = { .name = "mcpdm", .class = &omap44xx_mcpdm_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_mcpdm_irqs, .sdma_reqs = omap44xx_mcpdm_sdma_reqs, .main_clk = "mcpdm_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_PDM_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_PDM_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_PDM_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_mcpdm_slaves, @@ -3262,12 +3428,15 @@ static struct omap2_mcspi_dev_attr mcspi1_dev_attr = { static struct omap_hwmod omap44xx_mcspi1_hwmod = { .name = "mcspi1", .class = &omap44xx_mcspi_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_mcspi1_irqs, .sdma_reqs = omap44xx_mcspi1_sdma_reqs, .main_clk = "mcspi1_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_MCSPI1_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_MCSPI1_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_MCSPI1_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .dev_attr = &mcspi1_dev_attr, @@ -3322,12 +3491,15 @@ static struct omap2_mcspi_dev_attr mcspi2_dev_attr = { static struct omap_hwmod omap44xx_mcspi2_hwmod = { .name = "mcspi2", .class = &omap44xx_mcspi_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_mcspi2_irqs, .sdma_reqs = omap44xx_mcspi2_sdma_reqs, .main_clk = "mcspi2_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_MCSPI2_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_MCSPI2_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_MCSPI2_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .dev_attr = &mcspi2_dev_attr, @@ -3382,12 +3554,15 @@ static struct omap2_mcspi_dev_attr mcspi3_dev_attr = { static struct omap_hwmod omap44xx_mcspi3_hwmod = { .name = "mcspi3", .class = &omap44xx_mcspi_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_mcspi3_irqs, .sdma_reqs = omap44xx_mcspi3_sdma_reqs, .main_clk = "mcspi3_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_MCSPI3_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_MCSPI3_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_MCSPI3_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .dev_attr = &mcspi3_dev_attr, @@ -3440,12 +3615,15 @@ static struct omap2_mcspi_dev_attr mcspi4_dev_attr = { static struct omap_hwmod omap44xx_mcspi4_hwmod = { .name = "mcspi4", .class = &omap44xx_mcspi_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_mcspi4_irqs, .sdma_reqs = omap44xx_mcspi4_sdma_reqs, .main_clk = "mcspi4_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_MCSPI4_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_MCSPI4_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_MCSPI4_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .dev_attr = &mcspi4_dev_attr, @@ -3524,12 +3702,15 @@ static struct omap_mmc_dev_attr mmc1_dev_attr = { static struct omap_hwmod omap44xx_mmc1_hwmod = { .name = "mmc1", .class = &omap44xx_mmc_hwmod_class, + .clkdm_name = "l3_init_clkdm", .mpu_irqs = omap44xx_mmc1_irqs, .sdma_reqs = omap44xx_mmc1_sdma_reqs, .main_clk = "mmc1_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L3INIT_MMC1_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L3INIT_MMC1_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L3INIT_MMC1_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .dev_attr = &mmc1_dev_attr, @@ -3583,12 +3764,15 @@ static struct omap_hwmod_ocp_if *omap44xx_mmc2_slaves[] = { static struct omap_hwmod omap44xx_mmc2_hwmod = { .name = "mmc2", .class = &omap44xx_mmc_hwmod_class, + .clkdm_name = "l3_init_clkdm", .mpu_irqs = omap44xx_mmc2_irqs, .sdma_reqs = omap44xx_mmc2_sdma_reqs, .main_clk = "mmc2_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L3INIT_MMC2_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L3INIT_MMC2_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L3INIT_MMC2_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_mmc2_slaves, @@ -3637,12 +3821,15 @@ static struct omap_hwmod_ocp_if *omap44xx_mmc3_slaves[] = { static struct omap_hwmod omap44xx_mmc3_hwmod = { .name = "mmc3", .class = &omap44xx_mmc_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_mmc3_irqs, .sdma_reqs = omap44xx_mmc3_sdma_reqs, .main_clk = "mmc3_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_MMCSD3_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_MMCSD3_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_MMCSD3_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_mmc3_slaves, @@ -3689,13 +3876,16 @@ static struct omap_hwmod_ocp_if *omap44xx_mmc4_slaves[] = { static struct omap_hwmod omap44xx_mmc4_hwmod = { .name = "mmc4", .class = &omap44xx_mmc_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_mmc4_irqs, .sdma_reqs = omap44xx_mmc4_sdma_reqs, .main_clk = "mmc4_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_MMCSD4_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_MMCSD4_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_MMCSD4_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_mmc4_slaves, @@ -3742,12 +3932,15 @@ static struct omap_hwmod_ocp_if *omap44xx_mmc5_slaves[] = { static struct omap_hwmod omap44xx_mmc5_hwmod = { .name = "mmc5", .class = &omap44xx_mmc_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_mmc5_irqs, .sdma_reqs = omap44xx_mmc5_sdma_reqs, .main_clk = "mmc5_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_MMCSD5_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_MMCSD5_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_MMCSD5_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_mmc5_slaves, @@ -3782,12 +3975,14 @@ static struct omap_hwmod_ocp_if *omap44xx_mpu_masters[] = { static struct omap_hwmod omap44xx_mpu_hwmod = { .name = "mpu", .class = &omap44xx_mpu_hwmod_class, + .clkdm_name = "mpuss_clkdm", .flags = HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET, .mpu_irqs = omap44xx_mpu_irqs, .main_clk = "dpll_mpu_m2_ck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_MPU_MPU_CLKCTRL, + .clkctrl_offs = OMAP4_CM_MPU_MPU_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_MPU_MPU_CONTEXT_OFFSET, }, }, .masters = omap44xx_mpu_masters, @@ -3854,13 +4049,16 @@ static struct omap_hwmod_ocp_if *omap44xx_smartreflex_core_slaves[] = { static struct omap_hwmod omap44xx_smartreflex_core_hwmod = { .name = "smartreflex_core", .class = &omap44xx_smartreflex_hwmod_class, + .clkdm_name = "l4_ao_clkdm", .mpu_irqs = omap44xx_smartreflex_core_irqs, .main_clk = "smartreflex_core_fck", .vdd_name = "core", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_ALWON_SR_CORE_CLKCTRL, + .clkctrl_offs = OMAP4_CM_ALWON_SR_CORE_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ALWON_SR_CORE_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_smartreflex_core_slaves, @@ -3901,12 +4099,15 @@ static struct omap_hwmod_ocp_if *omap44xx_smartreflex_iva_slaves[] = { static struct omap_hwmod omap44xx_smartreflex_iva_hwmod = { .name = "smartreflex_iva", .class = &omap44xx_smartreflex_hwmod_class, + .clkdm_name = "l4_ao_clkdm", .mpu_irqs = omap44xx_smartreflex_iva_irqs, .main_clk = "smartreflex_iva_fck", .vdd_name = "iva", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_ALWON_SR_IVA_CLKCTRL, + .clkctrl_offs = OMAP4_CM_ALWON_SR_IVA_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ALWON_SR_IVA_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_smartreflex_iva_slaves, @@ -3947,12 +4148,15 @@ static struct omap_hwmod_ocp_if *omap44xx_smartreflex_mpu_slaves[] = { static struct omap_hwmod omap44xx_smartreflex_mpu_hwmod = { .name = "smartreflex_mpu", .class = &omap44xx_smartreflex_hwmod_class, + .clkdm_name = "l4_ao_clkdm", .mpu_irqs = omap44xx_smartreflex_mpu_irqs, .main_clk = "smartreflex_mpu_fck", .vdd_name = "mpu", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_ALWON_SR_MPU_CLKCTRL, + .clkctrl_offs = OMAP4_CM_ALWON_SR_MPU_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ALWON_SR_MPU_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_smartreflex_mpu_slaves, @@ -4011,9 +4215,11 @@ static struct omap_hwmod_ocp_if *omap44xx_spinlock_slaves[] = { static struct omap_hwmod omap44xx_spinlock_hwmod = { .name = "spinlock", .class = &omap44xx_spinlock_hwmod_class, + .clkdm_name = "l4_cfg_clkdm", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4CFG_HW_SEM_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4CFG_HW_SEM_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4CFG_HW_SEM_CONTEXT_OFFSET, }, }, .slaves = omap44xx_spinlock_slaves, @@ -4092,11 +4298,14 @@ static struct omap_hwmod_ocp_if *omap44xx_timer1_slaves[] = { static struct omap_hwmod omap44xx_timer1_hwmod = { .name = "timer1", .class = &omap44xx_timer_1ms_hwmod_class, + .clkdm_name = "l4_wkup_clkdm", .mpu_irqs = omap44xx_timer1_irqs, .main_clk = "timer1_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_WKUP_TIMER1_CLKCTRL, + .clkctrl_offs = OMAP4_CM_WKUP_TIMER1_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_WKUP_TIMER1_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer1_slaves, @@ -4137,11 +4346,14 @@ static struct omap_hwmod_ocp_if *omap44xx_timer2_slaves[] = { static struct omap_hwmod omap44xx_timer2_hwmod = { .name = "timer2", .class = &omap44xx_timer_1ms_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_timer2_irqs, .main_clk = "timer2_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER2_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_DMTIMER2_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_DMTIMER2_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer2_slaves, @@ -4182,11 +4394,14 @@ static struct omap_hwmod_ocp_if *omap44xx_timer3_slaves[] = { static struct omap_hwmod omap44xx_timer3_hwmod = { .name = "timer3", .class = &omap44xx_timer_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_timer3_irqs, .main_clk = "timer3_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER3_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_DMTIMER3_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_DMTIMER3_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer3_slaves, @@ -4227,11 +4442,14 @@ static struct omap_hwmod_ocp_if *omap44xx_timer4_slaves[] = { static struct omap_hwmod omap44xx_timer4_hwmod = { .name = "timer4", .class = &omap44xx_timer_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_timer4_irqs, .main_clk = "timer4_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER4_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_DMTIMER4_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_DMTIMER4_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer4_slaves, @@ -4291,11 +4509,14 @@ static struct omap_hwmod_ocp_if *omap44xx_timer5_slaves[] = { static struct omap_hwmod omap44xx_timer5_hwmod = { .name = "timer5", .class = &omap44xx_timer_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_timer5_irqs, .main_clk = "timer5_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_TIMER5_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_TIMER5_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_TIMER5_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer5_slaves, @@ -4355,12 +4576,15 @@ static struct omap_hwmod_ocp_if *omap44xx_timer6_slaves[] = { static struct omap_hwmod omap44xx_timer6_hwmod = { .name = "timer6", .class = &omap44xx_timer_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_timer6_irqs, .main_clk = "timer6_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_TIMER6_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_TIMER6_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_TIMER6_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer6_slaves, @@ -4420,11 +4644,14 @@ static struct omap_hwmod_ocp_if *omap44xx_timer7_slaves[] = { static struct omap_hwmod omap44xx_timer7_hwmod = { .name = "timer7", .class = &omap44xx_timer_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_timer7_irqs, .main_clk = "timer7_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_TIMER7_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_TIMER7_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_TIMER7_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer7_slaves, @@ -4484,11 +4711,14 @@ static struct omap_hwmod_ocp_if *omap44xx_timer8_slaves[] = { static struct omap_hwmod omap44xx_timer8_hwmod = { .name = "timer8", .class = &omap44xx_timer_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_timer8_irqs, .main_clk = "timer8_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_TIMER8_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_TIMER8_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_TIMER8_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer8_slaves, @@ -4529,11 +4759,14 @@ static struct omap_hwmod_ocp_if *omap44xx_timer9_slaves[] = { static struct omap_hwmod omap44xx_timer9_hwmod = { .name = "timer9", .class = &omap44xx_timer_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_timer9_irqs, .main_clk = "timer9_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER9_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_DMTIMER9_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_DMTIMER9_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer9_slaves, @@ -4574,11 +4807,14 @@ static struct omap_hwmod_ocp_if *omap44xx_timer10_slaves[] = { static struct omap_hwmod omap44xx_timer10_hwmod = { .name = "timer10", .class = &omap44xx_timer_1ms_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_timer10_irqs, .main_clk = "timer10_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER10_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_DMTIMER10_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_DMTIMER10_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer10_slaves, @@ -4619,11 +4855,14 @@ static struct omap_hwmod_ocp_if *omap44xx_timer11_slaves[] = { static struct omap_hwmod omap44xx_timer11_hwmod = { .name = "timer11", .class = &omap44xx_timer_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_timer11_irqs, .main_clk = "timer11_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_DMTIMER11_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_DMTIMER11_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_DMTIMER11_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_timer11_slaves, @@ -4692,12 +4931,15 @@ static struct omap_hwmod_ocp_if *omap44xx_uart1_slaves[] = { static struct omap_hwmod omap44xx_uart1_hwmod = { .name = "uart1", .class = &omap44xx_uart_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_uart1_irqs, .sdma_reqs = omap44xx_uart1_sdma_reqs, .main_clk = "uart1_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_UART1_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_UART1_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_UART1_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_uart1_slaves, @@ -4744,12 +4986,15 @@ static struct omap_hwmod_ocp_if *omap44xx_uart2_slaves[] = { static struct omap_hwmod omap44xx_uart2_hwmod = { .name = "uart2", .class = &omap44xx_uart_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_uart2_irqs, .sdma_reqs = omap44xx_uart2_sdma_reqs, .main_clk = "uart2_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_UART2_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_UART2_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_UART2_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_uart2_slaves, @@ -4796,13 +5041,16 @@ static struct omap_hwmod_ocp_if *omap44xx_uart3_slaves[] = { static struct omap_hwmod omap44xx_uart3_hwmod = { .name = "uart3", .class = &omap44xx_uart_hwmod_class, + .clkdm_name = "l4_per_clkdm", .flags = HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET, .mpu_irqs = omap44xx_uart3_irqs, .sdma_reqs = omap44xx_uart3_sdma_reqs, .main_clk = "uart3_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_UART3_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_UART3_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_UART3_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_uart3_slaves, @@ -4849,12 +5097,15 @@ static struct omap_hwmod_ocp_if *omap44xx_uart4_slaves[] = { static struct omap_hwmod omap44xx_uart4_hwmod = { .name = "uart4", .class = &omap44xx_uart_hwmod_class, + .clkdm_name = "l4_per_clkdm", .mpu_irqs = omap44xx_uart4_irqs, .sdma_reqs = omap44xx_uart4_sdma_reqs, .main_clk = "uart4_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L4PER_UART4_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L4PER_UART4_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L4PER_UART4_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_uart4_slaves, @@ -4927,12 +5178,15 @@ static struct omap_hwmod_opt_clk usb_otg_hs_opt_clks[] = { static struct omap_hwmod omap44xx_usb_otg_hs_hwmod = { .name = "usb_otg_hs", .class = &omap44xx_usb_otg_hs_hwmod_class, + .clkdm_name = "l3_init_clkdm", .flags = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY, .mpu_irqs = omap44xx_usb_otg_hs_irqs, .main_clk = "usb_otg_hs_ick", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_L3INIT_USB_OTG_CLKCTRL, + .clkctrl_offs = OMAP4_CM_L3INIT_USB_OTG_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_L3INIT_USB_OTG_CONTEXT_OFFSET, + .modulemode = MODULEMODE_HWCTRL, }, }, .opt_clks = usb_otg_hs_opt_clks, @@ -5000,11 +5254,14 @@ static struct omap_hwmod_ocp_if *omap44xx_wd_timer2_slaves[] = { static struct omap_hwmod omap44xx_wd_timer2_hwmod = { .name = "wd_timer2", .class = &omap44xx_wd_timer_hwmod_class, + .clkdm_name = "l4_wkup_clkdm", .mpu_irqs = omap44xx_wd_timer2_irqs, .main_clk = "wd_timer2_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM_WKUP_WDT2_CLKCTRL, + .clkctrl_offs = OMAP4_CM_WKUP_WDT2_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_WKUP_WDT2_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_wd_timer2_slaves, @@ -5064,11 +5321,14 @@ static struct omap_hwmod_ocp_if *omap44xx_wd_timer3_slaves[] = { static struct omap_hwmod omap44xx_wd_timer3_hwmod = { .name = "wd_timer3", .class = &omap44xx_wd_timer_hwmod_class, + .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_wd_timer3_irqs, .main_clk = "wd_timer3_fck", .prcm = { .omap4 = { - .clkctrl_reg = OMAP4430_CM1_ABE_WDT3_CLKCTRL, + .clkctrl_offs = OMAP4_CM1_ABE_WDT3_CLKCTRL_OFFSET, + .context_offs = OMAP4_RM_ABE_WDT3_CONTEXT_OFFSET, + .modulemode = MODULEMODE_SWCTRL, }, }, .slaves = omap44xx_wd_timer3_slaves, diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index d48813fd62de..3feb35911a32 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -108,6 +108,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state) u32 cur_state; int sleep_switch = -1; int ret = 0; + int hwsup = 0; if (pwrdm == NULL || IS_ERR(pwrdm)) return -EINVAL; @@ -127,6 +128,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state) (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) { sleep_switch = LOWPOWERSTATE_SWITCH; } else { + hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]); clkdm_wakeup(pwrdm->pwrdm_clkdms[0]); pwrdm_wait_transition(pwrdm); sleep_switch = FORCEWAKEUP_SWITCH; @@ -142,7 +144,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state) switch (sleep_switch) { case FORCEWAKEUP_SWITCH: - if (pwrdm->pwrdm_clkdms[0]->flags & CLKDM_CAN_ENABLE_AUTO) + if (hwsup) clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]); else clkdm_sleep(pwrdm->pwrdm_clkdms[0]); diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c index 6be14389e4f3..2e40a5cf0163 100644 --- a/arch/arm/mach-omap2/prcm.c +++ b/arch/arm/mach-omap2/prcm.c @@ -70,7 +70,7 @@ static void omap_prcm_arch_reset(char mode, const char *cmd) prcm_offs = OMAP3430_GR_MOD; omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0)); } else if (cpu_is_omap44xx()) { - omap4_prm_global_warm_sw_reset(); /* never returns */ + omap4_prminst_global_warm_sw_reset(); /* never returns */ } else { WARN_ON(1); } diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index a2a04bfa9628..00165558fc4d 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c @@ -1,7 +1,7 @@ /* * OMAP4 PRM module functions * - * Copyright (C) 2010 Texas Instruments, Inc. + * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2010 Nokia Corporation * Benoît Cousson * Paul Walmsley @@ -24,12 +24,6 @@ #include "prm44xx.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 */ /* 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) 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); -} diff --git a/arch/arm/mach-omap2/prm44xx.h b/arch/arm/mach-omap2/prm44xx.h index 6e53120fd6cb..7dfa379b625d 100644 --- a/arch/arm/mach-omap2/prm44xx.h +++ b/arch/arm/mach-omap2/prm44xx.h @@ -750,16 +750,6 @@ extern u32 omap4_prm_read_inst_reg(s16 inst, u16 idx); extern void omap4_prm_write_inst_reg(u32 val, s16 inst, u16 idx); extern u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx); -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 diff --git a/arch/arm/mach-omap2/prminst44xx.c b/arch/arm/mach-omap2/prminst44xx.c index a30324297278..3a7bab16edd5 100644 --- a/arch/arm/mach-omap2/prminst44xx.c +++ b/arch/arm/mach-omap2/prminst44xx.c @@ -2,6 +2,7 @@ * OMAP4 PRM instance functions * * Copyright (C) 2009 Nokia Corporation + * Copyright (C) 2011 Texas Instruments, Inc. * Paul Walmsley * * 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) /* 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, - s16 idx) + u16 idx) { u32 v; @@ -64,3 +65,112 @@ u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst, 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); +} diff --git a/arch/arm/mach-omap2/prminst44xx.h b/arch/arm/mach-omap2/prminst44xx.h index 02dd66ddda8b..46f2efb36596 100644 --- a/arch/arm/mach-omap2/prminst44xx.h +++ b/arch/arm/mach-omap2/prminst44xx.h @@ -2,6 +2,7 @@ * OMAP4 Power/Reset Management (PRM) function prototypes * * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2011 Texas Instruments, Inc. * Paul Walmsley * * This program is free software; you can redistribute it and/or modify @@ -18,8 +19,15 @@ 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 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 diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c index 3aaa46f6cd12..2543342dbccb 100644 --- a/arch/arm/mach-omap2/twl-common.c +++ b/arch/arm/mach-omap2/twl-common.c @@ -80,11 +80,11 @@ static struct twl4030_madc_platform_data omap3_madc_pdata = { .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 = &omap3_audio, + .codec = &omap3_codec, }; 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, if (pdata_flags & TWL_COMMON_PDATA_MADC && !pmic_data->madc) pmic_data->madc = &omap3_madc_pdata; - if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->codec) - pmic_data->codec = &omap3_codec_pdata; + if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->audio) + pmic_data->audio = &omap3_audio_pdata; /* Common regulator configurations */ if (regulators_flags & TWL_COMMON_REGULATOR_VDAC && !pmic_data->vdac) diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c index 964704f40bbe..3ba4d11ca73e 100644 --- a/arch/arm/plat-omap/clock.c +++ b/arch/arm/plat-omap/clock.c @@ -475,8 +475,41 @@ int __init clk_init(struct clk_functions * custom_clocks) /* * 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 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) { int err; @@ -545,6 +578,12 @@ static int __init clk_debugfs_init(void) if (err) goto err_out; } + + d = debugfs_create_file("summary", S_IRUGO, + d, NULL, &debug_clock_fops); + if (!d) + return -ENOMEM; + return 0; err_out: debugfs_remove_recursive(clk_debugfs_root); diff --git a/arch/arm/plat-omap/include/plat/irqs.h b/arch/arm/plat-omap/include/plat/irqs.h index c88432005665..926d25c780f3 100644 --- a/arch/arm/plat-omap/include/plat/irqs.h +++ b/arch/arm/plat-omap/include/plat/irqs.h @@ -407,11 +407,19 @@ #endif #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 */ -#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 #else -#define TWL_IRQ_END TWL6030_IRQ_END +#define TWL_IRQ_END TWL6040_CODEC_IRQ_END #endif /* GPMC related */ diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h index fafdfe3c8d4e..0e329ca88a70 100644 --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h @@ -2,6 +2,7 @@ * omap_hwmod macros, structures * * Copyright (C) 2009-2011 Nokia Corporation + * Copyright (C) 2011 Texas Instruments, Inc. * Paul Walmsley * * Created in collaboration with (alphabetical order): Benoît Cousson, @@ -79,6 +80,11 @@ extern struct omap_hwmod_sysc_fields omap_hwmod_sysc_type2; #define HWMOD_IDLEMODE_SMART (1 << 2) #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 * @pads: array of omap_device_pad entries @@ -360,9 +366,11 @@ struct omap_hwmod_omap2_prcm { * @submodule_wkdep_bit: bit shift of the WKDEP range */ struct omap_hwmod_omap4_prcm { - void __iomem *clkctrl_reg; - void __iomem *rstctrl_reg; + u16 clkctrl_offs; + u16 rstctrl_offs; + u16 context_offs; u8 submodule_wkdep_bit; + u8 modulemode; }; @@ -515,6 +523,8 @@ struct omap_hwmod { const char *main_clk; struct clk *_clk; struct omap_hwmod_opt_clk *opt_clks; + char *clkdm_name; + struct clockdomain *clkdm; char *vdd_name; struct voltagedomain *voltdm; struct omap_hwmod_ocp_if **masters; /* connect to *_IA */ diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c index 3471c650743b..b6b409744954 100644 --- a/arch/arm/plat-omap/omap_device.c +++ b/arch/arm/plat-omap/omap_device.c @@ -236,56 +236,71 @@ static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat) 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 + * @oh: struct omap_hwmod *oh * - * For every optional clock present per hwmod per omap_device, this function - * adds an entry in the clkdev table of the form <dev-id=dev_name, con-id=role> - * if it does not exist already. + * For the main clock and every optional clock present per hwmod per + * omap_device, this function adds an entry in the clkdev table of the + * 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 * omap_device_register. * * This allows drivers to get a pointer to its optional clocks based on its role * by calling clk_get(<dev*>, <role>). + * In the case of the main clock, a "fck" alias is used. * * No return value. */ -static void _add_optional_clock_clkdev(struct omap_device *od, - struct omap_hwmod *oh) +static void _add_hwmod_clocks_clkdev(struct omap_device *od, + struct omap_hwmod *oh) { int i; - for (i = 0; i < oh->opt_clks_cnt; i++) { - 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); - if (!IS_ERR(r)) - continue; /* clkdev entry exists */ + _add_clkdev(od, "fck", oh->main_clk); - 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); - } + for (i = 0; i < oh->opt_clks_cnt; i++) + _add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk); } @@ -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++) { hwmods[i]->od = od; - _add_optional_clock_clkdev(od, hwmods[i]); + _add_hwmod_clocks_clkdev(od, hwmods[i]); } if (ret) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 45dc6aa62ba4..d1bf8724b58f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -267,7 +267,7 @@ config INPUT_TWL4030_PWRBUTTON config INPUT_TWL4030_VIBRA tristate "Support for TWL4030 Vibrator" depends on TWL4030_CORE - select TWL4030_CODEC + select MFD_TWL4030_AUDIO select INPUT_FF_MEMLESS help This option enables support for TWL4030 Vibrator Driver. @@ -275,6 +275,17 @@ config INPUT_TWL4030_VIBRA To compile this driver as a module, choose M here. The module will 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 tristate "User level driver support" help diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 38efb2cb182b..4da7c3a60e04 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.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_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 014dd4ad0d4f..3c1a432c14dc 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -28,7 +28,7 @@ #include <linux/platform_device.h> #include <linux/workqueue.h> #include <linux/i2c/twl.h> -#include <linux/mfd/twl4030-codec.h> +#include <linux/mfd/twl4030-audio.h> #include <linux/input.h> #include <linux/slab.h> @@ -67,7 +67,7 @@ static void vibra_enable(struct vibra_info *info) { u8 reg; - twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); + twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); /* turn H-Bridge on */ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, @@ -75,7 +75,7 @@ static void vibra_enable(struct vibra_info *info) twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, (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; } @@ -90,8 +90,8 @@ static void vibra_disable(struct vibra_info *info) twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); - twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); - twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); + twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL); + twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); info->enabled = false; } @@ -196,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, 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; int ret; diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c new file mode 100644 index 000000000000..c43002e7ec72 --- /dev/null +++ b/drivers/input/misc/twl6040-vibra.c @@ -0,0 +1,423 @@ +/* + * 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>"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6ca938a6bf94..37b83eb6d703 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -218,7 +218,7 @@ config TWL4030_POWER and load scripts controlling which resources are switched off/on or reset when a sleep, wakeup or warm reset event occurs. -config TWL4030_CODEC +config MFD_TWL4030_AUDIO bool depends on TWL4030_CORE select MFD_CORE @@ -233,6 +233,12 @@ config TWL6030_PWM Say yes here if you want support for TWL6030 PWM. 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 bool "Support STMicroelectronics STMPE" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d7d47d2a4c76..22a280fcb705 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -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_MADC) += twl4030-madc.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_TWL6040_CORE) += twl6040-core.o twl6040-irq.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index b8f2a4e7f6e7..a2eddc70995c 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -110,7 +110,7 @@ #endif #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 #else #define twl_has_codec() false @@ -815,20 +815,19 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) 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; child = add_child(sub_chip_id, "twl4030-audio", - pdata->codec, sizeof(*pdata->codec), + pdata->audio, sizeof(*pdata->audio), false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } - /* Phoenix codec driver is probed directly atm */ - if (twl_has_codec() && pdata->codec && twl_class_is_6030()) { + if (twl_has_codec() && pdata->audio && twl_class_is_6030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl6040-codec", - pdata->codec, sizeof(*pdata->codec), + child = add_child(sub_chip_id, "twl6040", + pdata->audio, sizeof(*pdata->audio), false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c new file mode 100644 index 000000000000..ae51ab5d0e5d --- /dev/null +++ b/drivers/mfd/twl4030-audio.c @@ -0,0 +1,277 @@ +/* + * MFD driver for twl4030 audio submodule, which contains an audio codec, and + * the vibra control. + * + * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> + * + * Copyright: (C) 2009 Nokia Corporation + * + * 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/fs.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl.h> +#include <linux/mfd/core.h> +#include <linux/mfd/twl4030-audio.h> + +#define TWL4030_AUDIO_CELLS 2 + +static struct platform_device *twl4030_audio_dev; + +struct twl4030_audio_resource { + int request_count; + u8 reg; + u8 mask; +}; + +struct twl4030_audio { + unsigned int audio_mclk; + struct mutex mutex; + struct twl4030_audio_resource resource[TWL4030_AUDIO_RES_MAX]; + struct mfd_cell cells[TWL4030_AUDIO_CELLS]; +}; + +/* + * Modify the resource, the function returns the content of the register + * after the modification. + */ +static int twl4030_audio_set_resource(enum twl4030_audio_res id, int enable) +{ + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); + u8 val; + + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, + audio->resource[id].reg); + + if (enable) + val |= audio->resource[id].mask; + else + val &= ~audio->resource[id].mask; + + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + val, audio->resource[id].reg); + + return val; +} + +static inline int twl4030_audio_get_resource(enum twl4030_audio_res id) +{ + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); + u8 val; + + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, + audio->resource[id].reg); + + return val; +} + +/* + * Enable the resource. + * The function returns with error or the content of the register + */ +int twl4030_audio_enable_resource(enum twl4030_audio_res id) +{ + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); + int val; + + if (id >= TWL4030_AUDIO_RES_MAX) { + dev_err(&twl4030_audio_dev->dev, + "Invalid resource ID (%u)\n", id); + return -EINVAL; + } + + mutex_lock(&audio->mutex); + if (!audio->resource[id].request_count) + /* Resource was disabled, enable it */ + val = twl4030_audio_set_resource(id, 1); + else + val = twl4030_audio_get_resource(id); + + audio->resource[id].request_count++; + mutex_unlock(&audio->mutex); + + return val; +} +EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource); + +/* + * Disable the resource. + * The function returns with error or the content of the register + */ +int twl4030_audio_disable_resource(unsigned id) +{ + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); + int val; + + if (id >= TWL4030_AUDIO_RES_MAX) { + dev_err(&twl4030_audio_dev->dev, + "Invalid resource ID (%u)\n", id); + return -EINVAL; + } + + mutex_lock(&audio->mutex); + if (!audio->resource[id].request_count) { + dev_err(&twl4030_audio_dev->dev, + "Resource has been disabled already (%u)\n", id); + mutex_unlock(&audio->mutex); + return -EPERM; + } + audio->resource[id].request_count--; + + if (!audio->resource[id].request_count) + /* Resource can be disabled now */ + val = twl4030_audio_set_resource(id, 0); + else + val = twl4030_audio_get_resource(id); + + mutex_unlock(&audio->mutex); + + return val; +} +EXPORT_SYMBOL_GPL(twl4030_audio_disable_resource); + +unsigned int twl4030_audio_get_mclk(void) +{ + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); + + return audio->audio_mclk; +} +EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk); + +static int __devinit twl4030_audio_probe(struct platform_device *pdev) +{ + struct twl4030_audio *audio; + struct twl4030_audio_data *pdata = pdev->dev.platform_data; + struct mfd_cell *cell = NULL; + int ret, childs = 0; + u8 val; + + if (!pdata) { + dev_err(&pdev->dev, "Platform data is missing\n"); + return -EINVAL; + } + + /* Configure APLL_INFREQ and disable APLL if enabled */ + val = 0; + switch (pdata->audio_mclk) { + case 19200000: + val |= TWL4030_APLL_INFREQ_19200KHZ; + break; + case 26000000: + val |= TWL4030_APLL_INFREQ_26000KHZ; + break; + case 38400000: + val |= TWL4030_APLL_INFREQ_38400KHZ; + break; + default: + dev_err(&pdev->dev, "Invalid audio_mclk\n"); + return -EINVAL; + } + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + val, TWL4030_REG_APLL_CTL); + + audio = kzalloc(sizeof(struct twl4030_audio), GFP_KERNEL); + if (!audio) + return -ENOMEM; + + platform_set_drvdata(pdev, audio); + + twl4030_audio_dev = pdev; + mutex_init(&audio->mutex); + audio->audio_mclk = pdata->audio_mclk; + + /* Codec power */ + audio->resource[TWL4030_AUDIO_RES_POWER].reg = TWL4030_REG_CODEC_MODE; + audio->resource[TWL4030_AUDIO_RES_POWER].mask = TWL4030_CODECPDZ; + + /* PLL */ + audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL; + audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN; + + if (pdata->codec) { + cell = &audio->cells[childs]; + cell->name = "twl4030-codec"; + cell->platform_data = pdata->codec; + cell->pdata_size = sizeof(*pdata->codec); + childs++; + } + if (pdata->vibra) { + cell = &audio->cells[childs]; + cell->name = "twl4030-vibra"; + cell->platform_data = pdata->vibra; + cell->pdata_size = sizeof(*pdata->vibra); + childs++; + } + + if (childs) + ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells, + childs, NULL, 0); + else { + dev_err(&pdev->dev, "No platform data found for childs\n"); + ret = -ENODEV; + } + + if (!ret) + return 0; + + platform_set_drvdata(pdev, NULL); + kfree(audio); + twl4030_audio_dev = NULL; + return ret; +} + +static int __devexit twl4030_audio_remove(struct platform_device *pdev) +{ + struct twl4030_audio *audio = platform_get_drvdata(pdev); + + mfd_remove_devices(&pdev->dev); + platform_set_drvdata(pdev, NULL); + kfree(audio); + twl4030_audio_dev = NULL; + + return 0; +} + +MODULE_ALIAS("platform:twl4030-audio"); + +static struct platform_driver twl4030_audio_driver = { + .probe = twl4030_audio_probe, + .remove = __devexit_p(twl4030_audio_remove), + .driver = { + .owner = THIS_MODULE, + .name = "twl4030-audio", + }, +}; + +static int __devinit twl4030_audio_init(void) +{ + return platform_driver_register(&twl4030_audio_driver); +} +module_init(twl4030_audio_init); + +static void __devexit twl4030_audio_exit(void) +{ + platform_driver_unregister(&twl4030_audio_driver); +} +module_exit(twl4030_audio_exit); + +MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c deleted file mode 100644 index 2bf4136464c1..000000000000 --- a/drivers/mfd/twl4030-codec.c +++ /dev/null @@ -1,277 +0,0 @@ -/* - * MFD driver for twl4030 codec submodule - * - * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> - * - * Copyright: (C) 2009 Nokia Corporation - * - * 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/fs.h> -#include <linux/platform_device.h> -#include <linux/i2c/twl.h> -#include <linux/mfd/core.h> -#include <linux/mfd/twl4030-codec.h> - -#define TWL4030_CODEC_CELLS 2 - -static struct platform_device *twl4030_codec_dev; - -struct twl4030_codec_resource { - int request_count; - u8 reg; - u8 mask; -}; - -struct twl4030_codec { - unsigned int audio_mclk; - struct mutex mutex; - struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX]; - struct mfd_cell cells[TWL4030_CODEC_CELLS]; -}; - -/* - * Modify the resource, the function returns the content of the register - * after the modification. - */ -static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable) -{ - struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); - u8 val; - - twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, - codec->resource[id].reg); - - if (enable) - val |= codec->resource[id].mask; - else - val &= ~codec->resource[id].mask; - - twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, - val, codec->resource[id].reg); - - return val; -} - -static inline int twl4030_codec_get_resource(enum twl4030_codec_res id) -{ - struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); - u8 val; - - twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, - codec->resource[id].reg); - - return val; -} - -/* - * Enable the resource. - * The function returns with error or the content of the register - */ -int twl4030_codec_enable_resource(enum twl4030_codec_res id) -{ - struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); - int val; - - if (id >= TWL4030_CODEC_RES_MAX) { - dev_err(&twl4030_codec_dev->dev, - "Invalid resource ID (%u)\n", id); - return -EINVAL; - } - - mutex_lock(&codec->mutex); - if (!codec->resource[id].request_count) - /* Resource was disabled, enable it */ - val = twl4030_codec_set_resource(id, 1); - else - val = twl4030_codec_get_resource(id); - - codec->resource[id].request_count++; - mutex_unlock(&codec->mutex); - - return val; -} -EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource); - -/* - * Disable the resource. - * The function returns with error or the content of the register - */ -int twl4030_codec_disable_resource(unsigned id) -{ - struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); - int val; - - if (id >= TWL4030_CODEC_RES_MAX) { - dev_err(&twl4030_codec_dev->dev, - "Invalid resource ID (%u)\n", id); - return -EINVAL; - } - - mutex_lock(&codec->mutex); - if (!codec->resource[id].request_count) { - dev_err(&twl4030_codec_dev->dev, - "Resource has been disabled already (%u)\n", id); - mutex_unlock(&codec->mutex); - return -EPERM; - } - codec->resource[id].request_count--; - - if (!codec->resource[id].request_count) - /* Resource can be disabled now */ - val = twl4030_codec_set_resource(id, 0); - else - val = twl4030_codec_get_resource(id); - - mutex_unlock(&codec->mutex); - - return val; -} -EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource); - -unsigned int twl4030_codec_get_mclk(void) -{ - struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); - - return codec->audio_mclk; -} -EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk); - -static int __devinit twl4030_codec_probe(struct platform_device *pdev) -{ - struct twl4030_codec *codec; - struct twl4030_codec_data *pdata = pdev->dev.platform_data; - struct mfd_cell *cell = NULL; - int ret, childs = 0; - u8 val; - - if (!pdata) { - dev_err(&pdev->dev, "Platform data is missing\n"); - return -EINVAL; - } - - /* Configure APLL_INFREQ and disable APLL if enabled */ - val = 0; - switch (pdata->audio_mclk) { - case 19200000: - val |= TWL4030_APLL_INFREQ_19200KHZ; - break; - case 26000000: - val |= TWL4030_APLL_INFREQ_26000KHZ; - break; - case 38400000: - val |= TWL4030_APLL_INFREQ_38400KHZ; - break; - default: - dev_err(&pdev->dev, "Invalid audio_mclk\n"); - return -EINVAL; - } - twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, - val, TWL4030_REG_APLL_CTL); - - codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL); - if (!codec) - return -ENOMEM; - - platform_set_drvdata(pdev, codec); - - twl4030_codec_dev = pdev; - mutex_init(&codec->mutex); - codec->audio_mclk = pdata->audio_mclk; - - /* Codec power */ - codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE; - codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ; - - /* PLL */ - codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL; - codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN; - - if (pdata->audio) { - cell = &codec->cells[childs]; - cell->name = "twl4030-codec"; - cell->platform_data = pdata->audio; - cell->pdata_size = sizeof(*pdata->audio); - childs++; - } - if (pdata->vibra) { - cell = &codec->cells[childs]; - cell->name = "twl4030-vibra"; - cell->platform_data = pdata->vibra; - cell->pdata_size = sizeof(*pdata->vibra); - childs++; - } - - if (childs) - ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells, - childs, NULL, 0); - else { - dev_err(&pdev->dev, "No platform data found for childs\n"); - ret = -ENODEV; - } - - if (!ret) - return 0; - - platform_set_drvdata(pdev, NULL); - kfree(codec); - twl4030_codec_dev = NULL; - return ret; -} - -static int __devexit twl4030_codec_remove(struct platform_device *pdev) -{ - struct twl4030_codec *codec = platform_get_drvdata(pdev); - - mfd_remove_devices(&pdev->dev); - platform_set_drvdata(pdev, NULL); - kfree(codec); - twl4030_codec_dev = NULL; - - return 0; -} - -MODULE_ALIAS("platform:twl4030-audio"); - -static struct platform_driver twl4030_codec_driver = { - .probe = twl4030_codec_probe, - .remove = __devexit_p(twl4030_codec_remove), - .driver = { - .owner = THIS_MODULE, - .name = "twl4030-audio", - }, -}; - -static int __devinit twl4030_codec_init(void) -{ - return platform_driver_register(&twl4030_codec_driver); -} -module_init(twl4030_codec_init); - -static void __devexit twl4030_codec_exit(void) -{ - platform_driver_unregister(&twl4030_codec_driver); -} -module_exit(twl4030_codec_exit); - -MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c new file mode 100644 index 000000000000..24d436c2fe4a --- /dev/null +++ b/drivers/mfd/twl6040-core.c @@ -0,0 +1,620 @@ +/* + * 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"); diff --git a/drivers/mfd/twl6040-irq.c b/drivers/mfd/twl6040-irq.c new file mode 100644 index 000000000000..b3f8ddaa28a8 --- /dev/null +++ b/drivers/mfd/twl6040-irq.c @@ -0,0 +1,191 @@ +/* + * Interrupt controller support for TWL6040 + * + * Author: Misael Lopez Cruz <misael.lopez@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/kernel.h> +#include <linux/module.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/mfd/twl6040.h> + +struct twl6040_irq_data { + int mask; + int status; +}; + +static struct twl6040_irq_data twl6040_irqs[] = { + { + .mask = TWL6040_THMSK, + .status = TWL6040_THINT, + }, + { + .mask = TWL6040_PLUGMSK, + .status = TWL6040_PLUGINT | TWL6040_UNPLUGINT, + }, + { + .mask = TWL6040_HOOKMSK, + .status = TWL6040_HOOKINT, + }, + { + .mask = TWL6040_HFMSK, + .status = TWL6040_HFINT, + }, + { + .mask = TWL6040_VIBMSK, + .status = TWL6040_VIBINT, + }, + { + .mask = TWL6040_READYMSK, + .status = TWL6040_READYINT, + }, +}; + +static inline +struct twl6040_irq_data *irq_to_twl6040_irq(struct twl6040 *twl6040, + int irq) +{ + return &twl6040_irqs[irq - twl6040->irq_base]; +} + +static void twl6040_irq_lock(struct irq_data *data) +{ + struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); + + mutex_lock(&twl6040->irq_mutex); +} + +static void twl6040_irq_sync_unlock(struct irq_data *data) +{ + struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); + + /* write back to hardware any change in irq mask */ + if (twl6040->irq_masks_cur != twl6040->irq_masks_cache) { + twl6040->irq_masks_cache = twl6040->irq_masks_cur; + twl6040_reg_write(twl6040, TWL6040_REG_INTMR, + twl6040->irq_masks_cur); + } + + mutex_unlock(&twl6040->irq_mutex); +} + +static void twl6040_irq_enable(struct irq_data *data) +{ + struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); + struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040, + data->irq); + + twl6040->irq_masks_cur &= ~irq_data->mask; +} + +static void twl6040_irq_disable(struct irq_data *data) +{ + struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); + struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040, + data->irq); + + twl6040->irq_masks_cur |= irq_data->mask; +} + +static struct irq_chip twl6040_irq_chip = { + .name = "twl6040", + .irq_bus_lock = twl6040_irq_lock, + .irq_bus_sync_unlock = twl6040_irq_sync_unlock, + .irq_enable = twl6040_irq_enable, + .irq_disable = twl6040_irq_disable, +}; + +static irqreturn_t twl6040_irq_thread(int irq, void *data) +{ + struct twl6040 *twl6040 = data; + u8 intid; + int i; + + intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); + + /* apply masking and report (backwards to handle READYINT first) */ + for (i = ARRAY_SIZE(twl6040_irqs) - 1; i >= 0; i--) { + if (twl6040->irq_masks_cur & twl6040_irqs[i].mask) + intid &= ~twl6040_irqs[i].status; + if (intid & twl6040_irqs[i].status) + handle_nested_irq(twl6040->irq_base + i); + } + + /* ack unmasked irqs */ + twl6040_reg_write(twl6040, TWL6040_REG_INTID, intid); + + return IRQ_HANDLED; +} + +int twl6040_irq_init(struct twl6040 *twl6040) +{ + int cur_irq, ret; + u8 val; + + mutex_init(&twl6040->irq_mutex); + + /* mask the individual interrupt sources */ + twl6040->irq_masks_cur = TWL6040_ALLINT_MSK; + twl6040->irq_masks_cache = TWL6040_ALLINT_MSK; + twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK); + + /* Register them with genirq */ + for (cur_irq = twl6040->irq_base; + cur_irq < twl6040->irq_base + ARRAY_SIZE(twl6040_irqs); + cur_irq++) { + irq_set_chip_data(cur_irq, twl6040); + irq_set_chip_and_handler(cur_irq, &twl6040_irq_chip, + handle_level_irq); + irq_set_nested_thread(cur_irq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + irq_set_noprobe(cur_irq); +#endif + } + + ret = request_threaded_irq(twl6040->irq, NULL, twl6040_irq_thread, + IRQF_ONESHOT, "twl6040", twl6040); + if (ret) { + dev_err(twl6040->dev, "failed to request IRQ %d: %d\n", + twl6040->irq, ret); + return ret; + } + + /* reset interrupts */ + val = twl6040_reg_read(twl6040, TWL6040_REG_INTID); + + /* interrupts cleared on write */ + twl6040_clear_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_INTCLRMODE); + + return 0; +} +EXPORT_SYMBOL(twl6040_irq_init); + +void twl6040_irq_exit(struct twl6040 *twl6040) +{ + free_irq(twl6040->irq, twl6040); +} +EXPORT_SYMBOL(twl6040_irq_exit); diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index ba4f88624fcd..114c0f6fc63d 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -657,28 +657,41 @@ struct twl4030_power_data { extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts); extern int twl4030_remove_script(u8 flags); -struct twl4030_codec_audio_data { +struct twl4030_codec_data { unsigned int digimic_delay; /* in ms */ unsigned int ramp_delay_value; unsigned int offset_cncl_path; unsigned int check_defaults:1; unsigned int reset_registers:1; unsigned int hs_extmute:1; + u16 hs_left_step; + u16 hs_right_step; + u16 hf_left_step; + u16 hf_right_step; void (*set_hs_extmute)(int mute); }; -struct twl4030_codec_vibra_data { +struct twl4030_vibra_data { unsigned int coexist; + + /* twl6040 */ + unsigned int vibldrv_res; /* left driver resistance */ + unsigned int vibrdrv_res; /* right driver resistance */ + unsigned int viblmotor_res; /* left motor resistance */ + unsigned int vibrmotor_res; /* right motor resistance */ + int vddvibl_uV; /* VDDVIBL volt, set 0 for fixed reg */ + int vddvibr_uV; /* VDDVIBR volt, set 0 for fixed reg */ }; -struct twl4030_codec_data { +struct twl4030_audio_data { unsigned int audio_mclk; - struct twl4030_codec_audio_data *audio; - struct twl4030_codec_vibra_data *vibra; + struct twl4030_codec_data *codec; + struct twl4030_vibra_data *vibra; /* twl6040 */ int audpwron_gpio; /* audio power-on gpio */ int naudint_irq; /* audio interrupt */ + unsigned int irq_base; }; struct twl4030_platform_data { @@ -690,7 +703,7 @@ struct twl4030_platform_data { struct twl4030_keypad_data *keypad; struct twl4030_usb_data *usb; struct twl4030_power_data *power; - struct twl4030_codec_data *codec; + struct twl4030_audio_data *audio; /* Common LDO regulators for TWL4030/TWL6030 */ struct regulator_init_data *vdac; diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-audio.h index 5cc16bbd1da1..3d22b72df076 100644 --- a/include/linux/mfd/twl4030-codec.h +++ b/include/linux/mfd/twl4030-audio.h @@ -1,5 +1,5 @@ /* - * MFD driver for twl4030 codec submodule + * MFD driver for twl4030 audio submodule * * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> * @@ -259,14 +259,14 @@ #define TWL4030_VIBRA_DIR_SEL 0x20 /* TWL4030 codec resource IDs */ -enum twl4030_codec_res { - TWL4030_CODEC_RES_POWER = 0, - TWL4030_CODEC_RES_APLL, - TWL4030_CODEC_RES_MAX, +enum twl4030_audio_res { + TWL4030_AUDIO_RES_POWER = 0, + TWL4030_AUDIO_RES_APLL, + TWL4030_AUDIO_RES_MAX, }; -int twl4030_codec_disable_resource(enum twl4030_codec_res id); -int twl4030_codec_enable_resource(enum twl4030_codec_res id); -unsigned int twl4030_codec_get_mclk(void); +int twl4030_audio_disable_resource(enum twl4030_audio_res id); +int twl4030_audio_enable_resource(enum twl4030_audio_res id); +unsigned int twl4030_audio_get_mclk(void); #endif /* End of __TWL4030_CODEC_H__ */ diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h new file mode 100644 index 000000000000..4c806f6d663e --- /dev/null +++ b/include/linux/mfd/twl6040.h @@ -0,0 +1,228 @@ +/* + * MFD driver for twl6040 + * + * Authors: Jorge Eduardo Candelaria <jorge.candelaria@ti.com> + * Misael Lopez Cruz <misael.lopez@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 + * + */ + +#ifndef __TWL6040_CODEC_H__ +#define __TWL6040_CODEC_H__ + +#include <linux/interrupt.h> +#include <linux/mfd/core.h> + +#define TWL6040_REG_ASICID 0x01 +#define TWL6040_REG_ASICREV 0x02 +#define TWL6040_REG_INTID 0x03 +#define TWL6040_REG_INTMR 0x04 +#define TWL6040_REG_NCPCTL 0x05 +#define TWL6040_REG_LDOCTL 0x06 +#define TWL6040_REG_HPPLLCTL 0x07 +#define TWL6040_REG_LPPLLCTL 0x08 +#define TWL6040_REG_LPPLLDIV 0x09 +#define TWL6040_REG_AMICBCTL 0x0A +#define TWL6040_REG_DMICBCTL 0x0B +#define TWL6040_REG_MICLCTL 0x0C +#define TWL6040_REG_MICRCTL 0x0D +#define TWL6040_REG_MICGAIN 0x0E +#define TWL6040_REG_LINEGAIN 0x0F +#define TWL6040_REG_HSLCTL 0x10 +#define TWL6040_REG_HSRCTL 0x11 +#define TWL6040_REG_HSGAIN 0x12 +#define TWL6040_REG_EARCTL 0x13 +#define TWL6040_REG_HFLCTL 0x14 +#define TWL6040_REG_HFLGAIN 0x15 +#define TWL6040_REG_HFRCTL 0x16 +#define TWL6040_REG_HFRGAIN 0x17 +#define TWL6040_REG_VIBCTLL 0x18 +#define TWL6040_REG_VIBDATL 0x19 +#define TWL6040_REG_VIBCTLR 0x1A +#define TWL6040_REG_VIBDATR 0x1B +#define TWL6040_REG_HKCTL1 0x1C +#define TWL6040_REG_HKCTL2 0x1D +#define TWL6040_REG_GPOCTL 0x1E +#define TWL6040_REG_ALB 0x1F +#define TWL6040_REG_DLB 0x20 +#define TWL6040_REG_TRIM1 0x28 +#define TWL6040_REG_TRIM2 0x29 +#define TWL6040_REG_TRIM3 0x2A +#define TWL6040_REG_HSOTRIM 0x2B +#define TWL6040_REG_HFOTRIM 0x2C +#define TWL6040_REG_ACCCTL 0x2D +#define TWL6040_REG_STATUS 0x2E + +#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1) + +#define TWL6040_VIOREGNUM 18 +#define TWL6040_VDDREGNUM 21 + +/* INTID (0x03) fields */ + +#define TWL6040_THINT 0x01 +#define TWL6040_PLUGINT 0x02 +#define TWL6040_UNPLUGINT 0x04 +#define TWL6040_HOOKINT 0x08 +#define TWL6040_HFINT 0x10 +#define TWL6040_VIBINT 0x20 +#define TWL6040_READYINT 0x40 + +/* INTMR (0x04) fields */ + +#define TWL6040_THMSK 0x01 +#define TWL6040_PLUGMSK 0x02 +#define TWL6040_HOOKMSK 0x08 +#define TWL6040_HFMSK 0x10 +#define TWL6040_VIBMSK 0x20 +#define TWL6040_READYMSK 0x40 +#define TWL6040_ALLINT_MSK 0x7B + +/* NCPCTL (0x05) fields */ + +#define TWL6040_NCPENA 0x01 +#define TWL6040_NCPOPEN 0x40 + +/* LDOCTL (0x06) fields */ + +#define TWL6040_LSLDOENA 0x01 +#define TWL6040_HSLDOENA 0x04 +#define TWL6040_REFENA 0x40 +#define TWL6040_OSCENA 0x80 + +/* HPPLLCTL (0x07) fields */ + +#define TWL6040_HPLLENA 0x01 +#define TWL6040_HPLLRST 0x02 +#define TWL6040_HPLLBP 0x04 +#define TWL6040_HPLLSQRENA 0x08 +#define TWL6040_MCLK_12000KHZ (0 << 5) +#define TWL6040_MCLK_19200KHZ (1 << 5) +#define TWL6040_MCLK_26000KHZ (2 << 5) +#define TWL6040_MCLK_38400KHZ (3 << 5) +#define TWL6040_MCLK_MSK 0x60 + +/* LPPLLCTL (0x08) fields */ + +#define TWL6040_LPLLENA 0x01 +#define TWL6040_LPLLRST 0x02 +#define TWL6040_LPLLSEL 0x04 +#define TWL6040_LPLLFIN 0x08 +#define TWL6040_HPLLSEL 0x10 + +/* HSLCTL (0x10) fields */ + +#define TWL6040_HSDACMODEL 0x02 +#define TWL6040_HSDRVMODEL 0x08 + +/* HSRCTL (0x11) fields */ + +#define TWL6040_HSDACMODER 0x02 +#define TWL6040_HSDRVMODER 0x08 + +/* VIBCTLL (0x18) fields */ + +#define TWL6040_VIBENAL 0x01 +#define TWL6040_VIBCTRLL 0x04 +#define TWL6040_VIBCTRLLP 0x08 +#define TWL6040_VIBCTRLLN 0x10 + +/* VIBDATL (0x19) fields */ + +#define TWL6040_VIBDAT_MAX 0x64 + +/* VIBCTLR (0x1A) fields */ + +#define TWL6040_VIBENAR 0x01 +#define TWL6040_VIBCTRLR 0x04 +#define TWL6040_VIBCTRLRP 0x08 +#define TWL6040_VIBCTRLRN 0x10 + +/* GPOCTL (0x1E) fields */ + +#define TWL6040_GPO1 0x01 +#define TWL6040_GPO2 0x02 +#define TWL6040_GPO3 0x03 + +/* ACCCTL (0x2D) fields */ + +#define TWL6040_I2CSEL 0x01 +#define TWL6040_RESETSPLIT 0x04 +#define TWL6040_INTCLRMODE 0x08 + +/* STATUS (0x2E) fields */ + +#define TWL6040_PLUGCOMP 0x02 +#define TWL6040_VIBLOCDET 0x10 +#define TWL6040_VIBROCDET 0x20 +#define TWL6040_TSHUTDET 0x40 + +#define TWL6040_CELLS 2 + +#define TWL6040_REV_ES1_0 0x00 +#define TWL6040_REV_ES1_1 0x01 +#define TWL6040_REV_ES1_2 0x02 + +#define TWL6040_IRQ_TH 0 +#define TWL6040_IRQ_PLUG 1 +#define TWL6040_IRQ_HOOK 2 +#define TWL6040_IRQ_HF 3 +#define TWL6040_IRQ_VIB 4 +#define TWL6040_IRQ_READY 5 + +/* PLL selection */ +#define TWL6040_SYSCLK_SEL_LPPLL 0 +#define TWL6040_SYSCLK_SEL_HPPLL 1 + +struct twl6040 { + struct device *dev; + struct mutex mutex; + struct mutex io_mutex; + struct mutex irq_mutex; + struct mfd_cell cells[TWL6040_CELLS]; + struct completion ready; + + int audpwron; + int power_count; + int rev; + + int pll; + unsigned int sysclk; + + unsigned int irq; + unsigned int irq_base; + u8 irq_masks_cur; + u8 irq_masks_cache; +}; + +int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg); +int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, + u8 val); +int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, + u8 mask); +int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, + u8 mask); +int twl6040_power(struct twl6040 *twl6040, int on); +int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, + unsigned int freq_in, unsigned int freq_out); +int twl6040_get_pll(struct twl6040 *twl6040); +unsigned int twl6040_get_sysclk(struct twl6040 *twl6040); +int twl6040_irq_init(struct twl6040 *twl6040); +void twl6040_irq_exit(struct twl6040 *twl6040); + +#endif /* End of __TWL6040_CODEC_H__ */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 36a030f1d1f5..379b2e3afd98 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -250,10 +250,11 @@ config SND_SOC_TLV320DAC33 tristate config SND_SOC_TWL4030 - select TWL4030_CODEC + select MFD_TWL4030_AUDIO tristate config SND_SOC_TWL6040 + select TWL6040_CORE tristate config SND_SOC_UDA134X diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index bec788b12613..71674bec9604 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -36,7 +36,7 @@ #include <sound/tlv.h> /* Register descriptions are here */ -#include <linux/mfd/twl4030-codec.h> +#include <linux/mfd/twl4030-audio.h> /* Shadow register used by the audio driver */ #define TWL4030_REG_SW_SHADOW 0x4A @@ -251,9 +251,9 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) return; if (enable) - mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); + mode = twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); else - mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); + mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); if (mode >= 0) { twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode); @@ -297,7 +297,7 @@ static inline void twl4030_reset_registers(struct snd_soc_codec *codec) static void twl4030_init_chip(struct snd_soc_codec *codec) { - struct twl4030_codec_audio_data *pdata = dev_get_platdata(codec->dev); + struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 reg, byte; int i = 0; @@ -375,13 +375,13 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) if (enable) { twl4030->apll_enabled++; if (twl4030->apll_enabled == 1) - status = twl4030_codec_enable_resource( - TWL4030_CODEC_RES_APLL); + status = twl4030_audio_enable_resource( + TWL4030_AUDIO_RES_APLL); } else { twl4030->apll_enabled--; if (!twl4030->apll_enabled) - status = twl4030_codec_disable_resource( - TWL4030_CODEC_RES_APLL); + status = twl4030_audio_disable_resource( + TWL4030_AUDIO_RES_APLL); } if (status >= 0) @@ -732,7 +732,7 @@ static int aif_event(struct snd_soc_dapm_widget *w, static void headset_ramp(struct snd_soc_codec *codec, int ramp) { - struct twl4030_codec_audio_data *pdata = codec->dev->platform_data; + struct twl4030_codec_data *pdata = codec->dev->platform_data; unsigned char hs_gain, hs_pop; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); /* Base values for ramp delay calculation: 2^19 - 2^26 */ @@ -2260,7 +2260,7 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) } snd_soc_codec_set_drvdata(codec, twl4030); /* Set the defaults, and power up the codec */ - twl4030->sysclk = twl4030_codec_get_mclk() / 1000; + twl4030->sysclk = twl4030_audio_get_mclk() / 1000; codec->dapm.idle_bias_off = 1; twl4030_init_chip(codec); @@ -2297,7 +2297,7 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { static int __devinit twl4030_codec_probe(struct platform_device *pdev) { - struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; + struct twl4030_codec_data *pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "platform_data is missing\n"); diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index cd63bba623df..443032b3b329 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -24,11 +24,10 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/i2c/twl.h> +#include <linux/mfd/twl6040.h> #include <sound/core.h> #include <sound/pcm.h> @@ -77,14 +76,19 @@ struct twl6040_jack_data { /* codec private data */ struct twl6040_data { - int audpwron; - int naudint; + int plug_irq; int codec_powered; int pll; int non_lp; + int pll_power_mode; + int hs_power_mode; + int hs_power_mode_locked; + unsigned int clk_in; unsigned int sysclk; - struct snd_pcm_hw_constraint_list *sysclk_constraints; - struct completion ready; + u16 hs_left_step; + u16 hs_right_step; + u16 hf_left_step; + u16 hf_right_step; struct twl6040_jack_data hs_jack; struct snd_soc_codec *codec; struct workqueue_struct *workqueue; @@ -206,6 +210,32 @@ static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = { TWL6040_REG_DLB, }; +/* set of rates for each pll: low-power and high-performance */ +static unsigned int lp_rates[] = { + 8000, + 11250, + 16000, + 22500, + 32000, + 44100, + 48000, + 88200, + 96000, +}; + +static unsigned int hp_rates[] = { + 8000, + 16000, + 32000, + 48000, + 96000, +}; + +static struct snd_pcm_hw_constraint_list sysclk_constraints[] = { + { .count = ARRAY_SIZE(lp_rates), .list = lp_rates, }, + { .count = ARRAY_SIZE(hp_rates), .list = hp_rates, }, +}; + /* * read twl6040 register cache */ @@ -239,12 +269,13 @@ static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec, static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, unsigned int reg) { + struct twl6040 *twl6040 = codec->control_data; u8 value; if (reg >= TWL6040_CACHEREGNUM) return -EIO; - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &value, reg); + value = twl6040_reg_read(twl6040, reg); twl6040_write_reg_cache(codec, reg, value); return value; @@ -256,11 +287,13 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, static int twl6040_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { + struct twl6040 *twl6040 = codec->control_data; + if (reg >= TWL6040_CACHEREGNUM) return -EIO; twl6040_write_reg_cache(codec, reg, value); - return twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, value, reg); + return twl6040_reg_write(twl6040, reg, value); } static void twl6040_init_vio_regs(struct snd_soc_codec *codec) @@ -268,15 +301,21 @@ static void twl6040_init_vio_regs(struct snd_soc_codec *codec) u8 *cache = codec->reg_cache; int reg, i; - /* allow registers to be accessed by i2c */ - twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]); - for (i = 0; i < TWL6040_VIOREGNUM; i++) { reg = twl6040_vio_reg[i]; - /* skip read-only registers (ASICID, ASICREV, STATUS) */ + /* + * skip read-only registers (ASICID, ASICREV, STATUS) + * and registers shared among MFD children + */ switch (reg) { case TWL6040_REG_ASICID: case TWL6040_REG_ASICREV: + case TWL6040_REG_INTID: + case TWL6040_REG_INTMR: + case TWL6040_REG_NCPCTL: + case TWL6040_REG_LDOCTL: + case TWL6040_REG_GPOCTL: + case TWL6040_REG_ACCCTL: case TWL6040_REG_STATUS: continue; default: @@ -293,6 +332,20 @@ static void twl6040_init_vdd_regs(struct snd_soc_codec *codec) for (i = 0; i < TWL6040_VDDREGNUM; i++) { reg = twl6040_vdd_reg[i]; + /* skip vibra and PLL registers */ + switch (reg) { + case TWL6040_REG_VIBCTLL: + case TWL6040_REG_VIBDATL: + case TWL6040_REG_VIBCTLR: + case TWL6040_REG_VIBDATR: + case TWL6040_REG_HPPLLCTL: + case TWL6040_REG_LPPLLCTL: + case TWL6040_REG_LPPLLDIV: + continue; + default: + break; + } + twl6040_write(codec, reg, cache[reg]); } } @@ -317,7 +370,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, if (headset->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < headset->left_vol) { - val += left_step; + if (val + left_step > headset->left_vol) + val = headset->left_vol; + else + val += left_step; + reg &= ~TWL6040_HSL_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, (reg | (~val & TWL6040_HSL_VOL_MASK))); @@ -327,7 +384,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, } else if (headset->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0x0) { - val -= left_step; + if ((int)val - (int)left_step < 0) + val = 0; + else + val -= left_step; + reg &= ~TWL6040_HSL_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, reg | (~val & TWL6040_HSL_VOL_MASK)); @@ -344,7 +405,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, if (headset->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < headset->right_vol) { - val += right_step; + if (val + right_step > headset->right_vol) + val = headset->right_vol; + else + val += right_step; + reg &= ~TWL6040_HSR_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, (reg | (~val << TWL6040_HSR_VOL_SHIFT))); @@ -354,7 +419,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, } else if (headset->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0x0) { - val -= right_step; + if ((int)val - (int)right_step < 0) + val = 0; + else + val -= right_step; + reg &= ~TWL6040_HSR_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, reg | (~val << TWL6040_HSR_VOL_SHIFT)); @@ -385,7 +454,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, if (handsfree->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < handsfree->left_vol) { - val += left_step; + if (val + left_step > handsfree->left_vol) + val = handsfree->left_vol; + else + val += left_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFLGAIN, reg | (0x1D - val)); @@ -395,7 +468,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0) { - val -= left_step; + if ((int)val - (int)left_step < 0) + val = 0; + else + val -= left_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFLGAIN, reg | (0x1D - val)); @@ -412,7 +489,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, if (handsfree->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < handsfree->right_vol) { - val += right_step; + if (val + right_step > handsfree->right_vol) + val = handsfree->right_vol; + else + val += right_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFRGAIN, reg | (0x1D - val)); @@ -422,7 +503,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0) { - val -= right_step; + if ((int)val - (int)right_step < 0) + val = 0; + else + val -= right_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFRGAIN, reg | (0x1D - val)); @@ -451,11 +536,9 @@ static void twl6040_pga_hs_work(struct work_struct *work) /* HS PGA volumes have 4 bits of resolution to ramp */ for (i = 0; i <= 16; i++) { - headset_complete = 1; - if (headset->ramp != TWL6040_RAMP_NONE) - headset_complete = twl6040_hs_ramp_step(codec, - headset->left_step, - headset->right_step); + headset_complete = twl6040_hs_ramp_step(codec, + headset->left_step, + headset->right_step); /* ramp finished ? */ if (headset_complete) @@ -496,11 +579,9 @@ static void twl6040_pga_hf_work(struct work_struct *work) /* HF PGA volumes have 5 bits of resolution to ramp */ for (i = 0; i <= 32; i++) { - handsfree_complete = 1; - if (handsfree->ramp != TWL6040_RAMP_NONE) - handsfree_complete = twl6040_hf_ramp_step(codec, - handsfree->left_step, - handsfree->right_step); + handsfree_complete = twl6040_hf_ramp_step(codec, + handsfree->left_step, + handsfree->right_step); /* ramp finished ? */ if (handsfree_complete) @@ -541,12 +622,16 @@ static int pga_event(struct snd_soc_dapm_widget *w, out = &priv->headset; work = &priv->hs_delayed_work; queue = priv->hs_workqueue; + out->left_step = priv->hs_left_step; + out->right_step = priv->hs_right_step; out->step_delay = 5; /* 5 ms between volume ramp steps */ break; case 4: out = &priv->handsfree; work = &priv->hf_delayed_work; queue = priv->hf_workqueue; + out->left_step = priv->hf_left_step; + out->right_step = priv->hf_right_step; out->step_delay = 5; /* 5 ms between volume ramp steps */ if (SND_SOC_DAPM_EVENT_ON(event)) priv->non_lp++; @@ -579,8 +664,6 @@ static int pga_event(struct snd_soc_dapm_widget *w, if (!delayed_work_pending(work)) { /* use volume ramp for power-down */ - out->left_step = 1; - out->right_step = 1; out->ramp = TWL6040_RAMP_DOWN; INIT_COMPLETION(out->ramp_done); @@ -596,88 +679,6 @@ static int pga_event(struct snd_soc_dapm_widget *w, return 0; } -/* twl6040 codec manual power-up sequence */ -static void twl6040_power_up(struct snd_soc_codec *codec) -{ - u8 ncpctl, ldoctl, lppllctl, accctl; - - ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); - ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); - accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); - - /* enable reference system */ - ldoctl |= TWL6040_REFENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - msleep(10); - /* enable internal oscillator */ - ldoctl |= TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(10); - /* enable high-side ldo */ - ldoctl |= TWL6040_HSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* enable negative charge pump */ - ncpctl |= TWL6040_NCPENA | TWL6040_NCPOPEN; - twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); - udelay(488); - /* enable low-side ldo */ - ldoctl |= TWL6040_LSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* enable low-power pll */ - lppllctl |= TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - /* reset state machine */ - accctl |= TWL6040_RESETSPLIT; - twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); - mdelay(5); - accctl &= ~TWL6040_RESETSPLIT; - twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); - /* disable internal oscillator */ - ldoctl &= ~TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); -} - -/* twl6040 codec manual power-down sequence */ -static void twl6040_power_down(struct snd_soc_codec *codec) -{ - u8 ncpctl, ldoctl, lppllctl, accctl; - - ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); - ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); - accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); - - /* enable internal oscillator */ - ldoctl |= TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(10); - /* disable low-power pll */ - lppllctl &= ~TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - /* disable low-side ldo */ - ldoctl &= ~TWL6040_LSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* disable negative charge pump */ - ncpctl &= ~(TWL6040_NCPENA | TWL6040_NCPOPEN); - twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); - udelay(488); - /* disable high-side ldo */ - ldoctl &= ~TWL6040_HSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* disable internal oscillator */ - ldoctl &= ~TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - /* disable reference system */ - ldoctl &= ~TWL6040_REFENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - msleep(10); -} - /* set headset dac and driver power mode */ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) { @@ -713,15 +714,26 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int ret = 0; - if (SND_SOC_DAPM_EVENT_ON(event)) + if (SND_SOC_DAPM_EVENT_ON(event)) { priv->non_lp++; - else + if (!strcmp(w->name, "Earphone Driver")) { + /* Earphone doesn't support low power mode */ + priv->hs_power_mode_locked = 1; + ret = headset_power_mode(codec, 1); + } + } else { priv->non_lp--; + if (!strcmp(w->name, "Earphone Driver")) { + priv->hs_power_mode_locked = 0; + ret = headset_power_mode(codec, priv->hs_power_mode); + } + } msleep(1); - return 0; + return ret; } static void twl6040_hs_jack_report(struct snd_soc_codec *codec, @@ -766,33 +778,19 @@ static void twl6040_accessory_work(struct work_struct *work) } /* audio interrupt handler */ -static irqreturn_t twl6040_naudint_handler(int irq, void *data) +static irqreturn_t twl6040_audio_handler(int irq, void *data) { struct snd_soc_codec *codec = data; + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); u8 intid; - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID); - - if (intid & TWL6040_THINT) - dev_alert(codec->dev, "die temp over-limit detection\n"); + intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); if ((intid & TWL6040_PLUGINT) || (intid & TWL6040_UNPLUGINT)) queue_delayed_work(priv->workqueue, &priv->delayed_work, msecs_to_jiffies(200)); - if (intid & TWL6040_HOOKINT) - dev_info(codec->dev, "hook detection\n"); - - if (intid & TWL6040_HFINT) - dev_alert(codec->dev, "hf drivers over current detection\n"); - - if (intid & TWL6040_VIBINT) - dev_alert(codec->dev, "vib drivers over current detection\n"); - - if (intid & TWL6040_READYINT) - complete(&priv->ready); - return IRQ_HANDLED; } @@ -1040,6 +1038,73 @@ static const struct snd_kcontrol_new hfr_mux_controls = static const struct snd_kcontrol_new ep_driver_switch_controls = SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); +/* Headset power mode */ +static const char *twl6040_power_mode_texts[] = { + "Low-Power", "High-Perfomance", +}; + +static const struct soc_enum twl6040_power_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_power_mode_texts), + twl6040_power_mode_texts); + +static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = priv->hs_power_mode; + + return 0; +} + +static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int high_perf = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (!priv->hs_power_mode_locked) + ret = headset_power_mode(codec, high_perf); + + if (!ret) + priv->hs_power_mode = high_perf; + + return ret; +} + +static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = priv->pll_power_mode; + + return 0; +} + +static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + priv->pll_power_mode = ucontrol->value.enumerated.item[0]; + + return 0; +} + +int twl6040_get_clk_id(struct snd_soc_codec *codec) +{ + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + return priv->pll_power_mode; +} +EXPORT_SYMBOL_GPL(twl6040_get_clk_id); + static const struct snd_kcontrol_new twl6040_snd_controls[] = { /* Capture gains */ SOC_DOUBLE_TLV("Capture Preamplifier Volume", @@ -1058,6 +1123,13 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = { TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), SOC_SINGLE_TLV("Earphone Playback Volume", TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), + + SOC_ENUM_EXT("Headset Power Mode", twl6040_power_mode_enum, + twl6040_headset_power_get_enum, + twl6040_headset_power_put_enum), + + SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum, + twl6040_pll_get_enum, twl6040_pll_put_enum), }; static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { @@ -1231,36 +1303,11 @@ static int twl6040_add_widgets(struct snd_soc_codec *codec) return 0; } -static int twl6040_power_up_completion(struct snd_soc_codec *codec, - int naudint) -{ - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int time_left; - u8 intid; - - time_left = wait_for_completion_timeout(&priv->ready, - msecs_to_jiffies(144)); - - if (!time_left) { - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, - TWL6040_REG_INTID); - if (!(intid & TWL6040_READYINT)) { - dev_err(codec->dev, "timeout waiting for READYINT\n"); - return -ETIMEDOUT; - } - } - - priv->codec_powered = 1; - - return 0; -} - static int twl6040_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int audpwron = priv->audpwron; - int naudint = priv->naudint; int ret; switch (level) { @@ -1272,58 +1319,23 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, if (priv->codec_powered) break; - if (gpio_is_valid(audpwron)) { - /* use AUDPWRON line */ - gpio_set_value(audpwron, 1); + ret = twl6040_power(twl6040, 1); + if (ret) + return ret; - /* wait for power-up completion */ - ret = twl6040_power_up_completion(codec, naudint); - if (ret) - return ret; - - /* sync registers updated during power-up sequence */ - twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); - twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); - twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL); - } else { - /* use manual power-up sequence */ - twl6040_power_up(codec); - priv->codec_powered = 1; - } + priv->codec_powered = 1; /* initialize vdd/vss registers with reg_cache */ twl6040_init_vdd_regs(codec); /* Set external boost GPO */ twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02); - - /* Set initial minimal gain values */ - twl6040_write(codec, TWL6040_REG_HSGAIN, 0xFF); - twl6040_write(codec, TWL6040_REG_EARCTL, 0x1E); - twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1D); - twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1D); break; case SND_SOC_BIAS_OFF: if (!priv->codec_powered) break; - if (gpio_is_valid(audpwron)) { - /* use AUDPWRON line */ - gpio_set_value(audpwron, 0); - - /* power-down sequence latency */ - udelay(500); - - /* sync registers updated during power-down sequence */ - twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); - twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); - twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL, - 0x00); - } else { - /* use manual power-down sequence */ - twl6040_power_down(codec); - } - + twl6040_power(twl6040, 0); priv->codec_powered = 0; break; } @@ -1333,27 +1345,6 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, return 0; } -/* set of rates for each pll: low-power and high-performance */ - -static unsigned int lp_rates[] = { - 88200, - 96000, -}; - -static struct snd_pcm_hw_constraint_list lp_constraints = { - .count = ARRAY_SIZE(lp_rates), - .list = lp_rates, -}; - -static unsigned int hp_rates[] = { - 96000, -}; - -static struct snd_pcm_hw_constraint_list hp_constraints = { - .count = ARRAY_SIZE(hp_rates), - .list = hp_rates, -}; - static int twl6040_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -1363,7 +1354,7 @@ static int twl6040_startup(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - priv->sysclk_constraints); + &sysclk_constraints[priv->pll_power_mode]); return 0; } @@ -1375,22 +1366,27 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - u8 lppllctl; int rate; - /* nothing to do for high-perf pll, it supports only 48 kHz */ - if (priv->pll == TWL6040_HPPLL_ID) - return 0; - - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); - rate = params_rate(params); switch (rate) { case 11250: case 22500: case 44100: case 88200: - lppllctl |= TWL6040_LPLLFIN; + /* These rates are not supported when HPPLL is in use */ + if (unlikely(priv->pll == TWL6040_SYSCLK_SEL_HPPLL)) { + dev_err(codec->dev, "HPPLL does not support rate %d\n", + rate); + return -EINVAL; + } + /* Capture is not supported with 17.64MHz sysclk */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dev_err(codec->dev, + "capture mode is not supported at %dHz\n", + rate); + return -EINVAL; + } priv->sysclk = 17640000; break; case 8000: @@ -1398,7 +1394,6 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, case 32000: case 48000: case 96000: - lppllctl &= ~TWL6040_LPLLFIN; priv->sysclk = 19200000; break; default: @@ -1406,8 +1401,6 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - return 0; } @@ -1416,7 +1409,9 @@ static int twl6040_prepare(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int ret; if (!priv->sysclk) { dev_err(codec->dev, @@ -1424,24 +1419,19 @@ static int twl6040_prepare(struct snd_pcm_substream *substream, return -EINVAL; } - /* - * capture is not supported at 17.64 MHz, - * it's reserved for headset low-power playback scenario - */ - if ((priv->sysclk == 17640000) && - substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - dev_err(codec->dev, - "capture mode is not supported at %dHz\n", - priv->sysclk); - return -EINVAL; - } - if ((priv->sysclk == 17640000) && priv->non_lp) { dev_err(codec->dev, "some enabled paths aren't supported at %dHz\n", priv->sysclk); return -EPERM; } + + ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk); + if (ret) { + dev_err(codec->dev, "Can not set PLL (%d)\n", ret); + return -EPERM; + } + return 0; } @@ -1450,99 +1440,12 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - u8 hppllctl, lppllctl; - - hppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_HPPLLCTL); - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); switch (clk_id) { case TWL6040_SYSCLK_SEL_LPPLL: - switch (freq) { - case 32768: - /* headset dac and driver must be in low-power mode */ - headset_power_mode(codec, 0); - - /* clk32k input requires low-power pll */ - lppllctl |= TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - mdelay(5); - lppllctl &= ~TWL6040_HPLLSEL; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - hppllctl &= ~TWL6040_HPLLENA; - twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); - break; - default: - dev_err(codec->dev, "unknown mclk freq %d\n", freq); - return -EINVAL; - } - - /* lppll divider */ - switch (priv->sysclk) { - case 17640000: - lppllctl |= TWL6040_LPLLFIN; - break; - case 19200000: - lppllctl &= ~TWL6040_LPLLFIN; - break; - default: - /* sysclk not yet configured */ - lppllctl &= ~TWL6040_LPLLFIN; - priv->sysclk = 19200000; - break; - } - - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - - priv->pll = TWL6040_LPPLL_ID; - priv->sysclk_constraints = &lp_constraints; - break; case TWL6040_SYSCLK_SEL_HPPLL: - hppllctl &= ~TWL6040_MCLK_MSK; - - switch (freq) { - case 12000000: - /* mclk input, pll enabled */ - hppllctl |= TWL6040_MCLK_12000KHZ | - TWL6040_HPLLSQRBP | - TWL6040_HPLLENA; - break; - case 19200000: - /* mclk input, pll disabled */ - hppllctl |= TWL6040_MCLK_19200KHZ | - TWL6040_HPLLSQRENA | - TWL6040_HPLLBP; - break; - case 26000000: - /* mclk input, pll enabled */ - hppllctl |= TWL6040_MCLK_26000KHZ | - TWL6040_HPLLSQRBP | - TWL6040_HPLLENA; - break; - case 38400000: - /* clk slicer, pll disabled */ - hppllctl |= TWL6040_MCLK_38400KHZ | - TWL6040_HPLLSQRENA | - TWL6040_HPLLBP; - break; - default: - dev_err(codec->dev, "unknown mclk freq %d\n", freq); - return -EINVAL; - } - - /* headset dac and driver must be in high-performance mode */ - headset_power_mode(codec, 1); - - twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); - udelay(500); - lppllctl |= TWL6040_HPLLSEL; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - lppllctl &= ~TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - - /* high-performance pll can provide only 19.2 MHz */ - priv->pll = TWL6040_HPPLL_ID; - priv->sysclk = 19200000; - priv->sysclk_constraints = &hp_constraints; + priv->pll = clk_id; + priv->clk_in = freq; break; default: dev_err(codec->dev, "unknown clk_id %d\n", clk_id); @@ -1559,15 +1462,27 @@ static struct snd_soc_dai_ops twl6040_dai_ops = { .set_sysclk = twl6040_set_dai_sysclk, }; -static struct snd_soc_dai_driver twl6040_dai = { +static struct snd_soc_dai_driver twl6040_dai[] = { +{ .name = "twl6040-hifi", .playback = { .stream_name = "Playback", .channels_min = 1, - .channels_max = 4, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, .rates = TWL6040_RATES, .formats = TWL6040_FORMATS, }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-ul", .capture = { .stream_name = "Capture", .channels_min = 1, @@ -1576,6 +1491,40 @@ static struct snd_soc_dai_driver twl6040_dai = { .formats = TWL6040_FORMATS, }, .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-dl1", + .playback = { + .stream_name = "Headset Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-dl2", + .playback = { + .stream_name = "Handsfree Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-vib", + .playback = { + .stream_name = "Vibra Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, }; #ifdef CONFIG_PM @@ -1600,11 +1549,11 @@ static int twl6040_resume(struct snd_soc_codec *codec) static int twl6040_probe(struct snd_soc_codec *codec) { - struct twl4030_codec_data *twl_codec = codec->dev->platform_data; struct twl6040_data *priv; - int audpwron, naudint; + struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); + struct platform_device *pdev = container_of(codec->dev, + struct platform_device, dev); int ret = 0; - u8 icrev, intmr = TWL6040_ALLINT_MSK; priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); if (priv == NULL) @@ -1612,23 +1561,32 @@ static int twl6040_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, priv); priv->codec = codec; + codec->control_data = dev_get_drvdata(codec->dev->parent); - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &icrev, TWL6040_REG_ASICREV); + if (pdata && pdata->hs_left_step && pdata->hs_right_step) { + priv->hs_left_step = pdata->hs_left_step; + priv->hs_right_step = pdata->hs_right_step; + } else { + priv->hs_left_step = 1; + priv->hs_right_step = 1; + } - if (twl_codec && (icrev > 0)) - audpwron = twl_codec->audpwron_gpio; - else - audpwron = -EINVAL; + if (pdata && pdata->hf_left_step && pdata->hf_right_step) { + priv->hf_left_step = pdata->hf_left_step; + priv->hf_right_step = pdata->hf_right_step; + } else { + priv->hf_left_step = 1; + priv->hf_right_step = 1; + } - if (twl_codec) - naudint = twl_codec->naudint_irq; - else - naudint = 0; + priv->plug_irq = platform_get_irq(pdev, 0); + if (priv->plug_irq < 0) { + dev_err(codec->dev, "invalid irq\n"); + ret = -EINVAL; + goto work_err; + } - priv->audpwron = audpwron; - priv->naudint = naudint; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); - if (!priv->workqueue) { ret = -ENOMEM; goto work_err; @@ -1638,56 +1596,33 @@ static int twl6040_probe(struct snd_soc_codec *codec) mutex_init(&priv->mutex); - init_completion(&priv->ready); init_completion(&priv->headset.ramp_done); init_completion(&priv->handsfree.ramp_done); - if (gpio_is_valid(audpwron)) { - ret = gpio_request(audpwron, "audpwron"); - if (ret) - goto gpio1_err; - - ret = gpio_direction_output(audpwron, 0); - if (ret) - goto gpio2_err; - - priv->codec_powered = 0; - - /* enable only codec ready interrupt */ - intmr &= ~(TWL6040_READYMSK | TWL6040_PLUGMSK); - - /* reset interrupt status to allow correct power up sequence */ - twl6040_read_reg_volatile(codec, TWL6040_REG_INTID); - } - twl6040_write(codec, TWL6040_REG_INTMR, intmr); - - if (naudint) { - /* audio interrupt */ - ret = request_threaded_irq(naudint, NULL, - twl6040_naudint_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "twl6040_codec", codec); - if (ret) - goto gpio2_err; - } - - /* init vio registers */ - twl6040_init_vio_regs(codec); - priv->hf_workqueue = create_singlethread_workqueue("twl6040-hf"); if (priv->hf_workqueue == NULL) { ret = -ENOMEM; - goto irq_err; + goto hfwq_err; } priv->hs_workqueue = create_singlethread_workqueue("twl6040-hs"); if (priv->hs_workqueue == NULL) { ret = -ENOMEM; - goto wq_err; + goto hswq_err; } INIT_DELAYED_WORK(&priv->hs_delayed_work, twl6040_pga_hs_work); INIT_DELAYED_WORK(&priv->hf_delayed_work, twl6040_pga_hf_work); + ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler, + 0, "twl6040_irq_plug", codec); + if (ret) { + dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); + goto plugirq_err; + } + + /* init vio registers */ + twl6040_init_vio_regs(codec); + /* power on device */ ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret) @@ -1700,16 +1635,12 @@ static int twl6040_probe(struct snd_soc_codec *codec) return 0; bias_err: + free_irq(priv->plug_irq, codec); +plugirq_err: destroy_workqueue(priv->hs_workqueue); -wq_err: +hswq_err: destroy_workqueue(priv->hf_workqueue); -irq_err: - if (naudint) - free_irq(naudint, codec); -gpio2_err: - if (gpio_is_valid(audpwron)) - gpio_free(audpwron); -gpio1_err: +hfwq_err: destroy_workqueue(priv->workqueue); work_err: kfree(priv); @@ -1719,17 +1650,9 @@ work_err: static int twl6040_remove(struct snd_soc_codec *codec) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int audpwron = priv->audpwron; - int naudint = priv->naudint; twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); - - if (gpio_is_valid(audpwron)) - gpio_free(audpwron); - - if (naudint) - free_irq(naudint, codec); - + free_irq(priv->plug_irq, codec); destroy_workqueue(priv->workqueue); destroy_workqueue(priv->hf_workqueue); destroy_workqueue(priv->hs_workqueue); @@ -1753,8 +1676,8 @@ static struct snd_soc_codec_driver soc_codec_dev_twl6040 = { static int __devinit twl6040_codec_probe(struct platform_device *pdev) { - return snd_soc_register_codec(&pdev->dev, - &soc_codec_dev_twl6040, &twl6040_dai, 1); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl6040, + twl6040_dai, ARRAY_SIZE(twl6040_dai)); } static int __devexit twl6040_codec_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h index 23aeed0963e6..d8de67869dd9 100644 --- a/sound/soc/codecs/twl6040.h +++ b/sound/soc/codecs/twl6040.h @@ -22,125 +22,8 @@ #ifndef __TWL6040_H__ #define __TWL6040_H__ -#define TWL6040_REG_ASICID 0x01 -#define TWL6040_REG_ASICREV 0x02 -#define TWL6040_REG_INTID 0x03 -#define TWL6040_REG_INTMR 0x04 -#define TWL6040_REG_NCPCTL 0x05 -#define TWL6040_REG_LDOCTL 0x06 -#define TWL6040_REG_HPPLLCTL 0x07 -#define TWL6040_REG_LPPLLCTL 0x08 -#define TWL6040_REG_LPPLLDIV 0x09 -#define TWL6040_REG_AMICBCTL 0x0A -#define TWL6040_REG_DMICBCTL 0x0B -#define TWL6040_REG_MICLCTL 0x0C -#define TWL6040_REG_MICRCTL 0x0D -#define TWL6040_REG_MICGAIN 0x0E -#define TWL6040_REG_LINEGAIN 0x0F -#define TWL6040_REG_HSLCTL 0x10 -#define TWL6040_REG_HSRCTL 0x11 -#define TWL6040_REG_HSGAIN 0x12 -#define TWL6040_REG_EARCTL 0x13 -#define TWL6040_REG_HFLCTL 0x14 -#define TWL6040_REG_HFLGAIN 0x15 -#define TWL6040_REG_HFRCTL 0x16 -#define TWL6040_REG_HFRGAIN 0x17 -#define TWL6040_REG_VIBCTLL 0x18 -#define TWL6040_REG_VIBDATL 0x19 -#define TWL6040_REG_VIBCTLR 0x1A -#define TWL6040_REG_VIBDATR 0x1B -#define TWL6040_REG_HKCTL1 0x1C -#define TWL6040_REG_HKCTL2 0x1D -#define TWL6040_REG_GPOCTL 0x1E -#define TWL6040_REG_ALB 0x1F -#define TWL6040_REG_DLB 0x20 -#define TWL6040_REG_TRIM1 0x28 -#define TWL6040_REG_TRIM2 0x29 -#define TWL6040_REG_TRIM3 0x2A -#define TWL6040_REG_HSOTRIM 0x2B -#define TWL6040_REG_HFOTRIM 0x2C -#define TWL6040_REG_ACCCTL 0x2D -#define TWL6040_REG_STATUS 0x2E - -#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1) - -#define TWL6040_VIOREGNUM 18 -#define TWL6040_VDDREGNUM 21 - -/* INTID (0x03) fields */ - -#define TWL6040_THINT 0x01 -#define TWL6040_PLUGINT 0x02 -#define TWL6040_UNPLUGINT 0x04 -#define TWL6040_HOOKINT 0x08 -#define TWL6040_HFINT 0x10 -#define TWL6040_VIBINT 0x20 -#define TWL6040_READYINT 0x40 - -/* INTMR (0x04) fields */ - -#define TWL6040_PLUGMSK 0x02 -#define TWL6040_READYMSK 0x40 -#define TWL6040_ALLINT_MSK 0x7B - -/* NCPCTL (0x05) fields */ - -#define TWL6040_NCPENA 0x01 -#define TWL6040_NCPOPEN 0x40 - -/* LDOCTL (0x06) fields */ - -#define TWL6040_LSLDOENA 0x01 -#define TWL6040_HSLDOENA 0x04 -#define TWL6040_REFENA 0x40 -#define TWL6040_OSCENA 0x80 - -/* HPPLLCTL (0x07) fields */ - -#define TWL6040_HPLLENA 0x01 -#define TWL6040_HPLLRST 0x02 -#define TWL6040_HPLLBP 0x04 -#define TWL6040_HPLLSQRENA 0x08 -#define TWL6040_HPLLSQRBP 0x10 -#define TWL6040_MCLK_12000KHZ (0 << 5) -#define TWL6040_MCLK_19200KHZ (1 << 5) -#define TWL6040_MCLK_26000KHZ (2 << 5) -#define TWL6040_MCLK_38400KHZ (3 << 5) -#define TWL6040_MCLK_MSK 0x60 - -/* LPPLLCTL (0x08) fields */ - -#define TWL6040_LPLLENA 0x01 -#define TWL6040_LPLLRST 0x02 -#define TWL6040_LPLLSEL 0x04 -#define TWL6040_LPLLFIN 0x08 -#define TWL6040_HPLLSEL 0x10 - -/* HSLCTL (0x10) fields */ - -#define TWL6040_HSDACMODEL 0x02 -#define TWL6040_HSDRVMODEL 0x08 - -/* HSRCTL (0x11) fields */ - -#define TWL6040_HSDACMODER 0x02 -#define TWL6040_HSDRVMODER 0x08 - -/* ACCCTL (0x2D) fields */ - -#define TWL6040_RESETSPLIT 0x04 - -#define TWL6040_SYSCLK_SEL_LPPLL 1 -#define TWL6040_SYSCLK_SEL_HPPLL 2 - -#define TWL6040_HPPLL_ID 1 -#define TWL6040_LPPLL_ID 2 - -/* STATUS (0x2E) fields */ - -#define TWL6040_PLUGCOMP 0x02 - void twl6040_hs_jack_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int report); +int twl6040_get_clk_id(struct snd_soc_codec *codec); #endif /* End of __TWL6040_H__ */ diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index 3f72d17d1ef0..9f6a758029d1 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -36,7 +36,7 @@ #include <plat/mcbsp.h> /* Register descriptions for twl4030 codec part */ -#include <linux/mfd/twl4030-codec.h> +#include <linux/mfd/twl4030-audio.h> #include "omap-mcbsp.h" #include "omap-pcm.h" diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c index 189e03900637..b80efb02bfca 100644 --- a/sound/soc/omap/sdp4430.c +++ b/sound/soc/omap/sdp4430.c @@ -21,6 +21,8 @@ #include <linux/clk.h> #include <linux/platform_device.h> +#include <linux/mfd/twl6040.h> + #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> @@ -34,8 +36,6 @@ #include "omap-pcm.h" #include "../codecs/twl6040.h" -static int twl6040_power_mode; - static int sdp4430_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -44,13 +44,13 @@ static int sdp4430_hw_params(struct snd_pcm_substream *substream, int clk_id, freq; int ret; - if (twl6040_power_mode) { - clk_id = TWL6040_SYSCLK_SEL_HPPLL; + clk_id = twl6040_get_clk_id(rtd->codec); + if (clk_id == TWL6040_SYSCLK_SEL_HPPLL) freq = 38400000; - } else { - clk_id = TWL6040_SYSCLK_SEL_LPPLL; + else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL) freq = 32768; - } + else + return -EINVAL; /* set the codec mclk */ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq, @@ -81,35 +81,6 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { }, }; -static int sdp4430_get_power_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = twl6040_power_mode; - return 0; -} - -static int sdp4430_set_power_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - if (twl6040_power_mode == ucontrol->value.integer.value[0]) - return 0; - - twl6040_power_mode = ucontrol->value.integer.value[0]; - - return 1; -} - -static const char *power_texts[] = {"Low-Power", "High-Performance"}; - -static const struct soc_enum sdp4430_enum[] = { - SOC_ENUM_SINGLE_EXT(2, power_texts), -}; - -static const struct snd_kcontrol_new sdp4430_controls[] = { - SOC_ENUM_EXT("TWL6040 Power Mode", sdp4430_enum[0], - sdp4430_get_power_mode, sdp4430_set_power_mode), -}; - /* SDP4430 machine DAPM */ static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = { SND_SOC_DAPM_MIC("Ext Mic", NULL), @@ -152,12 +123,6 @@ static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - /* Add SDP4430 specific controls */ - ret = snd_soc_add_controls(codec, sdp4430_controls, - ARRAY_SIZE(sdp4430_controls)); - if (ret) - return ret; - /* Add SDP4430 specific widgets */ ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets, ARRAY_SIZE(sdp4430_twl6040_dapm_widgets)); @@ -237,9 +202,6 @@ static int __init sdp4430_soc_init(void) if (ret) goto err; - /* Codec starts in HP mode */ - twl6040_power_mode = 1; - return 0; err: diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c index 01709940a43c..9a2666ffc16c 100644 --- a/sound/soc/omap/zoom2.c +++ b/sound/soc/omap/zoom2.c @@ -32,7 +32,7 @@ #include <plat/mcbsp.h> /* Register descriptions for twl4030 codec part */ -#include <linux/mfd/twl4030-codec.h> +#include <linux/mfd/twl4030-audio.h> #include "omap-mcbsp.h" #include "omap-pcm.h" |