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

* 'next/devel2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/linux-arm-soc: (47 commits)
  OMAP: Add debugfs node to show the summary of all clocks
  OMAP2+: hwmod: Follow the recommended PRCM module enable sequence
  OMAP2+: clock: allow per-SoC clock init code to prevent clockdomain calls from clock code
  OMAP2+: clockdomain: Add per clkdm lock to prevent concurrent state programming
  OMAP2+: PM: idle clkdms only if already in idle
  OMAP2+: clockdomain: add clkdm_in_hwsup()
  OMAP2+: clockdomain: Add 2 APIs to control clockdomain from hwmod framework
  OMAP: clockdomain: Remove redundant call to pwrdm_wait_transition()
  OMAP4: hwmod: Introduce the module control in hwmod control
  OMAP4: cm: Add two new APIs for modulemode control
  OMAP4: hwmod data: Add modulemode entry in omap_hwmod structure
  OMAP4: hwmod data: Add PRM context register offset
  OMAP4: prm: Remove deprecated functions
  OMAP4: prm: Replace warm reset API with the offset based version
  OMAP4: hwmod: Replace RSTCTRL absolute address with offset macros
  OMAP: hwmod: Wait the idle status to be disabled
  OMAP4: hwmod: Replace CLKCTRL absolute address with offset macros
  OMAP2+: hwmod: Init clkdm field at boot time
  OMAP4: hwmod data: Add clock domain attribute
  OMAP4: clock data: Add missing divider selection for auxclks
  ...
This commit is contained in:
Linus Torvalds
2011-07-26 17:42:18 -07:00
51 changed files with 3583 additions and 1388 deletions

View File

@@ -345,11 +345,40 @@ static struct platform_device sdp4430_lcd_device = {
.id = -1, .id = -1,
}; };
static struct regulator_consumer_supply sdp4430_vbat_supply[] = {
REGULATOR_SUPPLY("vddvibl", "twl6040-vibra"),
REGULATOR_SUPPLY("vddvibr", "twl6040-vibra"),
};
static struct regulator_init_data sdp4430_vbat_data = {
.constraints = {
.always_on = 1,
},
.num_consumer_supplies = ARRAY_SIZE(sdp4430_vbat_supply),
.consumer_supplies = sdp4430_vbat_supply,
};
static struct fixed_voltage_config sdp4430_vbat_pdata = {
.supply_name = "VBAT",
.microvolts = 3750000,
.init_data = &sdp4430_vbat_data,
.gpio = -EINVAL,
};
static struct platform_device sdp4430_vbat = {
.name = "reg-fixed-voltage",
.id = -1,
.dev = {
.platform_data = &sdp4430_vbat_pdata,
},
};
static struct platform_device *sdp4430_devices[] __initdata = { static struct platform_device *sdp4430_devices[] __initdata = {
&sdp4430_lcd_device, &sdp4430_lcd_device,
&sdp4430_gpio_keys_device, &sdp4430_gpio_keys_device,
&sdp4430_leds_gpio, &sdp4430_leds_gpio,
&sdp4430_leds_pwm, &sdp4430_leds_pwm,
&sdp4430_vbat,
}; };
static struct omap_lcd_config sdp4430_lcd_config __initdata = { static struct omap_lcd_config sdp4430_lcd_config __initdata = {
@@ -505,7 +534,33 @@ static struct regulator_init_data sdp4430_vusim = {
}, },
}; };
static struct twl4030_codec_data twl6040_codec = {
/* single-step ramp for headset and handsfree */
.hs_left_step = 0x0f,
.hs_right_step = 0x0f,
.hf_left_step = 0x1d,
.hf_right_step = 0x1d,
};
static struct twl4030_vibra_data twl6040_vibra = {
.vibldrv_res = 8,
.vibrdrv_res = 3,
.viblmotor_res = 10,
.vibrmotor_res = 10,
.vddvibl_uV = 0, /* fixed volt supply - VBAT */
.vddvibr_uV = 0, /* fixed volt supply - VBAT */
};
static struct twl4030_audio_data twl6040_audio = {
.codec = &twl6040_codec,
.vibra = &twl6040_vibra,
.audpwron_gpio = 127,
.naudint_irq = OMAP44XX_IRQ_SYS_2N,
.irq_base = TWL6040_CODEC_IRQ_BASE,
};
static struct twl4030_platform_data sdp4430_twldata = { static struct twl4030_platform_data sdp4430_twldata = {
.audio = &twl6040_audio,
/* Regulators */ /* Regulators */
.vusim = &sdp4430_vusim, .vusim = &sdp4430_vusim,
.vaux1 = &sdp4430_vaux1, .vaux1 = &sdp4430_vaux1,

View File

@@ -864,11 +864,11 @@ static struct twl4030_power_data rx51_t2scripts_data __initdata = {
.resource_config = twl4030_rconfig, .resource_config = twl4030_rconfig,
}; };
struct twl4030_codec_vibra_data rx51_vibra_data __initdata = { struct twl4030_vibra_data rx51_vibra_data __initdata = {
.coexist = 0, .coexist = 0,
}; };
struct twl4030_codec_data rx51_codec_data __initdata = { struct twl4030_audio_data rx51_audio_data __initdata = {
.audio_mclk = 26000000, .audio_mclk = 26000000,
.vibra = &rx51_vibra_data, .vibra = &rx51_vibra_data,
}; };
@@ -878,7 +878,7 @@ static struct twl4030_platform_data rx51_twldata __initdata = {
.gpio = &rx51_gpio_data, .gpio = &rx51_gpio_data,
.keypad = &rx51_kp_data, .keypad = &rx51_kp_data,
.power = &rx51_t2scripts_data, .power = &rx51_t2scripts_data,
.codec = &rx51_codec_data, .audio = &rx51_audio_data,
.vaux1 = &rx51_vaux1, .vaux1 = &rx51_vaux1,
.vaux2 = &rx51_vaux2, .vaux2 = &rx51_vaux2,

View File

@@ -274,12 +274,12 @@ static int __init omap_i2c_init(void)
TWL_COMMON_REGULATOR_VDAC | TWL_COMMON_REGULATOR_VPLL2); TWL_COMMON_REGULATOR_VDAC | TWL_COMMON_REGULATOR_VPLL2);
if (machine_is_omap_zoom2()) { if (machine_is_omap_zoom2()) {
struct twl4030_codec_audio_data *audio_data; struct twl4030_codec_data *codec_data;
audio_data = zoom_twldata.codec->audio; codec_data = zoom_twldata.audio->codec;
audio_data->ramp_delay_value = 3; /* 161 ms */ codec_data->ramp_delay_value = 3; /* 161 ms */
audio_data->hs_extmute = 1; codec_data->hs_extmute = 1;
audio_data->set_hs_extmute = zoom2_set_hs_extmute; codec_data->set_hs_extmute = zoom2_set_hs_extmute;
} }
omap_pmic_init(1, 2400, "twl5030", INT_34XX_SYS_NIRQ, &zoom_twldata); omap_pmic_init(1, 2400, "twl5030", INT_34XX_SYS_NIRQ, &zoom_twldata);
omap_register_i2c_bus(2, 400, NULL, 0); omap_register_i2c_bus(2, 400, NULL, 0);

View File

@@ -37,6 +37,14 @@
u8 cpu_mask; u8 cpu_mask;
/*
* clkdm_control: if true, then when a clock is enabled in the
* hardware, its clockdomain will first be enabled; and when a clock
* is disabled in the hardware, its clockdomain will be disabled
* afterwards.
*/
static bool clkdm_control = true;
/* /*
* OMAP2+ specific clock functions * OMAP2+ specific clock functions
*/ */
@@ -99,6 +107,19 @@ void omap2_init_clk_clkdm(struct clk *clk)
} }
} }
/**
* omap2_clk_disable_clkdm_control - disable clkdm control on clk enable/disable
*
* Prevent the OMAP clock code from calling into the clockdomain code
* when a hardware clock in that clockdomain is enabled or disabled.
* Intended to be called at init time from omap*_clk_init(). No
* return value.
*/
void __init omap2_clk_disable_clkdm_control(void)
{
clkdm_control = false;
}
/** /**
* omap2_clk_dflt_find_companion - find companion clock to @clk * omap2_clk_dflt_find_companion - find companion clock to @clk
* @clk: struct clk * to find the companion clock of * @clk: struct clk * to find the companion clock of
@@ -268,7 +289,7 @@ void omap2_clk_disable(struct clk *clk)
clk->ops->disable(clk); clk->ops->disable(clk);
} }
if (clk->clkdm) if (clkdm_control && clk->clkdm)
clkdm_clk_disable(clk->clkdm, clk); clkdm_clk_disable(clk->clkdm, clk);
if (clk->parent) if (clk->parent)
@@ -308,7 +329,7 @@ int omap2_clk_enable(struct clk *clk)
} }
} }
if (clk->clkdm) { if (clkdm_control && clk->clkdm) {
ret = clkdm_clk_enable(clk->clkdm, clk); ret = clkdm_clk_enable(clk->clkdm, clk);
if (ret) { if (ret) {
WARN(1, "clock: %s: could not enable clockdomain %s: " WARN(1, "clock: %s: could not enable clockdomain %s: "
@@ -330,7 +351,7 @@ int omap2_clk_enable(struct clk *clk)
return 0; return 0;
oce_err3: oce_err3:
if (clk->clkdm) if (clkdm_control && clk->clkdm)
clkdm_clk_disable(clk->clkdm, clk); clkdm_clk_disable(clk->clkdm, clk);
oce_err2: oce_err2:
if (clk->parent) if (clk->parent)

View File

@@ -16,6 +16,8 @@
#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H #ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H
#define __ARCH_ARM_MACH_OMAP2_CLOCK_H #define __ARCH_ARM_MACH_OMAP2_CLOCK_H
#include <linux/kernel.h>
#include <plat/clock.h> #include <plat/clock.h>
/* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */ /* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */
@@ -72,6 +74,7 @@ void omap2_clk_disable_unused(struct clk *clk);
#endif #endif
void omap2_init_clk_clkdm(struct clk *clk); void omap2_init_clk_clkdm(struct clk *clk);
void __init omap2_clk_disable_clkdm_control(void);
/* clkt_clksel.c public functions */ /* clkt_clksel.c public functions */
u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,

View File

@@ -1805,9 +1805,9 @@ static struct omap_clk omap2420_clks[] = {
CLK(NULL, "gfx_ick", &gfx_ick, CK_242X), CLK(NULL, "gfx_ick", &gfx_ick, CK_242X),
/* DSS domain clocks */ /* DSS domain clocks */
CLK("omapdss_dss", "ick", &dss_ick, CK_242X), CLK("omapdss_dss", "ick", &dss_ick, CK_242X),
CLK("omapdss_dss", "fck", &dss1_fck, CK_242X), CLK(NULL, "dss1_fck", &dss1_fck, CK_242X),
CLK("omapdss_dss", "sys_clk", &dss2_fck, CK_242X), CLK(NULL, "dss2_fck", &dss2_fck, CK_242X),
CLK("omapdss_dss", "tv_clk", &dss_54m_fck, CK_242X), CLK(NULL, "dss_54m_fck", &dss_54m_fck, CK_242X),
/* L3 domain clocks */ /* L3 domain clocks */
CLK(NULL, "core_l3_ck", &core_l3_ck, CK_242X), CLK(NULL, "core_l3_ck", &core_l3_ck, CK_242X),
CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_242X), CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_242X),
@@ -1844,13 +1844,13 @@ static struct omap_clk omap2420_clks[] = {
CLK(NULL, "gpt12_ick", &gpt12_ick, CK_242X), CLK(NULL, "gpt12_ick", &gpt12_ick, CK_242X),
CLK(NULL, "gpt12_fck", &gpt12_fck, CK_242X), CLK(NULL, "gpt12_fck", &gpt12_fck, CK_242X),
CLK("omap-mcbsp.1", "ick", &mcbsp1_ick, CK_242X), CLK("omap-mcbsp.1", "ick", &mcbsp1_ick, CK_242X),
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_242X), CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_242X),
CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_242X), CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_242X),
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_242X), CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_242X),
CLK("omap2_mcspi.1", "ick", &mcspi1_ick, CK_242X), CLK("omap2_mcspi.1", "ick", &mcspi1_ick, CK_242X),
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_242X), CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_242X),
CLK("omap2_mcspi.2", "ick", &mcspi2_ick, CK_242X), CLK("omap2_mcspi.2", "ick", &mcspi2_ick, CK_242X),
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_242X), CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_242X),
CLK(NULL, "uart1_ick", &uart1_ick, CK_242X), CLK(NULL, "uart1_ick", &uart1_ick, CK_242X),
CLK(NULL, "uart1_fck", &uart1_fck, CK_242X), CLK(NULL, "uart1_fck", &uart1_fck, CK_242X),
CLK(NULL, "uart2_ick", &uart2_ick, CK_242X), CLK(NULL, "uart2_ick", &uart2_ick, CK_242X),
@@ -1860,7 +1860,7 @@ static struct omap_clk omap2420_clks[] = {
CLK(NULL, "gpios_ick", &gpios_ick, CK_242X), CLK(NULL, "gpios_ick", &gpios_ick, CK_242X),
CLK(NULL, "gpios_fck", &gpios_fck, CK_242X), CLK(NULL, "gpios_fck", &gpios_fck, CK_242X),
CLK("omap_wdt", "ick", &mpu_wdt_ick, CK_242X), CLK("omap_wdt", "ick", &mpu_wdt_ick, CK_242X),
CLK("omap_wdt", "fck", &mpu_wdt_fck, CK_242X), CLK(NULL, "mpu_wdt_fck", &mpu_wdt_fck, CK_242X),
CLK(NULL, "sync_32k_ick", &sync_32k_ick, CK_242X), CLK(NULL, "sync_32k_ick", &sync_32k_ick, CK_242X),
CLK(NULL, "wdt1_ick", &wdt1_ick, CK_242X), CLK(NULL, "wdt1_ick", &wdt1_ick, CK_242X),
CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_242X), CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_242X),
@@ -1880,11 +1880,11 @@ static struct omap_clk omap2420_clks[] = {
CLK(NULL, "eac_ick", &eac_ick, CK_242X), CLK(NULL, "eac_ick", &eac_ick, CK_242X),
CLK(NULL, "eac_fck", &eac_fck, CK_242X), CLK(NULL, "eac_fck", &eac_fck, CK_242X),
CLK("omap_hdq.0", "ick", &hdq_ick, CK_242X), CLK("omap_hdq.0", "ick", &hdq_ick, CK_242X),
CLK("omap_hdq.1", "fck", &hdq_fck, CK_242X), CLK("omap_hdq.0", "fck", &hdq_fck, CK_242X),
CLK("omap_i2c.1", "ick", &i2c1_ick, CK_242X), CLK("omap_i2c.1", "ick", &i2c1_ick, CK_242X),
CLK("omap_i2c.1", "fck", &i2c1_fck, CK_242X), CLK(NULL, "i2c1_fck", &i2c1_fck, CK_242X),
CLK("omap_i2c.2", "ick", &i2c2_ick, CK_242X), CLK("omap_i2c.2", "ick", &i2c2_ick, CK_242X),
CLK("omap_i2c.2", "fck", &i2c2_fck, CK_242X), CLK(NULL, "i2c2_fck", &i2c2_fck, CK_242X),
CLK(NULL, "gpmc_fck", &gpmc_fck, CK_242X), CLK(NULL, "gpmc_fck", &gpmc_fck, CK_242X),
CLK(NULL, "sdma_fck", &sdma_fck, CK_242X), CLK(NULL, "sdma_fck", &sdma_fck, CK_242X),
CLK(NULL, "sdma_ick", &sdma_ick, CK_242X), CLK(NULL, "sdma_ick", &sdma_ick, CK_242X),

View File

@@ -1895,9 +1895,9 @@ static struct omap_clk omap2430_clks[] = {
CLK(NULL, "mdm_osc_ck", &mdm_osc_ck, CK_243X), CLK(NULL, "mdm_osc_ck", &mdm_osc_ck, CK_243X),
/* DSS domain clocks */ /* DSS domain clocks */
CLK("omapdss_dss", "ick", &dss_ick, CK_243X), CLK("omapdss_dss", "ick", &dss_ick, CK_243X),
CLK("omapdss_dss", "fck", &dss1_fck, CK_243X), CLK(NULL, "dss1_fck", &dss1_fck, CK_243X),
CLK("omapdss_dss", "sys_clk", &dss2_fck, CK_243X), CLK(NULL, "dss2_fck", &dss2_fck, CK_243X),
CLK("omapdss_dss", "tv_clk", &dss_54m_fck, CK_243X), CLK(NULL, "dss_54m_fck", &dss_54m_fck, CK_243X),
/* L3 domain clocks */ /* L3 domain clocks */
CLK(NULL, "core_l3_ck", &core_l3_ck, CK_243X), CLK(NULL, "core_l3_ck", &core_l3_ck, CK_243X),
CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_243X), CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_243X),
@@ -1934,21 +1934,21 @@ static struct omap_clk omap2430_clks[] = {
CLK(NULL, "gpt12_ick", &gpt12_ick, CK_243X), CLK(NULL, "gpt12_ick", &gpt12_ick, CK_243X),
CLK(NULL, "gpt12_fck", &gpt12_fck, CK_243X), CLK(NULL, "gpt12_fck", &gpt12_fck, CK_243X),
CLK("omap-mcbsp.1", "ick", &mcbsp1_ick, CK_243X), CLK("omap-mcbsp.1", "ick", &mcbsp1_ick, CK_243X),
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_243X), CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_243X),
CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_243X), CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_243X),
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_243X), CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_243X),
CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_243X), CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_243X),
CLK("omap-mcbsp.3", "fck", &mcbsp3_fck, CK_243X), CLK(NULL, "mcbsp3_fck", &mcbsp3_fck, CK_243X),
CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_243X), CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_243X),
CLK("omap-mcbsp.4", "fck", &mcbsp4_fck, CK_243X), CLK(NULL, "mcbsp4_fck", &mcbsp4_fck, CK_243X),
CLK("omap-mcbsp.5", "ick", &mcbsp5_ick, CK_243X), CLK("omap-mcbsp.5", "ick", &mcbsp5_ick, CK_243X),
CLK("omap-mcbsp.5", "fck", &mcbsp5_fck, CK_243X), CLK(NULL, "mcbsp5_fck", &mcbsp5_fck, CK_243X),
CLK("omap2_mcspi.1", "ick", &mcspi1_ick, CK_243X), CLK("omap2_mcspi.1", "ick", &mcspi1_ick, CK_243X),
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_243X), CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_243X),
CLK("omap2_mcspi.2", "ick", &mcspi2_ick, CK_243X), CLK("omap2_mcspi.2", "ick", &mcspi2_ick, CK_243X),
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_243X), CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_243X),
CLK("omap2_mcspi.3", "ick", &mcspi3_ick, CK_243X), CLK("omap2_mcspi.3", "ick", &mcspi3_ick, CK_243X),
CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_243X), CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_243X),
CLK(NULL, "uart1_ick", &uart1_ick, CK_243X), CLK(NULL, "uart1_ick", &uart1_ick, CK_243X),
CLK(NULL, "uart1_fck", &uart1_fck, CK_243X), CLK(NULL, "uart1_fck", &uart1_fck, CK_243X),
CLK(NULL, "uart2_ick", &uart2_ick, CK_243X), CLK(NULL, "uart2_ick", &uart2_ick, CK_243X),
@@ -1958,7 +1958,7 @@ static struct omap_clk omap2430_clks[] = {
CLK(NULL, "gpios_ick", &gpios_ick, CK_243X), CLK(NULL, "gpios_ick", &gpios_ick, CK_243X),
CLK(NULL, "gpios_fck", &gpios_fck, CK_243X), CLK(NULL, "gpios_fck", &gpios_fck, CK_243X),
CLK("omap_wdt", "ick", &mpu_wdt_ick, CK_243X), CLK("omap_wdt", "ick", &mpu_wdt_ick, CK_243X),
CLK("omap_wdt", "fck", &mpu_wdt_fck, CK_243X), CLK(NULL, "mpu_wdt_fck", &mpu_wdt_fck, CK_243X),
CLK(NULL, "sync_32k_ick", &sync_32k_ick, CK_243X), CLK(NULL, "sync_32k_ick", &sync_32k_ick, CK_243X),
CLK(NULL, "wdt1_ick", &wdt1_ick, CK_243X), CLK(NULL, "wdt1_ick", &wdt1_ick, CK_243X),
CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_243X), CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_243X),
@@ -1975,9 +1975,9 @@ static struct omap_clk omap2430_clks[] = {
CLK("omap_hdq.0", "ick", &hdq_ick, CK_243X), CLK("omap_hdq.0", "ick", &hdq_ick, CK_243X),
CLK("omap_hdq.1", "fck", &hdq_fck, CK_243X), CLK("omap_hdq.1", "fck", &hdq_fck, CK_243X),
CLK("omap_i2c.1", "ick", &i2c1_ick, CK_243X), CLK("omap_i2c.1", "ick", &i2c1_ick, CK_243X),
CLK("omap_i2c.1", "fck", &i2chs1_fck, CK_243X), CLK(NULL, "i2chs1_fck", &i2chs1_fck, CK_243X),
CLK("omap_i2c.2", "ick", &i2c2_ick, CK_243X), CLK("omap_i2c.2", "ick", &i2c2_ick, CK_243X),
CLK("omap_i2c.2", "fck", &i2chs2_fck, CK_243X), CLK(NULL, "i2chs2_fck", &i2chs2_fck, CK_243X),
CLK(NULL, "gpmc_fck", &gpmc_fck, CK_243X), CLK(NULL, "gpmc_fck", &gpmc_fck, CK_243X),
CLK(NULL, "sdma_fck", &sdma_fck, CK_243X), CLK(NULL, "sdma_fck", &sdma_fck, CK_243X),
CLK(NULL, "sdma_ick", &sdma_ick, CK_243X), CLK(NULL, "sdma_ick", &sdma_ick, CK_243X),
@@ -1990,9 +1990,9 @@ static struct omap_clk omap2430_clks[] = {
CLK(NULL, "usb_fck", &usb_fck, CK_243X), CLK(NULL, "usb_fck", &usb_fck, CK_243X),
CLK("musb-omap2430", "ick", &usbhs_ick, CK_243X), CLK("musb-omap2430", "ick", &usbhs_ick, CK_243X),
CLK("omap_hsmmc.0", "ick", &mmchs1_ick, CK_243X), CLK("omap_hsmmc.0", "ick", &mmchs1_ick, CK_243X),
CLK("omap_hsmmc.0", "fck", &mmchs1_fck, CK_243X), CLK(NULL, "mmchs1_fck", &mmchs1_fck, CK_243X),
CLK("omap_hsmmc.1", "ick", &mmchs2_ick, CK_243X), CLK("omap_hsmmc.1", "ick", &mmchs2_ick, CK_243X),
CLK("omap_hsmmc.1", "fck", &mmchs2_fck, CK_243X), CLK(NULL, "mmchs2_fck", &mmchs2_fck, CK_243X),
CLK(NULL, "gpio5_ick", &gpio5_ick, CK_243X), CLK(NULL, "gpio5_ick", &gpio5_ick, CK_243X),
CLK(NULL, "gpio5_fck", &gpio5_fck, CK_243X), CLK(NULL, "gpio5_fck", &gpio5_fck, CK_243X),
CLK(NULL, "mdm_intc_ick", &mdm_intc_ick, CK_243X), CLK(NULL, "mdm_intc_ick", &mdm_intc_ick, CK_243X),

View File

@@ -3289,25 +3289,25 @@ static struct omap_clk omap3xxx_clks[] = {
CLK("omap-mcbsp.1", "prcm_fck", &core_96m_fck, CK_3XXX), CLK("omap-mcbsp.1", "prcm_fck", &core_96m_fck, CK_3XXX),
CLK("omap-mcbsp.5", "prcm_fck", &core_96m_fck, CK_3XXX), CLK("omap-mcbsp.5", "prcm_fck", &core_96m_fck, CK_3XXX),
CLK(NULL, "core_96m_fck", &core_96m_fck, CK_3XXX), CLK(NULL, "core_96m_fck", &core_96m_fck, CK_3XXX),
CLK("omap_hsmmc.2", "fck", &mmchs3_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), CLK(NULL, "mmchs3_fck", &mmchs3_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK("omap_hsmmc.1", "fck", &mmchs2_fck, CK_3XXX), CLK(NULL, "mmchs2_fck", &mmchs2_fck, CK_3XXX),
CLK(NULL, "mspro_fck", &mspro_fck, CK_34XX | CK_36XX), CLK(NULL, "mspro_fck", &mspro_fck, CK_34XX | CK_36XX),
CLK("omap_hsmmc.0", "fck", &mmchs1_fck, CK_3XXX), CLK(NULL, "mmchs1_fck", &mmchs1_fck, CK_3XXX),
CLK("omap_i2c.3", "fck", &i2c3_fck, CK_3XXX), CLK(NULL, "i2c3_fck", &i2c3_fck, CK_3XXX),
CLK("omap_i2c.2", "fck", &i2c2_fck, CK_3XXX), CLK(NULL, "i2c2_fck", &i2c2_fck, CK_3XXX),
CLK("omap_i2c.1", "fck", &i2c1_fck, CK_3XXX), CLK(NULL, "i2c1_fck", &i2c1_fck, CK_3XXX),
CLK("omap-mcbsp.5", "fck", &mcbsp5_fck, CK_3XXX), CLK(NULL, "mcbsp5_fck", &mcbsp5_fck, CK_3XXX),
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_3XXX), CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_3XXX),
CLK(NULL, "core_48m_fck", &core_48m_fck, CK_3XXX), CLK(NULL, "core_48m_fck", &core_48m_fck, CK_3XXX),
CLK("omap2_mcspi.4", "fck", &mcspi4_fck, CK_3XXX), CLK(NULL, "mcspi4_fck", &mcspi4_fck, CK_3XXX),
CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_3XXX), CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_3XXX),
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_3XXX), CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_3XXX),
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_3XXX), CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_3XXX),
CLK(NULL, "uart2_fck", &uart2_fck, CK_3XXX), CLK(NULL, "uart2_fck", &uart2_fck, CK_3XXX),
CLK(NULL, "uart1_fck", &uart1_fck, CK_3XXX), CLK(NULL, "uart1_fck", &uart1_fck, CK_3XXX),
CLK(NULL, "fshostusb_fck", &fshostusb_fck, CK_3430ES1), CLK(NULL, "fshostusb_fck", &fshostusb_fck, CK_3430ES1),
CLK(NULL, "core_12m_fck", &core_12m_fck, CK_3XXX), 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_3430es1, CK_3430ES1),
CLK(NULL, "ssi_ssr_fck", &ssi_ssr_fck_3430es2, CK_3430ES2PLUS | CK_36XX), CLK(NULL, "ssi_ssr_fck", &ssi_ssr_fck_3430es2, CK_3430ES2PLUS | CK_36XX),
CLK(NULL, "ssi_sst_fck", &ssi_sst_fck_3430es1, CK_3430ES1), 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("omap_rng", "ick", &rng_ick, CK_34XX | CK_36XX),
CLK(NULL, "sha11_ick", &sha11_ick, CK_34XX | CK_36XX), CLK(NULL, "sha11_ick", &sha11_ick, CK_34XX | CK_36XX),
CLK(NULL, "des1_ick", &des1_ick, CK_34XX | CK_36XX), CLK(NULL, "des1_ick", &des1_ick, CK_34XX | CK_36XX),
CLK("omapdss_dss", "fck", &dss1_alwon_fck_3430es1, CK_3430ES1), CLK(NULL, "dss1_alwon_fck", &dss1_alwon_fck_3430es1, CK_3430ES1),
CLK("omapdss_dss", "fck", &dss1_alwon_fck_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), CLK(NULL, "dss1_alwon_fck", &dss1_alwon_fck_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK("omapdss_dss", "tv_clk", &dss_tv_fck, CK_3XXX), CLK(NULL, "dss_tv_fck", &dss_tv_fck, CK_3XXX),
CLK("omapdss_dss", "video_clk", &dss_96m_fck, CK_3XXX), CLK(NULL, "dss_96m_fck", &dss_96m_fck, CK_3XXX),
CLK("omapdss_dss", "sys_clk", &dss2_alwon_fck, CK_3XXX), CLK(NULL, "dss2_alwon_fck", &dss2_alwon_fck, CK_3XXX),
CLK("omapdss_dss", "ick", &dss_ick_3430es1, CK_3430ES1), CLK("omapdss_dss", "ick", &dss_ick_3430es1, CK_3430ES1),
CLK("omapdss_dss", "ick", &dss_ick_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), CLK("omapdss_dss", "ick", &dss_ick_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK(NULL, "cam_mclk", &cam_mclk, CK_34XX | CK_36XX), CLK(NULL, "cam_mclk", &cam_mclk, CK_34XX | CK_36XX),
@@ -3385,7 +3385,7 @@ static struct omap_clk omap3xxx_clks[] = {
CLK(NULL, "gpt1_fck", &gpt1_fck, CK_3XXX), CLK(NULL, "gpt1_fck", &gpt1_fck, CK_3XXX),
CLK(NULL, "wkup_32k_fck", &wkup_32k_fck, CK_3XXX), CLK(NULL, "wkup_32k_fck", &wkup_32k_fck, CK_3XXX),
CLK(NULL, "gpio1_dbck", &gpio1_dbck, CK_3XXX), CLK(NULL, "gpio1_dbck", &gpio1_dbck, CK_3XXX),
CLK("omap_wdt", "fck", &wdt2_fck, CK_3XXX), CLK(NULL, "wdt2_fck", &wdt2_fck, CK_3XXX),
CLK(NULL, "wkup_l4_ick", &wkup_l4_ick, CK_34XX | CK_36XX), CLK(NULL, "wkup_l4_ick", &wkup_l4_ick, CK_34XX | CK_36XX),
CLK(NULL, "usim_ick", &usim_ick, CK_3430ES2PLUS | CK_36XX), CLK(NULL, "usim_ick", &usim_ick, CK_3430ES2PLUS | CK_36XX),
CLK("omap_wdt", "ick", &wdt2_ick, CK_3XXX), CLK("omap_wdt", "ick", &wdt2_ick, CK_3XXX),
@@ -3436,9 +3436,9 @@ static struct omap_clk omap3xxx_clks[] = {
CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_3XXX), CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_3XXX),
CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_3XXX), CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_3XXX),
CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_3XXX), CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_3XXX),
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_3XXX), CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_3XXX),
CLK("omap-mcbsp.3", "fck", &mcbsp3_fck, CK_3XXX), CLK(NULL, "mcbsp3_fck", &mcbsp3_fck, CK_3XXX),
CLK("omap-mcbsp.4", "fck", &mcbsp4_fck, CK_3XXX), CLK(NULL, "mcbsp4_fck", &mcbsp4_fck, CK_3XXX),
CLK("etb", "emu_src_ck", &emu_src_ck, CK_3XXX), CLK("etb", "emu_src_ck", &emu_src_ck, CK_3XXX),
CLK(NULL, "pclk_fck", &pclk_fck, CK_3XXX), CLK(NULL, "pclk_fck", &pclk_fck, CK_3XXX),
CLK(NULL, "pclkx2_fck", &pclkx2_fck, CK_3XXX), CLK(NULL, "pclkx2_fck", &pclkx2_fck, CK_3XXX),

View File

@@ -2808,19 +2808,39 @@ static struct clk trace_clk_div_ck = {
/* SCRM aux clk nodes */ /* SCRM aux clk nodes */
static const struct clksel auxclk_sel[] = { static const struct clksel auxclk_src_sel[] = {
{ .parent = &sys_clkin_ck, .rates = div_1_0_rates }, { .parent = &sys_clkin_ck, .rates = div_1_0_rates },
{ .parent = &dpll_core_m3x2_ck, .rates = div_1_1_rates }, { .parent = &dpll_core_m3x2_ck, .rates = div_1_1_rates },
{ .parent = &dpll_per_m3x2_ck, .rates = div_1_2_rates }, { .parent = &dpll_per_m3x2_ck, .rates = div_1_2_rates },
{ .parent = NULL }, { .parent = NULL },
}; };
static struct clk auxclk0_ck = { static const struct clksel_rate div16_1to16_rates[] = {
.name = "auxclk0_ck", { .div = 1, .val = 0, .flags = RATE_IN_4430 },
{ .div = 2, .val = 1, .flags = RATE_IN_4430 },
{ .div = 3, .val = 2, .flags = RATE_IN_4430 },
{ .div = 4, .val = 3, .flags = RATE_IN_4430 },
{ .div = 5, .val = 4, .flags = RATE_IN_4430 },
{ .div = 6, .val = 5, .flags = RATE_IN_4430 },
{ .div = 7, .val = 6, .flags = RATE_IN_4430 },
{ .div = 8, .val = 7, .flags = RATE_IN_4430 },
{ .div = 9, .val = 8, .flags = RATE_IN_4430 },
{ .div = 10, .val = 9, .flags = RATE_IN_4430 },
{ .div = 11, .val = 10, .flags = RATE_IN_4430 },
{ .div = 12, .val = 11, .flags = RATE_IN_4430 },
{ .div = 13, .val = 12, .flags = RATE_IN_4430 },
{ .div = 14, .val = 13, .flags = RATE_IN_4430 },
{ .div = 15, .val = 14, .flags = RATE_IN_4430 },
{ .div = 16, .val = 15, .flags = RATE_IN_4430 },
{ .div = 0 },
};
static struct clk auxclk0_src_ck = {
.name = "auxclk0_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK0, .clksel_reg = OMAP4_SCRM_AUXCLK0,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
@@ -2828,12 +2848,29 @@ static struct clk auxclk0_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static struct clk auxclk1_ck = { static const struct clksel auxclk0_sel[] = {
.name = "auxclk1_ck", { .parent = &auxclk0_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk0_ck = {
.name = "auxclk0_ck",
.parent = &auxclk0_src_ck,
.clksel = auxclk0_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK0,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static struct clk auxclk1_src_ck = {
.name = "auxclk1_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK1, .clksel_reg = OMAP4_SCRM_AUXCLK1,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
@@ -2841,12 +2878,29 @@ static struct clk auxclk1_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static struct clk auxclk2_ck = { static const struct clksel auxclk1_sel[] = {
.name = "auxclk2_ck", { .parent = &auxclk1_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk1_ck = {
.name = "auxclk1_ck",
.parent = &auxclk1_src_ck,
.clksel = auxclk1_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK1,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static struct clk auxclk2_src_ck = {
.name = "auxclk2_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK2, .clksel_reg = OMAP4_SCRM_AUXCLK2,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
@@ -2854,12 +2908,29 @@ static struct clk auxclk2_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static struct clk auxclk3_ck = { static const struct clksel auxclk2_sel[] = {
.name = "auxclk3_ck", { .parent = &auxclk2_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk2_ck = {
.name = "auxclk2_ck",
.parent = &auxclk2_src_ck,
.clksel = auxclk2_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK2,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static struct clk auxclk3_src_ck = {
.name = "auxclk3_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK3, .clksel_reg = OMAP4_SCRM_AUXCLK3,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
@@ -2867,12 +2938,29 @@ static struct clk auxclk3_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static struct clk auxclk4_ck = { static const struct clksel auxclk3_sel[] = {
.name = "auxclk4_ck", { .parent = &auxclk3_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk3_ck = {
.name = "auxclk3_ck",
.parent = &auxclk3_src_ck,
.clksel = auxclk3_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK3,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static struct clk auxclk4_src_ck = {
.name = "auxclk4_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK4, .clksel_reg = OMAP4_SCRM_AUXCLK4,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
@@ -2880,12 +2968,29 @@ static struct clk auxclk4_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static struct clk auxclk5_ck = { static const struct clksel auxclk4_sel[] = {
.name = "auxclk5_ck", { .parent = &auxclk4_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk4_ck = {
.name = "auxclk4_ck",
.parent = &auxclk4_src_ck,
.clksel = auxclk4_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK4,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static struct clk auxclk5_src_ck = {
.name = "auxclk5_src_ck",
.parent = &sys_clkin_ck, .parent = &sys_clkin_ck,
.init = &omap2_init_clksel_parent, .init = &omap2_init_clksel_parent,
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
.clksel = auxclk_sel, .clksel = auxclk_src_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK5, .clksel_reg = OMAP4_SCRM_AUXCLK5,
.clksel_mask = OMAP4_SRCSELECT_MASK, .clksel_mask = OMAP4_SRCSELECT_MASK,
.recalc = &omap2_clksel_recalc, .recalc = &omap2_clksel_recalc,
@@ -2893,6 +2998,23 @@ static struct clk auxclk5_ck = {
.enable_bit = OMAP4_ENABLE_SHIFT, .enable_bit = OMAP4_ENABLE_SHIFT,
}; };
static const struct clksel auxclk5_sel[] = {
{ .parent = &auxclk5_src_ck, .rates = div16_1to16_rates },
{ .parent = NULL },
};
static struct clk auxclk5_ck = {
.name = "auxclk5_ck",
.parent = &auxclk5_src_ck,
.clksel = auxclk5_sel,
.clksel_reg = OMAP4_SCRM_AUXCLK5,
.clksel_mask = OMAP4_CLKDIV_MASK,
.ops = &clkops_null,
.recalc = &omap2_clksel_recalc,
.round_rate = &omap2_clksel_round_rate,
.set_rate = &omap2_clksel_set_rate,
};
static const struct clksel auxclkreq_sel[] = { static const struct clksel auxclkreq_sel[] = {
{ .parent = &auxclk0_ck, .rates = div_1_0_rates }, { .parent = &auxclk0_ck, .rates = div_1_0_rates },
{ .parent = &auxclk1_ck, .rates = div_1_1_rates }, { .parent = &auxclk1_ck, .rates = div_1_1_rates },
@@ -3093,12 +3215,12 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "gpio6_ick", &gpio6_ick, CK_443X), CLK(NULL, "gpio6_ick", &gpio6_ick, CK_443X),
CLK(NULL, "gpmc_ick", &gpmc_ick, CK_443X), CLK(NULL, "gpmc_ick", &gpmc_ick, CK_443X),
CLK(NULL, "gpu_fck", &gpu_fck, CK_443X), CLK(NULL, "gpu_fck", &gpu_fck, CK_443X),
CLK("omap2_hdq.0", "fck", &hdq1w_fck, CK_443X), CLK(NULL, "hdq1w_fck", &hdq1w_fck, CK_443X),
CLK(NULL, "hsi_fck", &hsi_fck, CK_443X), CLK(NULL, "hsi_fck", &hsi_fck, CK_443X),
CLK("omap_i2c.1", "fck", &i2c1_fck, CK_443X), CLK(NULL, "i2c1_fck", &i2c1_fck, CK_443X),
CLK("omap_i2c.2", "fck", &i2c2_fck, CK_443X), CLK(NULL, "i2c2_fck", &i2c2_fck, CK_443X),
CLK("omap_i2c.3", "fck", &i2c3_fck, CK_443X), CLK(NULL, "i2c3_fck", &i2c3_fck, CK_443X),
CLK("omap_i2c.4", "fck", &i2c4_fck, CK_443X), CLK(NULL, "i2c4_fck", &i2c4_fck, CK_443X),
CLK(NULL, "ipu_fck", &ipu_fck, CK_443X), CLK(NULL, "ipu_fck", &ipu_fck, CK_443X),
CLK(NULL, "iss_ctrlclk", &iss_ctrlclk, CK_443X), CLK(NULL, "iss_ctrlclk", &iss_ctrlclk, CK_443X),
CLK(NULL, "iss_fck", &iss_fck, CK_443X), CLK(NULL, "iss_fck", &iss_fck, CK_443X),
@@ -3109,23 +3231,23 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "mcasp_sync_mux_ck", &mcasp_sync_mux_ck, CK_443X), CLK(NULL, "mcasp_sync_mux_ck", &mcasp_sync_mux_ck, CK_443X),
CLK(NULL, "mcasp_fck", &mcasp_fck, CK_443X), CLK(NULL, "mcasp_fck", &mcasp_fck, CK_443X),
CLK(NULL, "mcbsp1_sync_mux_ck", &mcbsp1_sync_mux_ck, CK_443X), CLK(NULL, "mcbsp1_sync_mux_ck", &mcbsp1_sync_mux_ck, CK_443X),
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_443X), CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_443X),
CLK(NULL, "mcbsp2_sync_mux_ck", &mcbsp2_sync_mux_ck, CK_443X), CLK(NULL, "mcbsp2_sync_mux_ck", &mcbsp2_sync_mux_ck, CK_443X),
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_443X), CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_443X),
CLK(NULL, "mcbsp3_sync_mux_ck", &mcbsp3_sync_mux_ck, CK_443X), CLK(NULL, "mcbsp3_sync_mux_ck", &mcbsp3_sync_mux_ck, CK_443X),
CLK("omap-mcbsp.3", "fck", &mcbsp3_fck, CK_443X), CLK(NULL, "mcbsp3_fck", &mcbsp3_fck, CK_443X),
CLK(NULL, "mcbsp4_sync_mux_ck", &mcbsp4_sync_mux_ck, CK_443X), CLK(NULL, "mcbsp4_sync_mux_ck", &mcbsp4_sync_mux_ck, CK_443X),
CLK("omap-mcbsp.4", "fck", &mcbsp4_fck, CK_443X), CLK(NULL, "mcbsp4_fck", &mcbsp4_fck, CK_443X),
CLK(NULL, "mcpdm_fck", &mcpdm_fck, CK_443X), CLK(NULL, "mcpdm_fck", &mcpdm_fck, CK_443X),
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_443X), CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_443X),
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_443X), CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_443X),
CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_443X), CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_443X),
CLK("omap2_mcspi.4", "fck", &mcspi4_fck, CK_443X), CLK(NULL, "mcspi4_fck", &mcspi4_fck, CK_443X),
CLK("omap_hsmmc.0", "fck", &mmc1_fck, CK_443X), CLK(NULL, "mmc1_fck", &mmc1_fck, CK_443X),
CLK("omap_hsmmc.1", "fck", &mmc2_fck, CK_443X), CLK(NULL, "mmc2_fck", &mmc2_fck, CK_443X),
CLK("omap_hsmmc.2", "fck", &mmc3_fck, CK_443X), CLK(NULL, "mmc3_fck", &mmc3_fck, CK_443X),
CLK("omap_hsmmc.3", "fck", &mmc4_fck, CK_443X), CLK(NULL, "mmc4_fck", &mmc4_fck, CK_443X),
CLK("omap_hsmmc.4", "fck", &mmc5_fck, CK_443X), CLK(NULL, "mmc5_fck", &mmc5_fck, CK_443X),
CLK(NULL, "ocp2scp_usb_phy_phy_48m", &ocp2scp_usb_phy_phy_48m, CK_443X), CLK(NULL, "ocp2scp_usb_phy_phy_48m", &ocp2scp_usb_phy_phy_48m, CK_443X),
CLK(NULL, "ocp2scp_usb_phy_ick", &ocp2scp_usb_phy_ick, CK_443X), CLK(NULL, "ocp2scp_usb_phy_ick", &ocp2scp_usb_phy_ick, CK_443X),
CLK(NULL, "ocp_wp_noc_ick", &ocp_wp_noc_ick, CK_443X), CLK(NULL, "ocp_wp_noc_ick", &ocp_wp_noc_ick, CK_443X),
@@ -3182,21 +3304,27 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "usim_ck", &usim_ck, CK_443X), CLK(NULL, "usim_ck", &usim_ck, CK_443X),
CLK(NULL, "usim_fclk", &usim_fclk, CK_443X), CLK(NULL, "usim_fclk", &usim_fclk, CK_443X),
CLK(NULL, "usim_fck", &usim_fck, CK_443X), CLK(NULL, "usim_fck", &usim_fck, CK_443X),
CLK("omap_wdt", "fck", &wd_timer2_fck, CK_443X), CLK(NULL, "wd_timer2_fck", &wd_timer2_fck, CK_443X),
CLK(NULL, "wd_timer3_fck", &wd_timer3_fck, CK_443X), CLK(NULL, "wd_timer3_fck", &wd_timer3_fck, CK_443X),
CLK(NULL, "stm_clk_div_ck", &stm_clk_div_ck, CK_443X), CLK(NULL, "stm_clk_div_ck", &stm_clk_div_ck, CK_443X),
CLK(NULL, "trace_clk_div_ck", &trace_clk_div_ck, CK_443X), CLK(NULL, "trace_clk_div_ck", &trace_clk_div_ck, CK_443X),
CLK(NULL, "auxclk0_src_ck", &auxclk0_src_ck, CK_443X),
CLK(NULL, "auxclk0_ck", &auxclk0_ck, CK_443X), CLK(NULL, "auxclk0_ck", &auxclk0_ck, CK_443X),
CLK(NULL, "auxclk1_ck", &auxclk1_ck, CK_443X),
CLK(NULL, "auxclk2_ck", &auxclk2_ck, CK_443X),
CLK(NULL, "auxclk3_ck", &auxclk3_ck, CK_443X),
CLK(NULL, "auxclk4_ck", &auxclk4_ck, CK_443X),
CLK(NULL, "auxclk5_ck", &auxclk5_ck, CK_443X),
CLK(NULL, "auxclkreq0_ck", &auxclkreq0_ck, CK_443X), CLK(NULL, "auxclkreq0_ck", &auxclkreq0_ck, CK_443X),
CLK(NULL, "auxclk1_src_ck", &auxclk1_src_ck, CK_443X),
CLK(NULL, "auxclk1_ck", &auxclk1_ck, CK_443X),
CLK(NULL, "auxclkreq1_ck", &auxclkreq1_ck, CK_443X), CLK(NULL, "auxclkreq1_ck", &auxclkreq1_ck, CK_443X),
CLK(NULL, "auxclk2_src_ck", &auxclk2_src_ck, CK_443X),
CLK(NULL, "auxclk2_ck", &auxclk2_ck, CK_443X),
CLK(NULL, "auxclkreq2_ck", &auxclkreq2_ck, CK_443X), CLK(NULL, "auxclkreq2_ck", &auxclkreq2_ck, CK_443X),
CLK(NULL, "auxclk3_src_ck", &auxclk3_src_ck, CK_443X),
CLK(NULL, "auxclk3_ck", &auxclk3_ck, CK_443X),
CLK(NULL, "auxclkreq3_ck", &auxclkreq3_ck, CK_443X), CLK(NULL, "auxclkreq3_ck", &auxclkreq3_ck, CK_443X),
CLK(NULL, "auxclk4_src_ck", &auxclk4_src_ck, CK_443X),
CLK(NULL, "auxclk4_ck", &auxclk4_ck, CK_443X),
CLK(NULL, "auxclkreq4_ck", &auxclkreq4_ck, CK_443X), CLK(NULL, "auxclkreq4_ck", &auxclkreq4_ck, CK_443X),
CLK(NULL, "auxclk5_src_ck", &auxclk5_src_ck, CK_443X),
CLK(NULL, "auxclk5_ck", &auxclk5_ck, CK_443X),
CLK(NULL, "auxclkreq5_ck", &auxclkreq5_ck, CK_443X), CLK(NULL, "auxclkreq5_ck", &auxclkreq5_ck, CK_443X),
CLK(NULL, "gpmc_ck", &dummy_ck, CK_443X), CLK(NULL, "gpmc_ck", &dummy_ck, CK_443X),
CLK(NULL, "gpt1_ick", &dummy_ck, CK_443X), CLK(NULL, "gpt1_ick", &dummy_ck, CK_443X),
@@ -3251,6 +3379,7 @@ int __init omap4xxx_clk_init(void)
} }
clk_init(&omap2_clk_functions); clk_init(&omap2_clk_functions);
omap2_clk_disable_clkdm_control();
for (c = omap44xx_clks; c < omap44xx_clks + ARRAY_SIZE(omap44xx_clks); for (c = omap44xx_clks; c < omap44xx_clks + ARRAY_SIZE(omap44xx_clks);
c++) c++)

View File

@@ -1,8 +1,8 @@
/* /*
* OMAP2/3/4 clockdomain framework functions * OMAP2/3/4 clockdomain framework functions
* *
* Copyright (C) 2008-2010 Texas Instruments, Inc. * Copyright (C) 2008-2011 Texas Instruments, Inc.
* Copyright (C) 2008-2010 Nokia Corporation * Copyright (C) 2008-2011 Nokia Corporation
* *
* Written by Paul Walmsley and Jouni Högander * Written by Paul Walmsley and Jouni Högander
* Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com> * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
@@ -92,6 +92,8 @@ static int _clkdm_register(struct clockdomain *clkdm)
pwrdm_add_clkdm(pwrdm, clkdm); pwrdm_add_clkdm(pwrdm, clkdm);
spin_lock_init(&clkdm->lock);
pr_debug("clockdomain: registered %s\n", clkdm->name); pr_debug("clockdomain: registered %s\n", clkdm->name);
return 0; return 0;
@@ -690,6 +692,9 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
*/ */
int clkdm_sleep(struct clockdomain *clkdm) int clkdm_sleep(struct clockdomain *clkdm)
{ {
int ret;
unsigned long flags;
if (!clkdm) if (!clkdm)
return -EINVAL; return -EINVAL;
@@ -704,7 +709,11 @@ int clkdm_sleep(struct clockdomain *clkdm)
pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name); pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
return arch_clkdm->clkdm_sleep(clkdm); spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
ret = arch_clkdm->clkdm_sleep(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags);
return ret;
} }
/** /**
@@ -718,6 +727,9 @@ int clkdm_sleep(struct clockdomain *clkdm)
*/ */
int clkdm_wakeup(struct clockdomain *clkdm) int clkdm_wakeup(struct clockdomain *clkdm)
{ {
int ret;
unsigned long flags;
if (!clkdm) if (!clkdm)
return -EINVAL; return -EINVAL;
@@ -732,7 +744,11 @@ int clkdm_wakeup(struct clockdomain *clkdm)
pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name); pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
return arch_clkdm->clkdm_wakeup(clkdm); spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
ret = arch_clkdm->clkdm_wakeup(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags);
return ret;
} }
/** /**
@@ -747,6 +763,8 @@ int clkdm_wakeup(struct clockdomain *clkdm)
*/ */
void clkdm_allow_idle(struct clockdomain *clkdm) void clkdm_allow_idle(struct clockdomain *clkdm)
{ {
unsigned long flags;
if (!clkdm) if (!clkdm)
return; return;
@@ -762,8 +780,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm)
pr_debug("clockdomain: enabling automatic idle transitions for %s\n", pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
clkdm->name); clkdm->name);
spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED;
arch_clkdm->clkdm_allow_idle(clkdm); arch_clkdm->clkdm_allow_idle(clkdm);
pwrdm_clkdm_state_switch(clkdm); pwrdm_clkdm_state_switch(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags);
} }
/** /**
@@ -777,6 +798,8 @@ void clkdm_allow_idle(struct clockdomain *clkdm)
*/ */
void clkdm_deny_idle(struct clockdomain *clkdm) void clkdm_deny_idle(struct clockdomain *clkdm)
{ {
unsigned long flags;
if (!clkdm) if (!clkdm)
return; return;
@@ -792,11 +815,90 @@ void clkdm_deny_idle(struct clockdomain *clkdm)
pr_debug("clockdomain: disabling automatic idle transitions for %s\n", pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
clkdm->name); clkdm->name);
spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
arch_clkdm->clkdm_deny_idle(clkdm); arch_clkdm->clkdm_deny_idle(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags);
} }
/**
* clkdm_in_hwsup - is clockdomain @clkdm have hardware-supervised idle enabled?
* @clkdm: struct clockdomain *
*
* Returns true if clockdomain @clkdm currently has
* hardware-supervised idle enabled, or false if it does not or if
* @clkdm is NULL. It is only valid to call this function after
* clkdm_init() has been called. This function does not actually read
* bits from the hardware; it instead tests an in-memory flag that is
* changed whenever the clockdomain code changes the auto-idle mode.
*/
bool clkdm_in_hwsup(struct clockdomain *clkdm)
{
bool ret;
unsigned long flags;
/* Clockdomain-to-clock framework interface code */ 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);
pr_debug("clockdomain: clkdm %s: disabled\n", clkdm->name);
return 0;
}
/** /**
* clkdm_clk_enable - add an enabled downstream clock to this clkdm * clkdm_clk_enable - add an enabled downstream clock to this clkdm
@@ -819,25 +921,10 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
* downstream clocks for debugging purposes? * downstream clocks for debugging purposes?
*/ */
if (!clkdm || !clk) if (!clk)
return -EINVAL; return -EINVAL;
if (!arch_clkdm || !arch_clkdm->clkdm_clk_enable) return _clkdm_clk_hwmod_enable(clkdm);
return -EINVAL;
if (atomic_inc_return(&clkdm->usecount) > 1)
return 0;
/* Clockdomain now has one enabled downstream clock */
pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name,
clk->name);
arch_clkdm->clkdm_clk_enable(clkdm);
pwrdm_wait_transition(clkdm->pwrdm.ptr);
pwrdm_clkdm_state_switch(clkdm);
return 0;
} }
/** /**
@@ -850,9 +937,8 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
* clockdomain usecount goes to 0, put the clockdomain to sleep * clockdomain usecount goes to 0, put the clockdomain to sleep
* (software-supervised mode) or remove the clkdm autodependencies * (software-supervised mode) or remove the clkdm autodependencies
* (hardware-supervised mode). Returns -EINVAL if passed null * (hardware-supervised mode). Returns -EINVAL if passed null
* pointers; -ERANGE if the @clkdm usecount underflows and debugging * pointers; -ERANGE if the @clkdm usecount underflows; or returns 0
* is enabled; or returns 0 upon success or if the clockdomain is in * upon success or if the clockdomain is in hwsup idle mode.
* hwsup idle mode.
*/ */
int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
{ {
@@ -861,30 +947,72 @@ int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
* downstream clocks for debugging purposes? * downstream clocks for debugging purposes?
*/ */
if (!clkdm || !clk) if (!clk)
return -EINVAL; return -EINVAL;
if (!arch_clkdm || !arch_clkdm->clkdm_clk_disable) return _clkdm_clk_hwmod_disable(clkdm);
return -EINVAL; }
#ifdef DEBUG /**
if (atomic_read(&clkdm->usecount) == 0) { * clkdm_hwmod_enable - add an enabled downstream hwmod to this clkdm
WARN_ON(1); /* underflow */ * @clkdm: struct clockdomain *
return -ERANGE; * @oh: struct omap_hwmod * of the enabled downstream hwmod
} *
#endif * Increment the usecount of the clockdomain @clkdm and ensure that it
* is awake before @oh is enabled. Intended to be called by
if (atomic_dec_return(&clkdm->usecount) > 0) * module_enable() code.
return 0; * If the clockdomain is in software-supervised idle mode, force the
* clockdomain to wake. If the clockdomain is in hardware-supervised idle
/* All downstream clocks of this clockdomain are now disabled */ * mode, add clkdm-pwrdm autodependencies, to ensure that devices in the
* clockdomain can be read from/written to by on-chip processors.
pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name, * Returns -EINVAL if passed null pointers;
clk->name); * returns 0 upon success or if the clockdomain is in hwsup idle mode.
*/
arch_clkdm->clkdm_clk_disable(clkdm); int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh)
pwrdm_clkdm_state_switch(clkdm); {
/* The clkdm attribute does not exist yet prior OMAP4 */
return 0; 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;
return _clkdm_clk_hwmod_enable(clkdm);
}
/**
* clkdm_hwmod_disable - remove an enabled downstream hwmod from this clkdm
* @clkdm: struct clockdomain *
* @oh: struct omap_hwmod * of the disabled downstream hwmod
*
* Decrement the usecount of this clockdomain @clkdm when @oh is
* disabled. Intended to be called by module_disable() code.
* If the clockdomain usecount goes to 0, put the clockdomain to sleep
* (software-supervised mode) or remove the clkdm autodependencies
* (hardware-supervised mode).
* Returns -EINVAL if passed null pointers; -ERANGE if the @clkdm usecount
* underflows; or returns 0 upon success or if the clockdomain is in hwsup
* idle mode.
*/
int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
{
/* The clkdm attribute does not exist yet prior OMAP4 */
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return 0;
/*
* XXX Rewrite this code to maintain a list of enabled
* downstream hwmods for debugging purposes?
*/
if (!oh)
return -EINVAL;
return _clkdm_clk_hwmod_disable(clkdm);
} }

View File

@@ -17,9 +17,11 @@
#define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H #define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H
#include <linux/init.h> #include <linux/init.h>
#include <linux/spinlock.h>
#include "powerdomain.h" #include "powerdomain.h"
#include <plat/clock.h> #include <plat/clock.h>
#include <plat/omap_hwmod.h>
#include <plat/cpu.h> #include <plat/cpu.h>
/* /*
@@ -82,6 +84,9 @@ struct clkdm_dep {
const struct omap_chip_id omap_chip; const struct omap_chip_id omap_chip;
}; };
/* Possible flags for struct clockdomain._flags */
#define _CLKDM_FLAG_HWSUP_ENABLED BIT(0)
/** /**
* struct clockdomain - OMAP clockdomain * struct clockdomain - OMAP clockdomain
* @name: clockdomain name * @name: clockdomain name
@@ -89,6 +94,7 @@ struct clkdm_dep {
* @clktrctrl_reg: CLKSTCTRL reg for the given clock domain * @clktrctrl_reg: CLKSTCTRL reg for the given clock domain
* @clktrctrl_mask: CLKTRCTRL/AUTOSTATE field mask in CM_CLKSTCTRL reg * @clktrctrl_mask: CLKTRCTRL/AUTOSTATE field mask in CM_CLKSTCTRL reg
* @flags: Clockdomain capability flags * @flags: Clockdomain capability flags
* @_flags: Flags for use only by internal clockdomain code
* @dep_bit: Bit shift of this clockdomain's PM_WKDEP/CM_SLEEPDEP bit * @dep_bit: Bit shift of this clockdomain's PM_WKDEP/CM_SLEEPDEP bit
* @prcm_partition: (OMAP4 only) PRCM partition ID for this clkdm's registers * @prcm_partition: (OMAP4 only) PRCM partition ID for this clkdm's registers
* @cm_inst: (OMAP4 only) CM instance register offset * @cm_inst: (OMAP4 only) CM instance register offset
@@ -113,6 +119,7 @@ struct clockdomain {
} pwrdm; } pwrdm;
const u16 clktrctrl_mask; const u16 clktrctrl_mask;
const u8 flags; const u8 flags;
u8 _flags;
const u8 dep_bit; const u8 dep_bit;
const u8 prcm_partition; const u8 prcm_partition;
const s16 cm_inst; const s16 cm_inst;
@@ -122,6 +129,7 @@ struct clockdomain {
const struct omap_chip_id omap_chip; const struct omap_chip_id omap_chip;
atomic_t usecount; atomic_t usecount;
struct list_head node; struct list_head node;
spinlock_t lock;
}; };
/** /**
@@ -177,12 +185,15 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm);
void clkdm_allow_idle(struct clockdomain *clkdm); void clkdm_allow_idle(struct clockdomain *clkdm);
void clkdm_deny_idle(struct clockdomain *clkdm); void clkdm_deny_idle(struct clockdomain *clkdm);
bool clkdm_in_hwsup(struct clockdomain *clkdm);
int clkdm_wakeup(struct clockdomain *clkdm); int clkdm_wakeup(struct clockdomain *clkdm);
int clkdm_sleep(struct clockdomain *clkdm); int clkdm_sleep(struct clockdomain *clkdm);
int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk); int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk);
int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk); int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk);
int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh);
int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh);
extern void __init omap2xxx_clockdomains_init(void); extern void __init omap2xxx_clockdomains_init(void);
extern void __init omap3xxx_clockdomains_init(void); extern void __init omap3xxx_clockdomains_init(void);

View File

@@ -183,7 +183,8 @@ static int omap2_clkdm_clk_enable(struct clockdomain *clkdm)
_clkdm_add_autodeps(clkdm); _clkdm_add_autodeps(clkdm);
_enable_hwsup(clkdm); _enable_hwsup(clkdm);
} else { } else {
clkdm_wakeup(clkdm); if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
omap2_clkdm_wakeup(clkdm);
} }
return 0; return 0;
@@ -205,7 +206,8 @@ static int omap2_clkdm_clk_disable(struct clockdomain *clkdm)
_clkdm_del_autodeps(clkdm); _clkdm_del_autodeps(clkdm);
_enable_hwsup(clkdm); _enable_hwsup(clkdm);
} else { } else {
clkdm_sleep(clkdm); if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
omap2_clkdm_sleep(clkdm);
} }
return 0; return 0;

View File

@@ -95,13 +95,8 @@ static void omap4_clkdm_deny_idle(struct clockdomain *clkdm)
static int omap4_clkdm_clk_enable(struct clockdomain *clkdm) static int omap4_clkdm_clk_enable(struct clockdomain *clkdm)
{ {
bool hwsup = false; if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
return omap4_clkdm_wakeup(clkdm);
hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition,
clkdm->cm_inst, clkdm->clkdm_offs);
if (!hwsup)
clkdm_wakeup(clkdm);
return 0; return 0;
} }
@@ -113,8 +108,8 @@ static int omap4_clkdm_clk_disable(struct clockdomain *clkdm)
hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition, hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition,
clkdm->cm_inst, clkdm->clkdm_offs); clkdm->cm_inst, clkdm->clkdm_offs);
if (!hwsup) if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
clkdm_sleep(clkdm); omap4_clkdm_sleep(clkdm);
return 0; return 0;
} }

View File

@@ -565,7 +565,7 @@ static struct clockdomain ducati_44xx_clkdm = {
}; };
static struct clockdomain mpu_44xx_clkdm = { static struct clockdomain mpu_44xx_clkdm = {
.name = "mpu_clkdm", .name = "mpuss_clkdm",
.pwrdm = { .name = "mpu_pwrdm" }, .pwrdm = { .name = "mpu_pwrdm" },
.prcm_partition = OMAP4430_CM1_PARTITION, .prcm_partition = OMAP4430_CM1_PARTITION,
.cm_inst = OMAP4430_CM1_MPU_INST, .cm_inst = OMAP4430_CM1_MPU_INST,

View File

@@ -1,7 +1,7 @@
/* /*
* OMAP4 Clock Management (CM) definitions * OMAP4 Clock Management (CM) definitions
* *
* Copyright (C) 2007-2009 Texas Instruments, Inc. * Copyright (C) 2007-2011 Texas Instruments, Inc.
* Copyright (C) 2007-2009 Nokia Corporation * Copyright (C) 2007-2009 Nokia Corporation
* *
* Written by Paul Walmsley * Written by Paul Walmsley
@@ -23,10 +23,4 @@
#define OMAP4_CM_CLKSTCTRL 0x0000 #define OMAP4_CM_CLKSTCTRL 0x0000
#define OMAP4_CM_STATICDEP 0x0004 #define OMAP4_CM_STATICDEP 0x0004
/* Function prototypes */
# ifndef __ASSEMBLER__
extern int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg);
# endif
#endif #endif

View File

@@ -2,6 +2,7 @@
* OMAP4 CM instance functions * OMAP4 CM instance functions
* *
* Copyright (C) 2009 Nokia Corporation * Copyright (C) 2009 Nokia Corporation
* Copyright (C) 2011 Texas Instruments, Inc.
* Paul Walmsley * Paul Walmsley
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -32,6 +33,22 @@
#include "prm44xx.h" #include "prm44xx.h"
#include "prcm_mpu44xx.h" #include "prcm_mpu44xx.h"
/*
* CLKCTRL_IDLEST_*: possible values for the CM_*_CLKCTRL.IDLEST bitfield:
*
* 0x0 func: Module is fully functional, including OCP
* 0x1 trans: Module is performing transition: wakeup, or sleep, or sleep
* abortion
* 0x2 idle: Module is in Idle mode (only OCP part). It is functional if
* using separate functional clock
* 0x3 disabled: Module is disabled and cannot be accessed
*
*/
#define CLKCTRL_IDLEST_FUNCTIONAL 0x0
#define CLKCTRL_IDLEST_INTRANSITION 0x1
#define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2
#define CLKCTRL_IDLEST_DISABLED 0x3
static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = { static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = {
[OMAP4430_INVALID_PRCM_PARTITION] = 0, [OMAP4430_INVALID_PRCM_PARTITION] = 0,
[OMAP4430_PRM_PARTITION] = OMAP4430_PRM_BASE, [OMAP4430_PRM_PARTITION] = OMAP4430_PRM_BASE,
@@ -41,6 +58,48 @@ static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = {
[OMAP4430_PRCM_MPU_PARTITION] = OMAP4430_PRCM_MPU_BASE, [OMAP4430_PRCM_MPU_PARTITION] = OMAP4430_PRCM_MPU_BASE,
}; };
/* Private functions */
/**
* _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
* @inst: CM instance register offset (*_INST macro)
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
*
* Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to
* bit 0.
*/
static u32 _clkctrl_idlest(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
{
u32 v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs);
v &= OMAP4430_IDLEST_MASK;
v >>= OMAP4430_IDLEST_SHIFT;
return v;
}
/**
* _is_module_ready - can module registers be accessed without causing an abort?
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
* @inst: CM instance register offset (*_INST macro)
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
*
* Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either
* *FUNCTIONAL or *INTERFACE_IDLE; false otherwise.
*/
static bool _is_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
{
u32 v;
v = _clkctrl_idlest(part, inst, cdoffs, clkctrl_offs);
return (v == CLKCTRL_IDLEST_FUNCTIONAL ||
v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false;
}
/* Public functions */
/* Read a register in a CM instance */ /* Read a register in a CM instance */
u32 omap4_cminst_read_inst_reg(u8 part, s16 inst, u16 idx) u32 omap4_cminst_read_inst_reg(u8 part, s16 inst, u16 idx)
{ {
@@ -200,36 +259,93 @@ void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs)
*/ */
/** /**
* omap4_cm_wait_module_ready - wait for a module to be in 'func' state * omap4_cminst_wait_module_ready - wait for a module to be in 'func' state
* @clkctrl_reg: CLKCTRL module address * @part: PRCM partition ID that the CM_CLKCTRL register exists in
* @inst: CM instance register offset (*_INST macro)
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
* *
* Wait for the module IDLEST to be functional. If the idle state is in any * Wait for the module IDLEST to be functional. If the idle state is in any
* the non functional state (trans, idle or disabled), module and thus the * the non functional state (trans, idle or disabled), module and thus the
* sysconfig cannot be accessed and will probably lead to an "imprecise * sysconfig cannot be accessed and will probably lead to an "imprecise
* external abort" * external abort"
*
* 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
*
*/ */
int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg) int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs,
u16 clkctrl_offs)
{ {
int i = 0; int i = 0;
if (!clkctrl_reg) if (!clkctrl_offs)
return 0; return 0;
omap_test_timeout(( omap_test_timeout(_is_module_ready(part, inst, cdoffs, clkctrl_offs),
((__raw_readl(clkctrl_reg) & OMAP4430_IDLEST_MASK) == 0) || MAX_MODULE_READY_TIME, i);
(((__raw_readl(clkctrl_reg) & OMAP4430_IDLEST_MASK) >>
OMAP4430_IDLEST_SHIFT) == 0x2)),
MAX_MODULE_READY_TIME, i);
return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; 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)
*
* 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_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
{
int i = 0;
if (!clkctrl_offs)
return 0;
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);
}

View File

@@ -17,6 +17,14 @@ extern void omap4_cminst_clkdm_disable_hwsup(u8 part, s16 inst, u16 cdoffs);
extern void omap4_cminst_clkdm_force_sleep(u8 part, s16 inst, u16 cdoffs); extern void omap4_cminst_clkdm_force_sleep(u8 part, s16 inst, u16 cdoffs);
extern void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs); extern void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs);
extern int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
extern int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
extern void omap4_cminst_module_enable(u8 mode, u8 part, u16 inst, s16 cdoffs,
u16 clkctrl_offs);
extern void omap4_cminst_module_disable(u8 part, u16 inst, s16 cdoffs,
u16 clkctrl_offs);
/* /*
* In an ideal world, we would not export these low-level functions, * In an ideal world, we would not export these low-level functions,
* but this will probably take some time to fix properly * but this will probably take some time to fix properly
@@ -32,6 +40,4 @@ extern u32 omap4_cminst_clear_inst_reg_bits(u32 bits, u8 part, s16 inst,
extern u32 omap4_cminst_read_inst_reg_bits(u8 part, u16 inst, s16 idx, extern u32 omap4_cminst_read_inst_reg_bits(u8 part, u16 inst, s16 idx,
u32 mask); u32 mask);
extern int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg);
#endif #endif

View File

@@ -146,9 +146,10 @@
#include <plat/prcm.h> #include <plat/prcm.h>
#include "cm2xxx_3xxx.h" #include "cm2xxx_3xxx.h"
#include "cm44xx.h" #include "cminst44xx.h"
#include "prm2xxx_3xxx.h" #include "prm2xxx_3xxx.h"
#include "prm44xx.h" #include "prm44xx.h"
#include "prminst44xx.h"
#include "mux.h" #include "mux.h"
/* Maximum microseconds to wait for OMAP module to softreset */ /* Maximum microseconds to wait for OMAP module to softreset */
@@ -678,6 +679,56 @@ static void _disable_optional_clocks(struct omap_hwmod *oh)
} }
} }
/**
* _enable_module - enable CLKCTRL modulemode on OMAP4
* @oh: struct omap_hwmod *
*
* Enables the PRCM module mode related to the hwmod @oh.
* No return value.
*/
static void _enable_module(struct omap_hwmod *oh)
{
/* The module mode does not exist prior OMAP4 */
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return;
if (!oh->clkdm || !oh->prcm.omap4.modulemode)
return;
pr_debug("omap_hwmod: %s: _enable_module: %d\n",
oh->name, oh->prcm.omap4.modulemode);
omap4_cminst_module_enable(oh->prcm.omap4.modulemode,
oh->clkdm->prcm_partition,
oh->clkdm->cm_inst,
oh->clkdm->clkdm_offs,
oh->prcm.omap4.clkctrl_offs);
}
/**
* _disable_module - enable CLKCTRL modulemode on OMAP4
* @oh: struct omap_hwmod *
*
* Disable the PRCM module mode related to the hwmod @oh.
* No return value.
*/
static void _disable_module(struct omap_hwmod *oh)
{
/* The module mode does not exist prior OMAP4 */
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return;
if (!oh->clkdm || !oh->prcm.omap4.modulemode)
return;
pr_debug("omap_hwmod: %s: _disable_module\n", oh->name);
omap4_cminst_module_disable(oh->clkdm->prcm_partition,
oh->clkdm->cm_inst,
oh->clkdm->clkdm_offs,
oh->prcm.omap4.clkctrl_offs);
}
/** /**
* _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh * _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh
* @oh: struct omap_hwmod *oh * @oh: struct omap_hwmod *oh
@@ -990,9 +1041,40 @@ static struct omap_hwmod *_lookup(const char *name)
return oh; return oh;
} }
/**
* _init_clkdm - look up a clockdomain name, store pointer in omap_hwmod
* @oh: struct omap_hwmod *
*
* Convert a clockdomain name stored in a struct omap_hwmod into a
* clockdomain pointer, and save it into the struct omap_hwmod.
* return -EINVAL if clkdm_name does not exist or if the lookup failed.
*/
static int _init_clkdm(struct omap_hwmod *oh)
{
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return 0;
if (!oh->clkdm_name) {
pr_warning("omap_hwmod: %s: no clkdm_name\n", oh->name);
return -EINVAL;
}
oh->clkdm = clkdm_lookup(oh->clkdm_name);
if (!oh->clkdm) {
pr_warning("omap_hwmod: %s: could not associate to clkdm %s\n",
oh->name, oh->clkdm_name);
return -EINVAL;
}
pr_debug("omap_hwmod: %s: associated to clkdm %s\n",
oh->name, oh->clkdm_name);
return 0;
}
/** /**
* _init_clocks - clk_get() all clocks associated with this hwmod * _init_clocks - clk_get() all clocks associated with this hwmod. Retrieve as
* well the clockdomain.
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
* @data: not used; pass NULL * @data: not used; pass NULL
* *
@@ -1012,6 +1094,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data)
ret |= _init_main_clk(oh); ret |= _init_main_clk(oh);
ret |= _init_interface_clks(oh); ret |= _init_interface_clks(oh);
ret |= _init_opt_clks(oh); ret |= _init_opt_clks(oh);
ret |= _init_clkdm(oh);
if (!ret) if (!ret)
oh->_state = _HWMOD_STATE_CLKS_INITED; oh->_state = _HWMOD_STATE_CLKS_INITED;
@@ -1028,7 +1111,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data)
* Wait for a module @oh to leave slave idle. Returns 0 if the module * Wait for a module @oh to leave slave idle. Returns 0 if the module
* does not have an IDLEST bit or if the module successfully leaves * does not have an IDLEST bit or if the module successfully leaves
* slave idle; otherwise, pass along the return value of the * slave idle; otherwise, pass along the return value of the
* appropriate *_cm_wait_module_ready() function. * appropriate *_cm*_wait_module_ready() function.
*/ */
static int _wait_target_ready(struct omap_hwmod *oh) static int _wait_target_ready(struct omap_hwmod *oh)
{ {
@@ -1055,7 +1138,13 @@ static int _wait_target_ready(struct omap_hwmod *oh)
oh->prcm.omap2.idlest_reg_id, oh->prcm.omap2.idlest_reg_id,
oh->prcm.omap2.idlest_idle_bit); oh->prcm.omap2.idlest_idle_bit);
} else if (cpu_is_omap44xx()) { } else if (cpu_is_omap44xx()) {
ret = omap4_cm_wait_module_ready(oh->prcm.omap4.clkctrl_reg); if (!oh->clkdm)
return -EINVAL;
ret = omap4_cminst_wait_module_ready(oh->clkdm->prcm_partition,
oh->clkdm->cm_inst,
oh->clkdm->clkdm_offs,
oh->prcm.omap4.clkctrl_offs);
} else { } else {
BUG(); BUG();
}; };
@@ -1063,6 +1152,36 @@ static int _wait_target_ready(struct omap_hwmod *oh)
return ret; return ret;
} }
/**
* _wait_target_disable - wait for a module to be disabled
* @oh: struct omap_hwmod *
*
* Wait for a module @oh to enter slave idle. Returns 0 if the module
* does not have an IDLEST bit or if the module successfully enters
* slave idle; otherwise, pass along the return value of the
* appropriate *_cm*_wait_module_idle() function.
*/
static int _wait_target_disable(struct omap_hwmod *oh)
{
/* TODO: For now just handle OMAP4+ */
if (cpu_is_omap24xx() || cpu_is_omap34xx())
return 0;
if (!oh)
return -EINVAL;
if (oh->_int_flags & _HWMOD_NO_MPU_PORT)
return 0;
if (oh->flags & HWMOD_NO_IDLEST)
return 0;
return omap4_cminst_wait_module_idle(oh->clkdm->prcm_partition,
oh->clkdm->cm_inst,
oh->clkdm->clkdm_offs,
oh->prcm.omap4.clkctrl_offs);
}
/** /**
* _lookup_hardreset - fill register bit info for this hwmod/reset line * _lookup_hardreset - fill register bit info for this hwmod/reset line
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
@@ -1119,8 +1238,10 @@ static int _assert_hardreset(struct omap_hwmod *oh, const char *name)
return omap2_prm_assert_hardreset(oh->prcm.omap2.module_offs, return omap2_prm_assert_hardreset(oh->prcm.omap2.module_offs,
ohri.rst_shift); ohri.rst_shift);
else if (cpu_is_omap44xx()) else if (cpu_is_omap44xx())
return omap4_prm_assert_hardreset(oh->prcm.omap4.rstctrl_reg, return omap4_prminst_assert_hardreset(ohri.rst_shift,
ohri.rst_shift); oh->clkdm->pwrdm.ptr->prcm_partition,
oh->clkdm->pwrdm.ptr->prcm_offs,
oh->prcm.omap4.rstctrl_offs);
else else
return -EINVAL; return -EINVAL;
} }
@@ -1155,8 +1276,10 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
if (ohri.st_shift) if (ohri.st_shift)
pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n", pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n",
oh->name, name); oh->name, name);
ret = omap4_prm_deassert_hardreset(oh->prcm.omap4.rstctrl_reg, ret = omap4_prminst_deassert_hardreset(ohri.rst_shift,
ohri.rst_shift); oh->clkdm->pwrdm.ptr->prcm_partition,
oh->clkdm->pwrdm.ptr->prcm_offs,
oh->prcm.omap4.rstctrl_offs);
} else { } else {
return -EINVAL; return -EINVAL;
} }
@@ -1191,8 +1314,10 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name)
return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs, return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs,
ohri.st_shift); ohri.st_shift);
} else if (cpu_is_omap44xx()) { } else if (cpu_is_omap44xx()) {
return omap4_prm_is_hardreset_asserted(oh->prcm.omap4.rstctrl_reg, return omap4_prminst_is_hardreset_asserted(ohri.rst_shift,
ohri.rst_shift); oh->clkdm->pwrdm.ptr->prcm_partition,
oh->clkdm->pwrdm.ptr->prcm_offs,
oh->prcm.omap4.rstctrl_offs);
} else { } else {
return -EINVAL; return -EINVAL;
} }
@@ -1312,6 +1437,7 @@ static int _reset(struct omap_hwmod *oh)
static int _enable(struct omap_hwmod *oh) static int _enable(struct omap_hwmod *oh)
{ {
int r; int r;
int hwsup = 0;
pr_debug("omap_hwmod: %s: enabling\n", oh->name); pr_debug("omap_hwmod: %s: enabling\n", oh->name);
@@ -1323,14 +1449,6 @@ static int _enable(struct omap_hwmod *oh)
return -EINVAL; return -EINVAL;
} }
/* Mux pins for device runtime if populated */
if (oh->mux && (!oh->mux->enabled ||
((oh->_state == _HWMOD_STATE_IDLE) &&
oh->mux->pads_dynamic)))
omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
_add_initiator_dep(oh, mpu_oh);
_enable_clocks(oh);
/* /*
* If an IP contains only one HW reset line, then de-assert it in order * If an IP contains only one HW reset line, then de-assert it in order
@@ -1341,22 +1459,56 @@ static int _enable(struct omap_hwmod *oh)
oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1) oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1)
_deassert_hardreset(oh, oh->rst_lines[0].name); _deassert_hardreset(oh, oh->rst_lines[0].name);
r = _wait_target_ready(oh); /* Mux pins for device runtime if populated */
if (r) { if (oh->mux && (!oh->mux->enabled ||
pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n", ((oh->_state == _HWMOD_STATE_IDLE) &&
oh->name, r); oh->mux->pads_dynamic)))
_disable_clocks(oh); omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
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 */ r = _wait_target_ready(oh);
if (oh->class->sysc) { if (!r) {
if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) /*
_update_sysc_cache(oh); * Set the clockdomain to HW_AUTO only if the target is ready,
_enable_sysc(oh); * 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; return r;
@@ -1372,6 +1524,8 @@ static int _enable(struct omap_hwmod *oh)
*/ */
static int _idle(struct omap_hwmod *oh) static int _idle(struct omap_hwmod *oh)
{ {
int ret;
pr_debug("omap_hwmod: %s: idling\n", oh->name); pr_debug("omap_hwmod: %s: idling\n", oh->name);
if (oh->_state != _HWMOD_STATE_ENABLED) { if (oh->_state != _HWMOD_STATE_ENABLED) {
@@ -1383,7 +1537,20 @@ static int _idle(struct omap_hwmod *oh)
if (oh->class->sysc) if (oh->class->sysc)
_idle_sysc(oh); _idle_sysc(oh);
_del_initiator_dep(oh, mpu_oh); _del_initiator_dep(oh, mpu_oh);
_disable_module(oh);
ret = _wait_target_disable(oh);
if (ret)
pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
oh->name);
/*
* The module must be in idle mode before disabling any parents
* clocks. Otherwise, the parent clock might be disabled before
* the module transition is done, and thus will prevent the
* transition to complete properly.
*/
_disable_clocks(oh); _disable_clocks(oh);
if (oh->clkdm)
clkdm_hwmod_disable(oh->clkdm, oh);
/* Mux pins for device idle if populated */ /* Mux pins for device idle if populated */
if (oh->mux && oh->mux->pads_dynamic) if (oh->mux && oh->mux->pads_dynamic)
@@ -1475,7 +1642,14 @@ static int _shutdown(struct omap_hwmod *oh)
if (oh->_state == _HWMOD_STATE_ENABLED) { if (oh->_state == _HWMOD_STATE_ENABLED) {
_del_initiator_dep(oh, mpu_oh); _del_initiator_dep(oh, mpu_oh);
/* XXX what about the other system initiators here? dma, dsp */ /* XXX what about the other system initiators here? dma, dsp */
_disable_module(oh);
ret = _wait_target_disable(oh);
if (ret)
pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
oh->name);
_disable_clocks(oh); _disable_clocks(oh);
if (oh->clkdm)
clkdm_hwmod_disable(oh->clkdm, oh);
} }
/* XXX Should this code also force-disable the optional clocks? */ /* XXX Should this code also force-disable the optional clocks? */

File diff suppressed because it is too large Load Diff

View File

@@ -108,6 +108,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
u32 cur_state; u32 cur_state;
int sleep_switch = -1; int sleep_switch = -1;
int ret = 0; int ret = 0;
int hwsup = 0;
if (pwrdm == NULL || IS_ERR(pwrdm)) if (pwrdm == NULL || IS_ERR(pwrdm))
return -EINVAL; return -EINVAL;
@@ -127,6 +128,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) { (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) {
sleep_switch = LOWPOWERSTATE_SWITCH; sleep_switch = LOWPOWERSTATE_SWITCH;
} else { } else {
hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
clkdm_wakeup(pwrdm->pwrdm_clkdms[0]); clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);
pwrdm_wait_transition(pwrdm); pwrdm_wait_transition(pwrdm);
sleep_switch = FORCEWAKEUP_SWITCH; sleep_switch = FORCEWAKEUP_SWITCH;
@@ -142,7 +144,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
switch (sleep_switch) { switch (sleep_switch) {
case FORCEWAKEUP_SWITCH: case FORCEWAKEUP_SWITCH:
if (pwrdm->pwrdm_clkdms[0]->flags & CLKDM_CAN_ENABLE_AUTO) if (hwsup)
clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]); clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);
else else
clkdm_sleep(pwrdm->pwrdm_clkdms[0]); clkdm_sleep(pwrdm->pwrdm_clkdms[0]);

View File

@@ -70,7 +70,7 @@ static void omap_prcm_arch_reset(char mode, const char *cmd)
prcm_offs = OMAP3430_GR_MOD; prcm_offs = OMAP3430_GR_MOD;
omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0)); omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0));
} else if (cpu_is_omap44xx()) { } else if (cpu_is_omap44xx()) {
omap4_prm_global_warm_sw_reset(); /* never returns */ omap4_prminst_global_warm_sw_reset(); /* never returns */
} else { } else {
WARN_ON(1); WARN_ON(1);
} }

View File

@@ -1,7 +1,7 @@
/* /*
* OMAP4 PRM module functions * OMAP4 PRM module functions
* *
* Copyright (C) 2010 Texas Instruments, Inc. * Copyright (C) 2011 Texas Instruments, Inc.
* Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Nokia Corporation
* Benoît Cousson * Benoît Cousson
* Paul Walmsley * Paul Walmsley
@@ -24,12 +24,6 @@
#include "prm44xx.h" #include "prm44xx.h"
#include "prm-regbits-44xx.h" #include "prm-regbits-44xx.h"
/*
* Address offset (in bytes) between the reset control and the reset
* status registers: 4 bytes on OMAP4
*/
#define OMAP4_RST_CTRL_ST_OFFSET 4
/* PRM low-level functions */ /* PRM low-level functions */
/* Read a register in a CM/PRM instance in the PRM module */ /* Read a register in a CM/PRM instance in the PRM module */
@@ -56,140 +50,3 @@ u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 reg)
return v; return v;
} }
/* Read a PRM register, AND it, and shift the result down to bit 0 */
/* XXX deprecated */
u32 omap4_prm_read_bits_shift(void __iomem *reg, u32 mask)
{
u32 v;
v = __raw_readl(reg);
v &= mask;
v >>= __ffs(mask);
return v;
}
/* Read-modify-write a register in a PRM module. Caller must lock */
/* XXX deprecated */
u32 omap4_prm_rmw_reg_bits(u32 mask, u32 bits, void __iomem *reg)
{
u32 v;
v = __raw_readl(reg);
v &= ~mask;
v |= bits;
__raw_writel(v, reg);
return v;
}
u32 omap4_prm_set_inst_reg_bits(u32 bits, s16 inst, s16 reg)
{
return omap4_prm_rmw_inst_reg_bits(bits, bits, inst, reg);
}
u32 omap4_prm_clear_inst_reg_bits(u32 bits, s16 inst, s16 reg)
{
return omap4_prm_rmw_inst_reg_bits(bits, 0x0, inst, reg);
}
/**
* omap4_prm_is_hardreset_asserted - read the HW reset line state of
* submodules contained in the hwmod module
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to check
*
* Returns 1 if the (sub)module hardreset line is currently asserted,
* 0 if the (sub)module hardreset line is not currently asserted, or
* -EINVAL upon parameter error.
*/
int omap4_prm_is_hardreset_asserted(void __iomem *rstctrl_reg, u8 shift)
{
if (!cpu_is_omap44xx() || !rstctrl_reg)
return -EINVAL;
return omap4_prm_read_bits_shift(rstctrl_reg, (1 << shift));
}
/**
* omap4_prm_assert_hardreset - assert the HW reset line of a submodule
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to assert
*
* Some IPs like dsp, ipu or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* place the submodule into reset. Returns 0 upon success or -EINVAL
* upon an argument error.
*/
int omap4_prm_assert_hardreset(void __iomem *rstctrl_reg, u8 shift)
{
u32 mask;
if (!cpu_is_omap44xx() || !rstctrl_reg)
return -EINVAL;
mask = 1 << shift;
omap4_prm_rmw_reg_bits(mask, mask, rstctrl_reg);
return 0;
}
/**
* omap4_prm_deassert_hardreset - deassert a submodule hardreset line and wait
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to deassert
*
* Some IPs like dsp, ipu or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* take the submodule out of reset and wait until the PRCM indicates
* that the reset has completed before returning. Returns 0 upon success or
* -EINVAL upon an argument error, -EEXIST if the submodule was already out
* of reset, or -EBUSY if the submodule did not exit reset promptly.
*/
int omap4_prm_deassert_hardreset(void __iomem *rstctrl_reg, u8 shift)
{
u32 mask;
void __iomem *rstst_reg;
int c;
if (!cpu_is_omap44xx() || !rstctrl_reg)
return -EINVAL;
rstst_reg = rstctrl_reg + OMAP4_RST_CTRL_ST_OFFSET;
mask = 1 << shift;
/* Check the current status to avoid de-asserting the line twice */
if (omap4_prm_read_bits_shift(rstctrl_reg, mask) == 0)
return -EEXIST;
/* Clear the reset status by writing 1 to the status bit */
omap4_prm_rmw_reg_bits(0xffffffff, mask, rstst_reg);
/* de-assert the reset control line */
omap4_prm_rmw_reg_bits(mask, 0, rstctrl_reg);
/* wait the status to be set */
omap_test_timeout(omap4_prm_read_bits_shift(rstst_reg, mask),
MAX_MODULE_HARDRESET_WAIT, c);
return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
}
void omap4_prm_global_warm_sw_reset(void)
{
u32 v;
v = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
OMAP4_RM_RSTCTRL);
v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
omap4_prm_write_inst_reg(v, OMAP4430_PRM_DEVICE_INST,
OMAP4_RM_RSTCTRL);
/* OCP barrier */
v = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
OMAP4_RM_RSTCTRL);
}

View File

@@ -750,16 +750,6 @@
extern u32 omap4_prm_read_inst_reg(s16 inst, u16 idx); extern u32 omap4_prm_read_inst_reg(s16 inst, u16 idx);
extern void omap4_prm_write_inst_reg(u32 val, s16 inst, u16 idx); extern void omap4_prm_write_inst_reg(u32 val, s16 inst, u16 idx);
extern u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx); extern u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
extern u32 omap4_prm_rmw_reg_bits(u32 mask, u32 bits, void __iomem *reg);
extern u32 omap4_prm_set_inst_reg_bits(u32 bits, s16 inst, s16 idx);
extern u32 omap4_prm_clear_inst_reg_bits(u32 bits, s16 inst, s16 idx);
extern u32 omap4_prm_read_bits_shift(void __iomem *reg, u32 mask);
extern int omap4_prm_is_hardreset_asserted(void __iomem *rstctrl_reg, u8 shift);
extern int omap4_prm_assert_hardreset(void __iomem *rstctrl_reg, u8 shift);
extern int omap4_prm_deassert_hardreset(void __iomem *rstctrl_reg, u8 shift);
extern void omap4_prm_global_warm_sw_reset(void);
# endif # endif

View File

@@ -2,6 +2,7 @@
* OMAP4 PRM instance functions * OMAP4 PRM instance functions
* *
* Copyright (C) 2009 Nokia Corporation * Copyright (C) 2009 Nokia Corporation
* Copyright (C) 2011 Texas Instruments, Inc.
* Paul Walmsley * Paul Walmsley
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -53,7 +54,7 @@ void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx)
/* Read-modify-write a register in PRM. Caller must lock */ /* Read-modify-write a register in PRM. Caller must lock */
u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst, u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst,
s16 idx) u16 idx)
{ {
u32 v; u32 v;
@@ -64,3 +65,112 @@ u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst,
return v; return v;
} }
/*
* Address offset (in bytes) between the reset control and the reset
* status registers: 4 bytes on OMAP4
*/
#define OMAP4_RST_CTRL_ST_OFFSET 4
/**
* omap4_prminst_is_hardreset_asserted - read the HW reset line state of
* submodules contained in the hwmod module
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to check
*
* Returns 1 if the (sub)module hardreset line is currently asserted,
* 0 if the (sub)module hardreset line is not currently asserted, or
* -EINVAL upon parameter error.
*/
int omap4_prminst_is_hardreset_asserted(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs)
{
u32 v;
v = omap4_prminst_read_inst_reg(part, inst, rstctrl_offs);
v &= 1 << shift;
v >>= shift;
return v;
}
/**
* omap4_prminst_assert_hardreset - assert the HW reset line of a submodule
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to assert
*
* Some IPs like dsp, ipu or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* place the submodule into reset. Returns 0 upon success or -EINVAL
* upon an argument error.
*/
int omap4_prminst_assert_hardreset(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs)
{
u32 mask = 1 << shift;
omap4_prminst_rmw_inst_reg_bits(mask, mask, part, inst, rstctrl_offs);
return 0;
}
/**
* omap4_prminst_deassert_hardreset - deassert a submodule hardreset line and
* wait
* @rstctrl_reg: RM_RSTCTRL register address for this module
* @shift: register bit shift corresponding to the reset line to deassert
*
* Some IPs like dsp, ipu or iva contain processors that require an HW
* reset line to be asserted / deasserted in order to fully enable the
* IP. These modules may have multiple hard-reset lines that reset
* different 'submodules' inside the IP block. This function will
* take the submodule out of reset and wait until the PRCM indicates
* that the reset has completed before returning. Returns 0 upon success or
* -EINVAL upon an argument error, -EEXIST if the submodule was already out
* of reset, or -EBUSY if the submodule did not exit reset promptly.
*/
int omap4_prminst_deassert_hardreset(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs)
{
int c;
u32 mask = 1 << shift;
u16 rstst_offs = rstctrl_offs + OMAP4_RST_CTRL_ST_OFFSET;
/* Check the current status to avoid de-asserting the line twice */
if (omap4_prminst_is_hardreset_asserted(shift, part, inst,
rstctrl_offs) == 0)
return -EEXIST;
/* Clear the reset status by writing 1 to the status bit */
omap4_prminst_rmw_inst_reg_bits(0xffffffff, mask, part, inst,
rstst_offs);
/* de-assert the reset control line */
omap4_prminst_rmw_inst_reg_bits(mask, 0, part, inst, rstctrl_offs);
/* wait the status to be set */
omap_test_timeout(omap4_prminst_is_hardreset_asserted(shift, part, inst,
rstst_offs),
MAX_MODULE_HARDRESET_WAIT, c);
return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
}
void omap4_prminst_global_warm_sw_reset(void)
{
u32 v;
v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_RSTCTRL_OFFSET);
v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
omap4_prminst_write_inst_reg(v, OMAP4430_PRM_PARTITION,
OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_RSTCTRL_OFFSET);
/* OCP barrier */
v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
OMAP4430_PRM_DEVICE_INST,
OMAP4_PRM_RSTCTRL_OFFSET);
}

View File

@@ -2,6 +2,7 @@
* OMAP4 Power/Reset Management (PRM) function prototypes * OMAP4 Power/Reset Management (PRM) function prototypes
* *
* Copyright (C) 2010 Nokia Corporation * Copyright (C) 2010 Nokia Corporation
* Copyright (C) 2011 Texas Instruments, Inc.
* Paul Walmsley * Paul Walmsley
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -18,8 +19,15 @@
extern u32 omap4_prminst_read_inst_reg(u8 part, s16 inst, u16 idx); extern u32 omap4_prminst_read_inst_reg(u8 part, s16 inst, u16 idx);
extern void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx); extern void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx);
extern u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, extern u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part,
s16 inst, s16 idx); s16 inst, u16 idx);
extern void omap4_prm_global_warm_sw_reset(void); extern void omap4_prminst_global_warm_sw_reset(void);
extern int omap4_prminst_is_hardreset_asserted(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs);
extern int omap4_prminst_assert_hardreset(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs);
extern int omap4_prminst_deassert_hardreset(u8 shift, u8 part, s16 inst,
u16 rstctrl_offs);
#endif #endif

View File

@@ -80,11 +80,11 @@ static struct twl4030_madc_platform_data omap3_madc_pdata = {
.irq_line = 1, .irq_line = 1,
}; };
static struct twl4030_codec_audio_data omap3_audio; static struct twl4030_codec_data omap3_codec;
static struct twl4030_codec_data omap3_codec_pdata = { static struct twl4030_audio_data omap3_audio_pdata = {
.audio_mclk = 26000000, .audio_mclk = 26000000,
.audio = &omap3_audio, .codec = &omap3_codec,
}; };
static struct regulator_consumer_supply omap3_vdda_dac_supplies[] = { static struct regulator_consumer_supply omap3_vdda_dac_supplies[] = {
@@ -292,8 +292,8 @@ void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data,
if (pdata_flags & TWL_COMMON_PDATA_MADC && !pmic_data->madc) if (pdata_flags & TWL_COMMON_PDATA_MADC && !pmic_data->madc)
pmic_data->madc = &omap3_madc_pdata; pmic_data->madc = &omap3_madc_pdata;
if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->codec) if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->audio)
pmic_data->codec = &omap3_codec_pdata; pmic_data->audio = &omap3_audio_pdata;
/* Common regulator configurations */ /* Common regulator configurations */
if (regulators_flags & TWL_COMMON_REGULATOR_VDAC && !pmic_data->vdac) if (regulators_flags & TWL_COMMON_REGULATOR_VDAC && !pmic_data->vdac)

View File

@@ -475,8 +475,41 @@ int __init clk_init(struct clk_functions * custom_clocks)
/* /*
* debugfs support to trace clock tree hierarchy and attributes * debugfs support to trace clock tree hierarchy and attributes
*/ */
#include <linux/debugfs.h>
#include <linux/seq_file.h>
static struct dentry *clk_debugfs_root; static struct dentry *clk_debugfs_root;
static int clk_dbg_show_summary(struct seq_file *s, void *unused)
{
struct clk *c;
struct clk *pa;
seq_printf(s, "%-30s %-30s %-10s %s\n",
"clock-name", "parent-name", "rate", "use-count");
list_for_each_entry(c, &clocks, node) {
pa = c->parent;
seq_printf(s, "%-30s %-30s %-10lu %d\n",
c->name, pa ? pa->name : "none", c->rate, c->usecount);
}
return 0;
}
static int clk_dbg_open(struct inode *inode, struct file *file)
{
return single_open(file, clk_dbg_show_summary, inode->i_private);
}
static const struct file_operations debug_clock_fops = {
.open = clk_dbg_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int clk_debugfs_register_one(struct clk *c) static int clk_debugfs_register_one(struct clk *c)
{ {
int err; int err;
@@ -545,6 +578,12 @@ static int __init clk_debugfs_init(void)
if (err) if (err)
goto err_out; goto err_out;
} }
d = debugfs_create_file("summary", S_IRUGO,
d, NULL, &debug_clock_fops);
if (!d)
return -ENOMEM;
return 0; return 0;
err_out: err_out:
debugfs_remove_recursive(clk_debugfs_root); debugfs_remove_recursive(clk_debugfs_root);

View File

@@ -407,11 +407,19 @@
#endif #endif
#define TWL6030_IRQ_END (TWL6030_IRQ_BASE + TWL6030_BASE_NR_IRQS) #define TWL6030_IRQ_END (TWL6030_IRQ_BASE + TWL6030_BASE_NR_IRQS)
#define TWL6040_CODEC_IRQ_BASE TWL6030_IRQ_END
#ifdef CONFIG_TWL6040_CODEC
#define TWL6040_CODEC_NR_IRQS 6
#else
#define TWL6040_CODEC_NR_IRQS 0
#endif
#define TWL6040_CODEC_IRQ_END (TWL6040_CODEC_IRQ_BASE + TWL6040_CODEC_NR_IRQS)
/* Total number of interrupts depends on the enabled blocks above */ /* Total number of interrupts depends on the enabled blocks above */
#if (TWL4030_GPIO_IRQ_END > TWL6030_IRQ_END) #if (TWL4030_GPIO_IRQ_END > TWL6040_CODEC_IRQ_END)
#define TWL_IRQ_END TWL4030_GPIO_IRQ_END #define TWL_IRQ_END TWL4030_GPIO_IRQ_END
#else #else
#define TWL_IRQ_END TWL6030_IRQ_END #define TWL_IRQ_END TWL6040_CODEC_IRQ_END
#endif #endif
/* GPMC related */ /* GPMC related */

View File

@@ -2,6 +2,7 @@
* omap_hwmod macros, structures * omap_hwmod macros, structures
* *
* Copyright (C) 2009-2011 Nokia Corporation * Copyright (C) 2009-2011 Nokia Corporation
* Copyright (C) 2011 Texas Instruments, Inc.
* Paul Walmsley * Paul Walmsley
* *
* Created in collaboration with (alphabetical order): Benoît Cousson, * Created in collaboration with (alphabetical order): Benoît Cousson,
@@ -79,6 +80,11 @@ extern struct omap_hwmod_sysc_fields omap_hwmod_sysc_type2;
#define HWMOD_IDLEMODE_SMART (1 << 2) #define HWMOD_IDLEMODE_SMART (1 << 2)
#define HWMOD_IDLEMODE_SMART_WKUP (1 << 3) #define HWMOD_IDLEMODE_SMART_WKUP (1 << 3)
/* modulemode control type (SW or HW) */
#define MODULEMODE_HWCTRL 1
#define MODULEMODE_SWCTRL 2
/** /**
* struct omap_hwmod_mux_info - hwmod specific mux configuration * struct omap_hwmod_mux_info - hwmod specific mux configuration
* @pads: array of omap_device_pad entries * @pads: array of omap_device_pad entries
@@ -360,9 +366,11 @@ struct omap_hwmod_omap2_prcm {
* @submodule_wkdep_bit: bit shift of the WKDEP range * @submodule_wkdep_bit: bit shift of the WKDEP range
*/ */
struct omap_hwmod_omap4_prcm { struct omap_hwmod_omap4_prcm {
void __iomem *clkctrl_reg; u16 clkctrl_offs;
void __iomem *rstctrl_reg; u16 rstctrl_offs;
u16 context_offs;
u8 submodule_wkdep_bit; u8 submodule_wkdep_bit;
u8 modulemode;
}; };
@@ -515,6 +523,8 @@ struct omap_hwmod {
const char *main_clk; const char *main_clk;
struct clk *_clk; struct clk *_clk;
struct omap_hwmod_opt_clk *opt_clks; struct omap_hwmod_opt_clk *opt_clks;
char *clkdm_name;
struct clockdomain *clkdm;
char *vdd_name; char *vdd_name;
struct voltagedomain *voltdm; struct voltagedomain *voltdm;
struct omap_hwmod_ocp_if **masters; /* connect to *_IA */ struct omap_hwmod_ocp_if **masters; /* connect to *_IA */

View File

@@ -236,56 +236,71 @@ static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat)
return 0; return 0;
} }
static void _add_clkdev(struct omap_device *od, const char *clk_alias,
const char *clk_name)
{
struct clk *r;
struct clk_lookup *l;
if (!clk_alias || !clk_name)
return;
pr_debug("omap_device: %s: Creating %s -> %s\n",
dev_name(&od->pdev.dev), clk_alias, clk_name);
r = clk_get_sys(dev_name(&od->pdev.dev), clk_alias);
if (!IS_ERR(r)) {
pr_warning("omap_device: %s: alias %s already exists\n",
dev_name(&od->pdev.dev), clk_alias);
clk_put(r);
return;
}
r = omap_clk_get_by_name(clk_name);
if (IS_ERR(r)) {
pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
dev_name(&od->pdev.dev), clk_name);
return;
}
l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev.dev));
if (!l) {
pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
dev_name(&od->pdev.dev), clk_alias);
return;
}
clkdev_add(l);
}
/** /**
* _add_optional_clock_clkdev - Add clkdev entry for hwmod optional clocks * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks
* and main clock
* @od: struct omap_device *od * @od: struct omap_device *od
* @oh: struct omap_hwmod *oh
* *
* For every optional clock present per hwmod per omap_device, this function * For the main clock and every optional clock present per hwmod per
* adds an entry in the clkdev table of the form <dev-id=dev_name, con-id=role> * omap_device, this function adds an entry in the clkdev table of the
* if it does not exist already. * form <dev-id=dev_name, con-id=role> if it does not exist already.
* *
* The function is called from inside omap_device_build_ss(), after * The function is called from inside omap_device_build_ss(), after
* omap_device_register. * omap_device_register.
* *
* This allows drivers to get a pointer to its optional clocks based on its role * This allows drivers to get a pointer to its optional clocks based on its role
* by calling clk_get(<dev*>, <role>). * by calling clk_get(<dev*>, <role>).
* In the case of the main clock, a "fck" alias is used.
* *
* No return value. * No return value.
*/ */
static void _add_optional_clock_clkdev(struct omap_device *od, static void _add_hwmod_clocks_clkdev(struct omap_device *od,
struct omap_hwmod *oh) struct omap_hwmod *oh)
{ {
int i; int i;
for (i = 0; i < oh->opt_clks_cnt; i++) { _add_clkdev(od, "fck", oh->main_clk);
struct omap_hwmod_opt_clk *oc;
struct clk *r;
struct clk_lookup *l;
oc = &oh->opt_clks[i]; for (i = 0; i < oh->opt_clks_cnt; i++)
_add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk);
if (!oc->_clk)
continue;
r = clk_get_sys(dev_name(&od->pdev.dev), oc->role);
if (!IS_ERR(r))
continue; /* clkdev entry exists */
r = omap_clk_get_by_name((char *)oc->clk);
if (IS_ERR(r)) {
pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
dev_name(&od->pdev.dev), oc->clk);
continue;
}
l = clkdev_alloc(r, oc->role, dev_name(&od->pdev.dev));
if (!l) {
pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
dev_name(&od->pdev.dev), oc->role);
return;
}
clkdev_add(l);
}
} }
@@ -492,7 +507,7 @@ struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id,
for (i = 0; i < oh_cnt; i++) { for (i = 0; i < oh_cnt; i++) {
hwmods[i]->od = od; hwmods[i]->od = od;
_add_optional_clock_clkdev(od, hwmods[i]); _add_hwmod_clocks_clkdev(od, hwmods[i]);
} }
if (ret) if (ret)

View File

@@ -267,7 +267,7 @@ config INPUT_TWL4030_PWRBUTTON
config INPUT_TWL4030_VIBRA config INPUT_TWL4030_VIBRA
tristate "Support for TWL4030 Vibrator" tristate "Support for TWL4030 Vibrator"
depends on TWL4030_CORE depends on TWL4030_CORE
select TWL4030_CODEC select MFD_TWL4030_AUDIO
select INPUT_FF_MEMLESS select INPUT_FF_MEMLESS
help help
This option enables support for TWL4030 Vibrator Driver. This option enables support for TWL4030 Vibrator Driver.
@@ -275,6 +275,17 @@ config INPUT_TWL4030_VIBRA
To compile this driver as a module, choose M here. The module will To compile this driver as a module, choose M here. The module will
be called twl4030_vibra. be called twl4030_vibra.
config INPUT_TWL6040_VIBRA
tristate "Support for TWL6040 Vibrator"
depends on TWL4030_CORE
select TWL6040_CORE
select INPUT_FF_MEMLESS
help
This option enables support for TWL6040 Vibrator Driver.
To compile this driver as a module, choose M here. The module will
be called twl6040_vibra.
config INPUT_UINPUT config INPUT_UINPUT
tristate "User level driver support" tristate "User level driver support"
help help

View File

@@ -40,6 +40,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o

View File

@@ -28,7 +28,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/i2c/twl.h> #include <linux/i2c/twl.h>
#include <linux/mfd/twl4030-codec.h> #include <linux/mfd/twl4030-audio.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/slab.h> #include <linux/slab.h>
@@ -67,7 +67,7 @@ static void vibra_enable(struct vibra_info *info)
{ {
u8 reg; u8 reg;
twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
/* turn H-Bridge on */ /* turn H-Bridge on */
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
@@ -75,7 +75,7 @@ static void vibra_enable(struct vibra_info *info)
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
(reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL); twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL);
info->enabled = true; info->enabled = true;
} }
@@ -90,8 +90,8 @@ static void vibra_disable(struct vibra_info *info)
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
(reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL);
twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
info->enabled = false; info->enabled = false;
} }
@@ -196,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
static int __devinit twl4030_vibra_probe(struct platform_device *pdev) static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
{ {
struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data; struct twl4030_vibra_data *pdata = pdev->dev.platform_data;
struct vibra_info *info; struct vibra_info *info;
int ret; int ret;

View File

@@ -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>");

View File

@@ -218,7 +218,7 @@ config TWL4030_POWER
and load scripts controlling which resources are switched off/on and load scripts controlling which resources are switched off/on
or reset when a sleep, wakeup or warm reset event occurs. or reset when a sleep, wakeup or warm reset event occurs.
config TWL4030_CODEC config MFD_TWL4030_AUDIO
bool bool
depends on TWL4030_CORE depends on TWL4030_CORE
select MFD_CORE select MFD_CORE
@@ -233,6 +233,12 @@ config TWL6030_PWM
Say yes here if you want support for TWL6030 PWM. Say yes here if you want support for TWL6030 PWM.
This is used to control charging LED brightness. This is used to control charging LED brightness.
config TWL6040_CORE
bool
depends on TWL4030_CORE && GENERIC_HARDIRQS
select MFD_CORE
default n
config MFD_STMPE config MFD_STMPE
bool "Support STMicroelectronics STMPE" bool "Support STMicroelectronics STMPE"
depends on I2C=y && GENERIC_HARDIRQS depends on I2C=y && GENERIC_HARDIRQS

View File

@@ -40,8 +40,9 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o

View File

@@ -110,7 +110,7 @@
#endif #endif
#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\ #if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
defined(CONFIG_SND_SOC_TWL6040) || defined(CONFIG_SND_SOC_TWL6040_MODULE) defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE)
#define twl_has_codec() true #define twl_has_codec() true
#else #else
#define twl_has_codec() false #define twl_has_codec() false
@@ -815,20 +815,19 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child); return PTR_ERR(child);
} }
if (twl_has_codec() && pdata->codec && twl_class_is_4030()) { if (twl_has_codec() && pdata->audio && twl_class_is_4030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
child = add_child(sub_chip_id, "twl4030-audio", child = add_child(sub_chip_id, "twl4030-audio",
pdata->codec, sizeof(*pdata->codec), pdata->audio, sizeof(*pdata->audio),
false, 0, 0); false, 0, 0);
if (IS_ERR(child)) if (IS_ERR(child))
return PTR_ERR(child); return PTR_ERR(child);
} }
/* Phoenix codec driver is probed directly atm */ if (twl_has_codec() && pdata->audio && twl_class_is_6030()) {
if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
child = add_child(sub_chip_id, "twl6040-codec", child = add_child(sub_chip_id, "twl6040",
pdata->codec, sizeof(*pdata->codec), pdata->audio, sizeof(*pdata->audio),
false, 0, 0); false, 0, 0);
if (IS_ERR(child)) if (IS_ERR(child))
return PTR_ERR(child); return PTR_ERR(child);

277
drivers/mfd/twl4030-audio.c Normal file
View File

@@ -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");

View File

@@ -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");

620
drivers/mfd/twl6040-core.c Normal file
View File

@@ -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");

191
drivers/mfd/twl6040-irq.c Normal file
View File

@@ -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);

View File

@@ -657,28 +657,41 @@ struct twl4030_power_data {
extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts); extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
extern int twl4030_remove_script(u8 flags); extern int twl4030_remove_script(u8 flags);
struct twl4030_codec_audio_data { struct twl4030_codec_data {
unsigned int digimic_delay; /* in ms */ unsigned int digimic_delay; /* in ms */
unsigned int ramp_delay_value; unsigned int ramp_delay_value;
unsigned int offset_cncl_path; unsigned int offset_cncl_path;
unsigned int check_defaults:1; unsigned int check_defaults:1;
unsigned int reset_registers:1; unsigned int reset_registers:1;
unsigned int hs_extmute: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); void (*set_hs_extmute)(int mute);
}; };
struct twl4030_codec_vibra_data { struct twl4030_vibra_data {
unsigned int coexist; 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; unsigned int audio_mclk;
struct twl4030_codec_audio_data *audio; struct twl4030_codec_data *codec;
struct twl4030_codec_vibra_data *vibra; struct twl4030_vibra_data *vibra;
/* twl6040 */ /* twl6040 */
int audpwron_gpio; /* audio power-on gpio */ int audpwron_gpio; /* audio power-on gpio */
int naudint_irq; /* audio interrupt */ int naudint_irq; /* audio interrupt */
unsigned int irq_base;
}; };
struct twl4030_platform_data { struct twl4030_platform_data {
@@ -690,7 +703,7 @@ struct twl4030_platform_data {
struct twl4030_keypad_data *keypad; struct twl4030_keypad_data *keypad;
struct twl4030_usb_data *usb; struct twl4030_usb_data *usb;
struct twl4030_power_data *power; struct twl4030_power_data *power;
struct twl4030_codec_data *codec; struct twl4030_audio_data *audio;
/* Common LDO regulators for TWL4030/TWL6030 */ /* Common LDO regulators for TWL4030/TWL6030 */
struct regulator_init_data *vdac; struct regulator_init_data *vdac;

View File

@@ -1,5 +1,5 @@
/* /*
* MFD driver for twl4030 codec submodule * MFD driver for twl4030 audio submodule
* *
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com> * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
@@ -259,14 +259,14 @@
#define TWL4030_VIBRA_DIR_SEL 0x20 #define TWL4030_VIBRA_DIR_SEL 0x20
/* TWL4030 codec resource IDs */ /* TWL4030 codec resource IDs */
enum twl4030_codec_res { enum twl4030_audio_res {
TWL4030_CODEC_RES_POWER = 0, TWL4030_AUDIO_RES_POWER = 0,
TWL4030_CODEC_RES_APLL, TWL4030_AUDIO_RES_APLL,
TWL4030_CODEC_RES_MAX, TWL4030_AUDIO_RES_MAX,
}; };
int twl4030_codec_disable_resource(enum twl4030_codec_res id); int twl4030_audio_disable_resource(enum twl4030_audio_res id);
int twl4030_codec_enable_resource(enum twl4030_codec_res id); int twl4030_audio_enable_resource(enum twl4030_audio_res id);
unsigned int twl4030_codec_get_mclk(void); unsigned int twl4030_audio_get_mclk(void);
#endif /* End of __TWL4030_CODEC_H__ */ #endif /* End of __TWL4030_CODEC_H__ */

228
include/linux/mfd/twl6040.h Normal file
View File

@@ -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__ */

View File

@@ -250,10 +250,11 @@ config SND_SOC_TLV320DAC33
tristate tristate
config SND_SOC_TWL4030 config SND_SOC_TWL4030
select TWL4030_CODEC select MFD_TWL4030_AUDIO
tristate tristate
config SND_SOC_TWL6040 config SND_SOC_TWL6040
select TWL6040_CORE
tristate tristate
config SND_SOC_UDA134X config SND_SOC_UDA134X

View File

@@ -36,7 +36,7 @@
#include <sound/tlv.h> #include <sound/tlv.h>
/* Register descriptions are here */ /* Register descriptions are here */
#include <linux/mfd/twl4030-codec.h> #include <linux/mfd/twl4030-audio.h>
/* Shadow register used by the audio driver */ /* Shadow register used by the audio driver */
#define TWL4030_REG_SW_SHADOW 0x4A #define TWL4030_REG_SW_SHADOW 0x4A
@@ -251,9 +251,9 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
return; return;
if (enable) if (enable)
mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); mode = twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
else else
mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
if (mode >= 0) { if (mode >= 0) {
twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode); 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) 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); struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 reg, byte; u8 reg, byte;
int i = 0; int i = 0;
@@ -375,13 +375,13 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
if (enable) { if (enable) {
twl4030->apll_enabled++; twl4030->apll_enabled++;
if (twl4030->apll_enabled == 1) if (twl4030->apll_enabled == 1)
status = twl4030_codec_enable_resource( status = twl4030_audio_enable_resource(
TWL4030_CODEC_RES_APLL); TWL4030_AUDIO_RES_APLL);
} else { } else {
twl4030->apll_enabled--; twl4030->apll_enabled--;
if (!twl4030->apll_enabled) if (!twl4030->apll_enabled)
status = twl4030_codec_disable_resource( status = twl4030_audio_disable_resource(
TWL4030_CODEC_RES_APLL); TWL4030_AUDIO_RES_APLL);
} }
if (status >= 0) 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) 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; unsigned char hs_gain, hs_pop;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
/* Base values for ramp delay calculation: 2^19 - 2^26 */ /* 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); snd_soc_codec_set_drvdata(codec, twl4030);
/* Set the defaults, and power up the codec */ /* 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; codec->dapm.idle_bias_off = 1;
twl4030_init_chip(codec); 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) 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) { if (!pdata) {
dev_err(&pdev->dev, "platform_data is missing\n"); dev_err(&pdev->dev, "platform_data is missing\n");

File diff suppressed because it is too large Load Diff

View File

@@ -22,125 +22,8 @@
#ifndef __TWL6040_H__ #ifndef __TWL6040_H__
#define __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, void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *jack, int report); struct snd_soc_jack *jack, int report);
int twl6040_get_clk_id(struct snd_soc_codec *codec);
#endif /* End of __TWL6040_H__ */ #endif /* End of __TWL6040_H__ */

View File

@@ -36,7 +36,7 @@
#include <plat/mcbsp.h> #include <plat/mcbsp.h>
/* Register descriptions for twl4030 codec part */ /* Register descriptions for twl4030 codec part */
#include <linux/mfd/twl4030-codec.h> #include <linux/mfd/twl4030-audio.h>
#include "omap-mcbsp.h" #include "omap-mcbsp.h"
#include "omap-pcm.h" #include "omap-pcm.h"

View File

@@ -21,6 +21,8 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mfd/twl6040.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/soc.h> #include <sound/soc.h>
@@ -34,8 +36,6 @@
#include "omap-pcm.h" #include "omap-pcm.h"
#include "../codecs/twl6040.h" #include "../codecs/twl6040.h"
static int twl6040_power_mode;
static int sdp4430_hw_params(struct snd_pcm_substream *substream, static int sdp4430_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) 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 clk_id, freq;
int ret; int ret;
if (twl6040_power_mode) { clk_id = twl6040_get_clk_id(rtd->codec);
clk_id = TWL6040_SYSCLK_SEL_HPPLL; if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
freq = 38400000; freq = 38400000;
} else { else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
clk_id = TWL6040_SYSCLK_SEL_LPPLL;
freq = 32768; freq = 32768;
} else
return -EINVAL;
/* set the codec mclk */ /* set the codec mclk */
ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq, 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 */ /* SDP4430 machine DAPM */
static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = { static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Ext Mic", NULL), 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; struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret; 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 */ /* Add SDP4430 specific widgets */
ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets, ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets,
ARRAY_SIZE(sdp4430_twl6040_dapm_widgets)); ARRAY_SIZE(sdp4430_twl6040_dapm_widgets));
@@ -237,9 +202,6 @@ static int __init sdp4430_soc_init(void)
if (ret) if (ret)
goto err; goto err;
/* Codec starts in HP mode */
twl6040_power_mode = 1;
return 0; return 0;
err: err:

View File

@@ -32,7 +32,7 @@
#include <plat/mcbsp.h> #include <plat/mcbsp.h>
/* Register descriptions for twl4030 codec part */ /* Register descriptions for twl4030 codec part */
#include <linux/mfd/twl4030-codec.h> #include <linux/mfd/twl4030-audio.h>
#include "omap-mcbsp.h" #include "omap-mcbsp.h"
#include "omap-pcm.h" #include "omap-pcm.h"