mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 15:50:59 +07:00
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:
commit
b0189cd087
@ -345,11 +345,40 @@ static struct platform_device sdp4430_lcd_device = {
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply sdp4430_vbat_supply[] = {
|
||||
REGULATOR_SUPPLY("vddvibl", "twl6040-vibra"),
|
||||
REGULATOR_SUPPLY("vddvibr", "twl6040-vibra"),
|
||||
};
|
||||
|
||||
static struct regulator_init_data sdp4430_vbat_data = {
|
||||
.constraints = {
|
||||
.always_on = 1,
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(sdp4430_vbat_supply),
|
||||
.consumer_supplies = sdp4430_vbat_supply,
|
||||
};
|
||||
|
||||
static struct fixed_voltage_config sdp4430_vbat_pdata = {
|
||||
.supply_name = "VBAT",
|
||||
.microvolts = 3750000,
|
||||
.init_data = &sdp4430_vbat_data,
|
||||
.gpio = -EINVAL,
|
||||
};
|
||||
|
||||
static struct platform_device sdp4430_vbat = {
|
||||
.name = "reg-fixed-voltage",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &sdp4430_vbat_pdata,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device *sdp4430_devices[] __initdata = {
|
||||
&sdp4430_lcd_device,
|
||||
&sdp4430_gpio_keys_device,
|
||||
&sdp4430_leds_gpio,
|
||||
&sdp4430_leds_pwm,
|
||||
&sdp4430_vbat,
|
||||
};
|
||||
|
||||
static struct omap_lcd_config sdp4430_lcd_config __initdata = {
|
||||
@ -505,7 +534,33 @@ static struct regulator_init_data sdp4430_vusim = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct twl4030_codec_data twl6040_codec = {
|
||||
/* single-step ramp for headset and handsfree */
|
||||
.hs_left_step = 0x0f,
|
||||
.hs_right_step = 0x0f,
|
||||
.hf_left_step = 0x1d,
|
||||
.hf_right_step = 0x1d,
|
||||
};
|
||||
|
||||
static struct twl4030_vibra_data twl6040_vibra = {
|
||||
.vibldrv_res = 8,
|
||||
.vibrdrv_res = 3,
|
||||
.viblmotor_res = 10,
|
||||
.vibrmotor_res = 10,
|
||||
.vddvibl_uV = 0, /* fixed volt supply - VBAT */
|
||||
.vddvibr_uV = 0, /* fixed volt supply - VBAT */
|
||||
};
|
||||
|
||||
static struct twl4030_audio_data twl6040_audio = {
|
||||
.codec = &twl6040_codec,
|
||||
.vibra = &twl6040_vibra,
|
||||
.audpwron_gpio = 127,
|
||||
.naudint_irq = OMAP44XX_IRQ_SYS_2N,
|
||||
.irq_base = TWL6040_CODEC_IRQ_BASE,
|
||||
};
|
||||
|
||||
static struct twl4030_platform_data sdp4430_twldata = {
|
||||
.audio = &twl6040_audio,
|
||||
/* Regulators */
|
||||
.vusim = &sdp4430_vusim,
|
||||
.vaux1 = &sdp4430_vaux1,
|
||||
|
@ -864,11 +864,11 @@ static struct twl4030_power_data rx51_t2scripts_data __initdata = {
|
||||
.resource_config = twl4030_rconfig,
|
||||
};
|
||||
|
||||
struct twl4030_codec_vibra_data rx51_vibra_data __initdata = {
|
||||
struct twl4030_vibra_data rx51_vibra_data __initdata = {
|
||||
.coexist = 0,
|
||||
};
|
||||
|
||||
struct twl4030_codec_data rx51_codec_data __initdata = {
|
||||
struct twl4030_audio_data rx51_audio_data __initdata = {
|
||||
.audio_mclk = 26000000,
|
||||
.vibra = &rx51_vibra_data,
|
||||
};
|
||||
@ -878,7 +878,7 @@ static struct twl4030_platform_data rx51_twldata __initdata = {
|
||||
.gpio = &rx51_gpio_data,
|
||||
.keypad = &rx51_kp_data,
|
||||
.power = &rx51_t2scripts_data,
|
||||
.codec = &rx51_codec_data,
|
||||
.audio = &rx51_audio_data,
|
||||
|
||||
.vaux1 = &rx51_vaux1,
|
||||
.vaux2 = &rx51_vaux2,
|
||||
|
@ -274,12 +274,12 @@ static int __init omap_i2c_init(void)
|
||||
TWL_COMMON_REGULATOR_VDAC | TWL_COMMON_REGULATOR_VPLL2);
|
||||
|
||||
if (machine_is_omap_zoom2()) {
|
||||
struct twl4030_codec_audio_data *audio_data;
|
||||
audio_data = zoom_twldata.codec->audio;
|
||||
struct twl4030_codec_data *codec_data;
|
||||
codec_data = zoom_twldata.audio->codec;
|
||||
|
||||
audio_data->ramp_delay_value = 3; /* 161 ms */
|
||||
audio_data->hs_extmute = 1;
|
||||
audio_data->set_hs_extmute = zoom2_set_hs_extmute;
|
||||
codec_data->ramp_delay_value = 3; /* 161 ms */
|
||||
codec_data->hs_extmute = 1;
|
||||
codec_data->set_hs_extmute = zoom2_set_hs_extmute;
|
||||
}
|
||||
omap_pmic_init(1, 2400, "twl5030", INT_34XX_SYS_NIRQ, &zoom_twldata);
|
||||
omap_register_i2c_bus(2, 400, NULL, 0);
|
||||
|
@ -37,6 +37,14 @@
|
||||
|
||||
u8 cpu_mask;
|
||||
|
||||
/*
|
||||
* clkdm_control: if true, then when a clock is enabled in the
|
||||
* hardware, its clockdomain will first be enabled; and when a clock
|
||||
* is disabled in the hardware, its clockdomain will be disabled
|
||||
* afterwards.
|
||||
*/
|
||||
static bool clkdm_control = true;
|
||||
|
||||
/*
|
||||
* OMAP2+ specific clock functions
|
||||
*/
|
||||
@ -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
|
||||
* @clk: struct clk * to find the companion clock of
|
||||
@ -268,7 +289,7 @@ void omap2_clk_disable(struct clk *clk)
|
||||
clk->ops->disable(clk);
|
||||
}
|
||||
|
||||
if (clk->clkdm)
|
||||
if (clkdm_control && clk->clkdm)
|
||||
clkdm_clk_disable(clk->clkdm, clk);
|
||||
|
||||
if (clk->parent)
|
||||
@ -308,7 +329,7 @@ int omap2_clk_enable(struct clk *clk)
|
||||
}
|
||||
}
|
||||
|
||||
if (clk->clkdm) {
|
||||
if (clkdm_control && clk->clkdm) {
|
||||
ret = clkdm_clk_enable(clk->clkdm, clk);
|
||||
if (ret) {
|
||||
WARN(1, "clock: %s: could not enable clockdomain %s: "
|
||||
@ -330,7 +351,7 @@ int omap2_clk_enable(struct clk *clk)
|
||||
return 0;
|
||||
|
||||
oce_err3:
|
||||
if (clk->clkdm)
|
||||
if (clkdm_control && clk->clkdm)
|
||||
clkdm_clk_disable(clk->clkdm, clk);
|
||||
oce_err2:
|
||||
if (clk->parent)
|
||||
|
@ -16,6 +16,8 @@
|
||||
#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H
|
||||
#define __ARCH_ARM_MACH_OMAP2_CLOCK_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <plat/clock.h>
|
||||
|
||||
/* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */
|
||||
@ -72,6 +74,7 @@ void omap2_clk_disable_unused(struct clk *clk);
|
||||
#endif
|
||||
|
||||
void omap2_init_clk_clkdm(struct clk *clk);
|
||||
void __init omap2_clk_disable_clkdm_control(void);
|
||||
|
||||
/* clkt_clksel.c public functions */
|
||||
u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
|
||||
|
@ -1805,9 +1805,9 @@ static struct omap_clk omap2420_clks[] = {
|
||||
CLK(NULL, "gfx_ick", &gfx_ick, CK_242X),
|
||||
/* DSS domain clocks */
|
||||
CLK("omapdss_dss", "ick", &dss_ick, CK_242X),
|
||||
CLK("omapdss_dss", "fck", &dss1_fck, CK_242X),
|
||||
CLK("omapdss_dss", "sys_clk", &dss2_fck, CK_242X),
|
||||
CLK("omapdss_dss", "tv_clk", &dss_54m_fck, CK_242X),
|
||||
CLK(NULL, "dss1_fck", &dss1_fck, CK_242X),
|
||||
CLK(NULL, "dss2_fck", &dss2_fck, CK_242X),
|
||||
CLK(NULL, "dss_54m_fck", &dss_54m_fck, CK_242X),
|
||||
/* L3 domain clocks */
|
||||
CLK(NULL, "core_l3_ck", &core_l3_ck, CK_242X),
|
||||
CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_242X),
|
||||
@ -1844,13 +1844,13 @@ static struct omap_clk omap2420_clks[] = {
|
||||
CLK(NULL, "gpt12_ick", &gpt12_ick, CK_242X),
|
||||
CLK(NULL, "gpt12_fck", &gpt12_fck, CK_242X),
|
||||
CLK("omap-mcbsp.1", "ick", &mcbsp1_ick, CK_242X),
|
||||
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_242X),
|
||||
CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_242X),
|
||||
CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_242X),
|
||||
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_242X),
|
||||
CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_242X),
|
||||
CLK("omap2_mcspi.1", "ick", &mcspi1_ick, CK_242X),
|
||||
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_242X),
|
||||
CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_242X),
|
||||
CLK("omap2_mcspi.2", "ick", &mcspi2_ick, CK_242X),
|
||||
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_242X),
|
||||
CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_242X),
|
||||
CLK(NULL, "uart1_ick", &uart1_ick, CK_242X),
|
||||
CLK(NULL, "uart1_fck", &uart1_fck, CK_242X),
|
||||
CLK(NULL, "uart2_ick", &uart2_ick, CK_242X),
|
||||
@ -1860,7 +1860,7 @@ static struct omap_clk omap2420_clks[] = {
|
||||
CLK(NULL, "gpios_ick", &gpios_ick, CK_242X),
|
||||
CLK(NULL, "gpios_fck", &gpios_fck, CK_242X),
|
||||
CLK("omap_wdt", "ick", &mpu_wdt_ick, CK_242X),
|
||||
CLK("omap_wdt", "fck", &mpu_wdt_fck, CK_242X),
|
||||
CLK(NULL, "mpu_wdt_fck", &mpu_wdt_fck, CK_242X),
|
||||
CLK(NULL, "sync_32k_ick", &sync_32k_ick, CK_242X),
|
||||
CLK(NULL, "wdt1_ick", &wdt1_ick, CK_242X),
|
||||
CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_242X),
|
||||
@ -1880,11 +1880,11 @@ static struct omap_clk omap2420_clks[] = {
|
||||
CLK(NULL, "eac_ick", &eac_ick, CK_242X),
|
||||
CLK(NULL, "eac_fck", &eac_fck, CK_242X),
|
||||
CLK("omap_hdq.0", "ick", &hdq_ick, CK_242X),
|
||||
CLK("omap_hdq.1", "fck", &hdq_fck, CK_242X),
|
||||
CLK("omap_hdq.0", "fck", &hdq_fck, CK_242X),
|
||||
CLK("omap_i2c.1", "ick", &i2c1_ick, CK_242X),
|
||||
CLK("omap_i2c.1", "fck", &i2c1_fck, CK_242X),
|
||||
CLK(NULL, "i2c1_fck", &i2c1_fck, CK_242X),
|
||||
CLK("omap_i2c.2", "ick", &i2c2_ick, CK_242X),
|
||||
CLK("omap_i2c.2", "fck", &i2c2_fck, CK_242X),
|
||||
CLK(NULL, "i2c2_fck", &i2c2_fck, CK_242X),
|
||||
CLK(NULL, "gpmc_fck", &gpmc_fck, CK_242X),
|
||||
CLK(NULL, "sdma_fck", &sdma_fck, CK_242X),
|
||||
CLK(NULL, "sdma_ick", &sdma_ick, CK_242X),
|
||||
|
@ -1895,9 +1895,9 @@ static struct omap_clk omap2430_clks[] = {
|
||||
CLK(NULL, "mdm_osc_ck", &mdm_osc_ck, CK_243X),
|
||||
/* DSS domain clocks */
|
||||
CLK("omapdss_dss", "ick", &dss_ick, CK_243X),
|
||||
CLK("omapdss_dss", "fck", &dss1_fck, CK_243X),
|
||||
CLK("omapdss_dss", "sys_clk", &dss2_fck, CK_243X),
|
||||
CLK("omapdss_dss", "tv_clk", &dss_54m_fck, CK_243X),
|
||||
CLK(NULL, "dss1_fck", &dss1_fck, CK_243X),
|
||||
CLK(NULL, "dss2_fck", &dss2_fck, CK_243X),
|
||||
CLK(NULL, "dss_54m_fck", &dss_54m_fck, CK_243X),
|
||||
/* L3 domain clocks */
|
||||
CLK(NULL, "core_l3_ck", &core_l3_ck, CK_243X),
|
||||
CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_243X),
|
||||
@ -1934,21 +1934,21 @@ static struct omap_clk omap2430_clks[] = {
|
||||
CLK(NULL, "gpt12_ick", &gpt12_ick, CK_243X),
|
||||
CLK(NULL, "gpt12_fck", &gpt12_fck, CK_243X),
|
||||
CLK("omap-mcbsp.1", "ick", &mcbsp1_ick, CK_243X),
|
||||
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_243X),
|
||||
CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_243X),
|
||||
CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_243X),
|
||||
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_243X),
|
||||
CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_243X),
|
||||
CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_243X),
|
||||
CLK("omap-mcbsp.3", "fck", &mcbsp3_fck, CK_243X),
|
||||
CLK(NULL, "mcbsp3_fck", &mcbsp3_fck, CK_243X),
|
||||
CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_243X),
|
||||
CLK("omap-mcbsp.4", "fck", &mcbsp4_fck, CK_243X),
|
||||
CLK(NULL, "mcbsp4_fck", &mcbsp4_fck, CK_243X),
|
||||
CLK("omap-mcbsp.5", "ick", &mcbsp5_ick, CK_243X),
|
||||
CLK("omap-mcbsp.5", "fck", &mcbsp5_fck, CK_243X),
|
||||
CLK(NULL, "mcbsp5_fck", &mcbsp5_fck, CK_243X),
|
||||
CLK("omap2_mcspi.1", "ick", &mcspi1_ick, CK_243X),
|
||||
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_243X),
|
||||
CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_243X),
|
||||
CLK("omap2_mcspi.2", "ick", &mcspi2_ick, CK_243X),
|
||||
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_243X),
|
||||
CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_243X),
|
||||
CLK("omap2_mcspi.3", "ick", &mcspi3_ick, CK_243X),
|
||||
CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_243X),
|
||||
CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_243X),
|
||||
CLK(NULL, "uart1_ick", &uart1_ick, CK_243X),
|
||||
CLK(NULL, "uart1_fck", &uart1_fck, CK_243X),
|
||||
CLK(NULL, "uart2_ick", &uart2_ick, CK_243X),
|
||||
@ -1958,7 +1958,7 @@ static struct omap_clk omap2430_clks[] = {
|
||||
CLK(NULL, "gpios_ick", &gpios_ick, CK_243X),
|
||||
CLK(NULL, "gpios_fck", &gpios_fck, CK_243X),
|
||||
CLK("omap_wdt", "ick", &mpu_wdt_ick, CK_243X),
|
||||
CLK("omap_wdt", "fck", &mpu_wdt_fck, CK_243X),
|
||||
CLK(NULL, "mpu_wdt_fck", &mpu_wdt_fck, CK_243X),
|
||||
CLK(NULL, "sync_32k_ick", &sync_32k_ick, CK_243X),
|
||||
CLK(NULL, "wdt1_ick", &wdt1_ick, CK_243X),
|
||||
CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_243X),
|
||||
@ -1975,9 +1975,9 @@ static struct omap_clk omap2430_clks[] = {
|
||||
CLK("omap_hdq.0", "ick", &hdq_ick, CK_243X),
|
||||
CLK("omap_hdq.1", "fck", &hdq_fck, CK_243X),
|
||||
CLK("omap_i2c.1", "ick", &i2c1_ick, CK_243X),
|
||||
CLK("omap_i2c.1", "fck", &i2chs1_fck, CK_243X),
|
||||
CLK(NULL, "i2chs1_fck", &i2chs1_fck, CK_243X),
|
||||
CLK("omap_i2c.2", "ick", &i2c2_ick, CK_243X),
|
||||
CLK("omap_i2c.2", "fck", &i2chs2_fck, CK_243X),
|
||||
CLK(NULL, "i2chs2_fck", &i2chs2_fck, CK_243X),
|
||||
CLK(NULL, "gpmc_fck", &gpmc_fck, CK_243X),
|
||||
CLK(NULL, "sdma_fck", &sdma_fck, CK_243X),
|
||||
CLK(NULL, "sdma_ick", &sdma_ick, CK_243X),
|
||||
@ -1990,9 +1990,9 @@ static struct omap_clk omap2430_clks[] = {
|
||||
CLK(NULL, "usb_fck", &usb_fck, CK_243X),
|
||||
CLK("musb-omap2430", "ick", &usbhs_ick, CK_243X),
|
||||
CLK("omap_hsmmc.0", "ick", &mmchs1_ick, CK_243X),
|
||||
CLK("omap_hsmmc.0", "fck", &mmchs1_fck, CK_243X),
|
||||
CLK(NULL, "mmchs1_fck", &mmchs1_fck, CK_243X),
|
||||
CLK("omap_hsmmc.1", "ick", &mmchs2_ick, CK_243X),
|
||||
CLK("omap_hsmmc.1", "fck", &mmchs2_fck, CK_243X),
|
||||
CLK(NULL, "mmchs2_fck", &mmchs2_fck, CK_243X),
|
||||
CLK(NULL, "gpio5_ick", &gpio5_ick, CK_243X),
|
||||
CLK(NULL, "gpio5_fck", &gpio5_fck, CK_243X),
|
||||
CLK(NULL, "mdm_intc_ick", &mdm_intc_ick, CK_243X),
|
||||
|
@ -3289,25 +3289,25 @@ static struct omap_clk omap3xxx_clks[] = {
|
||||
CLK("omap-mcbsp.1", "prcm_fck", &core_96m_fck, CK_3XXX),
|
||||
CLK("omap-mcbsp.5", "prcm_fck", &core_96m_fck, CK_3XXX),
|
||||
CLK(NULL, "core_96m_fck", &core_96m_fck, CK_3XXX),
|
||||
CLK("omap_hsmmc.2", "fck", &mmchs3_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK("omap_hsmmc.1", "fck", &mmchs2_fck, CK_3XXX),
|
||||
CLK(NULL, "mmchs3_fck", &mmchs3_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK(NULL, "mmchs2_fck", &mmchs2_fck, CK_3XXX),
|
||||
CLK(NULL, "mspro_fck", &mspro_fck, CK_34XX | CK_36XX),
|
||||
CLK("omap_hsmmc.0", "fck", &mmchs1_fck, CK_3XXX),
|
||||
CLK("omap_i2c.3", "fck", &i2c3_fck, CK_3XXX),
|
||||
CLK("omap_i2c.2", "fck", &i2c2_fck, CK_3XXX),
|
||||
CLK("omap_i2c.1", "fck", &i2c1_fck, CK_3XXX),
|
||||
CLK("omap-mcbsp.5", "fck", &mcbsp5_fck, CK_3XXX),
|
||||
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_3XXX),
|
||||
CLK(NULL, "mmchs1_fck", &mmchs1_fck, CK_3XXX),
|
||||
CLK(NULL, "i2c3_fck", &i2c3_fck, CK_3XXX),
|
||||
CLK(NULL, "i2c2_fck", &i2c2_fck, CK_3XXX),
|
||||
CLK(NULL, "i2c1_fck", &i2c1_fck, CK_3XXX),
|
||||
CLK(NULL, "mcbsp5_fck", &mcbsp5_fck, CK_3XXX),
|
||||
CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_3XXX),
|
||||
CLK(NULL, "core_48m_fck", &core_48m_fck, CK_3XXX),
|
||||
CLK("omap2_mcspi.4", "fck", &mcspi4_fck, CK_3XXX),
|
||||
CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_3XXX),
|
||||
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_3XXX),
|
||||
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_3XXX),
|
||||
CLK(NULL, "mcspi4_fck", &mcspi4_fck, CK_3XXX),
|
||||
CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_3XXX),
|
||||
CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_3XXX),
|
||||
CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_3XXX),
|
||||
CLK(NULL, "uart2_fck", &uart2_fck, CK_3XXX),
|
||||
CLK(NULL, "uart1_fck", &uart1_fck, CK_3XXX),
|
||||
CLK(NULL, "fshostusb_fck", &fshostusb_fck, CK_3430ES1),
|
||||
CLK(NULL, "core_12m_fck", &core_12m_fck, CK_3XXX),
|
||||
CLK("omap_hdq.0", "fck", &hdq_fck, CK_3XXX),
|
||||
CLK("omap_hdq.0", "fck", &hdq_fck, CK_3XXX),
|
||||
CLK(NULL, "ssi_ssr_fck", &ssi_ssr_fck_3430es1, CK_3430ES1),
|
||||
CLK(NULL, "ssi_ssr_fck", &ssi_ssr_fck_3430es2, CK_3430ES2PLUS | CK_36XX),
|
||||
CLK(NULL, "ssi_sst_fck", &ssi_sst_fck_3430es1, CK_3430ES1),
|
||||
@ -3356,11 +3356,11 @@ static struct omap_clk omap3xxx_clks[] = {
|
||||
CLK("omap_rng", "ick", &rng_ick, CK_34XX | CK_36XX),
|
||||
CLK(NULL, "sha11_ick", &sha11_ick, CK_34XX | CK_36XX),
|
||||
CLK(NULL, "des1_ick", &des1_ick, CK_34XX | CK_36XX),
|
||||
CLK("omapdss_dss", "fck", &dss1_alwon_fck_3430es1, CK_3430ES1),
|
||||
CLK("omapdss_dss", "fck", &dss1_alwon_fck_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK("omapdss_dss", "tv_clk", &dss_tv_fck, CK_3XXX),
|
||||
CLK("omapdss_dss", "video_clk", &dss_96m_fck, CK_3XXX),
|
||||
CLK("omapdss_dss", "sys_clk", &dss2_alwon_fck, CK_3XXX),
|
||||
CLK(NULL, "dss1_alwon_fck", &dss1_alwon_fck_3430es1, CK_3430ES1),
|
||||
CLK(NULL, "dss1_alwon_fck", &dss1_alwon_fck_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK(NULL, "dss_tv_fck", &dss_tv_fck, CK_3XXX),
|
||||
CLK(NULL, "dss_96m_fck", &dss_96m_fck, CK_3XXX),
|
||||
CLK(NULL, "dss2_alwon_fck", &dss2_alwon_fck, CK_3XXX),
|
||||
CLK("omapdss_dss", "ick", &dss_ick_3430es1, CK_3430ES1),
|
||||
CLK("omapdss_dss", "ick", &dss_ick_3430es2, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK(NULL, "cam_mclk", &cam_mclk, CK_34XX | CK_36XX),
|
||||
@ -3385,7 +3385,7 @@ static struct omap_clk omap3xxx_clks[] = {
|
||||
CLK(NULL, "gpt1_fck", &gpt1_fck, CK_3XXX),
|
||||
CLK(NULL, "wkup_32k_fck", &wkup_32k_fck, CK_3XXX),
|
||||
CLK(NULL, "gpio1_dbck", &gpio1_dbck, CK_3XXX),
|
||||
CLK("omap_wdt", "fck", &wdt2_fck, CK_3XXX),
|
||||
CLK(NULL, "wdt2_fck", &wdt2_fck, CK_3XXX),
|
||||
CLK(NULL, "wkup_l4_ick", &wkup_l4_ick, CK_34XX | CK_36XX),
|
||||
CLK(NULL, "usim_ick", &usim_ick, CK_3430ES2PLUS | CK_36XX),
|
||||
CLK("omap_wdt", "ick", &wdt2_ick, CK_3XXX),
|
||||
@ -3436,9 +3436,9 @@ static struct omap_clk omap3xxx_clks[] = {
|
||||
CLK("omap-mcbsp.2", "ick", &mcbsp2_ick, CK_3XXX),
|
||||
CLK("omap-mcbsp.3", "ick", &mcbsp3_ick, CK_3XXX),
|
||||
CLK("omap-mcbsp.4", "ick", &mcbsp4_ick, CK_3XXX),
|
||||
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_3XXX),
|
||||
CLK("omap-mcbsp.3", "fck", &mcbsp3_fck, CK_3XXX),
|
||||
CLK("omap-mcbsp.4", "fck", &mcbsp4_fck, CK_3XXX),
|
||||
CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_3XXX),
|
||||
CLK(NULL, "mcbsp3_fck", &mcbsp3_fck, CK_3XXX),
|
||||
CLK(NULL, "mcbsp4_fck", &mcbsp4_fck, CK_3XXX),
|
||||
CLK("etb", "emu_src_ck", &emu_src_ck, CK_3XXX),
|
||||
CLK(NULL, "pclk_fck", &pclk_fck, CK_3XXX),
|
||||
CLK(NULL, "pclkx2_fck", &pclkx2_fck, CK_3XXX),
|
||||
|
@ -2808,19 +2808,39 @@ static struct clk trace_clk_div_ck = {
|
||||
|
||||
/* SCRM aux clk nodes */
|
||||
|
||||
static const struct clksel auxclk_sel[] = {
|
||||
static const struct clksel auxclk_src_sel[] = {
|
||||
{ .parent = &sys_clkin_ck, .rates = div_1_0_rates },
|
||||
{ .parent = &dpll_core_m3x2_ck, .rates = div_1_1_rates },
|
||||
{ .parent = &dpll_per_m3x2_ck, .rates = div_1_2_rates },
|
||||
{ .parent = NULL },
|
||||
};
|
||||
|
||||
static struct clk auxclk0_ck = {
|
||||
.name = "auxclk0_ck",
|
||||
static const struct clksel_rate div16_1to16_rates[] = {
|
||||
{ .div = 1, .val = 0, .flags = RATE_IN_4430 },
|
||||
{ .div = 2, .val = 1, .flags = RATE_IN_4430 },
|
||||
{ .div = 3, .val = 2, .flags = RATE_IN_4430 },
|
||||
{ .div = 4, .val = 3, .flags = RATE_IN_4430 },
|
||||
{ .div = 5, .val = 4, .flags = RATE_IN_4430 },
|
||||
{ .div = 6, .val = 5, .flags = RATE_IN_4430 },
|
||||
{ .div = 7, .val = 6, .flags = RATE_IN_4430 },
|
||||
{ .div = 8, .val = 7, .flags = RATE_IN_4430 },
|
||||
{ .div = 9, .val = 8, .flags = RATE_IN_4430 },
|
||||
{ .div = 10, .val = 9, .flags = RATE_IN_4430 },
|
||||
{ .div = 11, .val = 10, .flags = RATE_IN_4430 },
|
||||
{ .div = 12, .val = 11, .flags = RATE_IN_4430 },
|
||||
{ .div = 13, .val = 12, .flags = RATE_IN_4430 },
|
||||
{ .div = 14, .val = 13, .flags = RATE_IN_4430 },
|
||||
{ .div = 15, .val = 14, .flags = RATE_IN_4430 },
|
||||
{ .div = 16, .val = 15, .flags = RATE_IN_4430 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static struct clk auxclk0_src_ck = {
|
||||
.name = "auxclk0_src_ck",
|
||||
.parent = &sys_clkin_ck,
|
||||
.init = &omap2_init_clksel_parent,
|
||||
.ops = &clkops_omap2_dflt,
|
||||
.clksel = auxclk_sel,
|
||||
.clksel = auxclk_src_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK0,
|
||||
.clksel_mask = OMAP4_SRCSELECT_MASK,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
@ -2828,12 +2848,29 @@ static struct clk auxclk0_ck = {
|
||||
.enable_bit = OMAP4_ENABLE_SHIFT,
|
||||
};
|
||||
|
||||
static struct clk auxclk1_ck = {
|
||||
.name = "auxclk1_ck",
|
||||
static const struct clksel auxclk0_sel[] = {
|
||||
{ .parent = &auxclk0_src_ck, .rates = div16_1to16_rates },
|
||||
{ .parent = NULL },
|
||||
};
|
||||
|
||||
static struct clk auxclk0_ck = {
|
||||
.name = "auxclk0_ck",
|
||||
.parent = &auxclk0_src_ck,
|
||||
.clksel = auxclk0_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK0,
|
||||
.clksel_mask = OMAP4_CLKDIV_MASK,
|
||||
.ops = &clkops_null,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
.round_rate = &omap2_clksel_round_rate,
|
||||
.set_rate = &omap2_clksel_set_rate,
|
||||
};
|
||||
|
||||
static struct clk auxclk1_src_ck = {
|
||||
.name = "auxclk1_src_ck",
|
||||
.parent = &sys_clkin_ck,
|
||||
.init = &omap2_init_clksel_parent,
|
||||
.ops = &clkops_omap2_dflt,
|
||||
.clksel = auxclk_sel,
|
||||
.clksel = auxclk_src_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK1,
|
||||
.clksel_mask = OMAP4_SRCSELECT_MASK,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
@ -2841,12 +2878,29 @@ static struct clk auxclk1_ck = {
|
||||
.enable_bit = OMAP4_ENABLE_SHIFT,
|
||||
};
|
||||
|
||||
static struct clk auxclk2_ck = {
|
||||
.name = "auxclk2_ck",
|
||||
static const struct clksel auxclk1_sel[] = {
|
||||
{ .parent = &auxclk1_src_ck, .rates = div16_1to16_rates },
|
||||
{ .parent = NULL },
|
||||
};
|
||||
|
||||
static struct clk auxclk1_ck = {
|
||||
.name = "auxclk1_ck",
|
||||
.parent = &auxclk1_src_ck,
|
||||
.clksel = auxclk1_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK1,
|
||||
.clksel_mask = OMAP4_CLKDIV_MASK,
|
||||
.ops = &clkops_null,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
.round_rate = &omap2_clksel_round_rate,
|
||||
.set_rate = &omap2_clksel_set_rate,
|
||||
};
|
||||
|
||||
static struct clk auxclk2_src_ck = {
|
||||
.name = "auxclk2_src_ck",
|
||||
.parent = &sys_clkin_ck,
|
||||
.init = &omap2_init_clksel_parent,
|
||||
.ops = &clkops_omap2_dflt,
|
||||
.clksel = auxclk_sel,
|
||||
.clksel = auxclk_src_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK2,
|
||||
.clksel_mask = OMAP4_SRCSELECT_MASK,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
@ -2854,12 +2908,29 @@ static struct clk auxclk2_ck = {
|
||||
.enable_bit = OMAP4_ENABLE_SHIFT,
|
||||
};
|
||||
|
||||
static struct clk auxclk3_ck = {
|
||||
.name = "auxclk3_ck",
|
||||
static const struct clksel auxclk2_sel[] = {
|
||||
{ .parent = &auxclk2_src_ck, .rates = div16_1to16_rates },
|
||||
{ .parent = NULL },
|
||||
};
|
||||
|
||||
static struct clk auxclk2_ck = {
|
||||
.name = "auxclk2_ck",
|
||||
.parent = &auxclk2_src_ck,
|
||||
.clksel = auxclk2_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK2,
|
||||
.clksel_mask = OMAP4_CLKDIV_MASK,
|
||||
.ops = &clkops_null,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
.round_rate = &omap2_clksel_round_rate,
|
||||
.set_rate = &omap2_clksel_set_rate,
|
||||
};
|
||||
|
||||
static struct clk auxclk3_src_ck = {
|
||||
.name = "auxclk3_src_ck",
|
||||
.parent = &sys_clkin_ck,
|
||||
.init = &omap2_init_clksel_parent,
|
||||
.ops = &clkops_omap2_dflt,
|
||||
.clksel = auxclk_sel,
|
||||
.clksel = auxclk_src_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK3,
|
||||
.clksel_mask = OMAP4_SRCSELECT_MASK,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
@ -2867,12 +2938,29 @@ static struct clk auxclk3_ck = {
|
||||
.enable_bit = OMAP4_ENABLE_SHIFT,
|
||||
};
|
||||
|
||||
static struct clk auxclk4_ck = {
|
||||
.name = "auxclk4_ck",
|
||||
static const struct clksel auxclk3_sel[] = {
|
||||
{ .parent = &auxclk3_src_ck, .rates = div16_1to16_rates },
|
||||
{ .parent = NULL },
|
||||
};
|
||||
|
||||
static struct clk auxclk3_ck = {
|
||||
.name = "auxclk3_ck",
|
||||
.parent = &auxclk3_src_ck,
|
||||
.clksel = auxclk3_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK3,
|
||||
.clksel_mask = OMAP4_CLKDIV_MASK,
|
||||
.ops = &clkops_null,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
.round_rate = &omap2_clksel_round_rate,
|
||||
.set_rate = &omap2_clksel_set_rate,
|
||||
};
|
||||
|
||||
static struct clk auxclk4_src_ck = {
|
||||
.name = "auxclk4_src_ck",
|
||||
.parent = &sys_clkin_ck,
|
||||
.init = &omap2_init_clksel_parent,
|
||||
.ops = &clkops_omap2_dflt,
|
||||
.clksel = auxclk_sel,
|
||||
.clksel = auxclk_src_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK4,
|
||||
.clksel_mask = OMAP4_SRCSELECT_MASK,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
@ -2880,12 +2968,29 @@ static struct clk auxclk4_ck = {
|
||||
.enable_bit = OMAP4_ENABLE_SHIFT,
|
||||
};
|
||||
|
||||
static struct clk auxclk5_ck = {
|
||||
.name = "auxclk5_ck",
|
||||
static const struct clksel auxclk4_sel[] = {
|
||||
{ .parent = &auxclk4_src_ck, .rates = div16_1to16_rates },
|
||||
{ .parent = NULL },
|
||||
};
|
||||
|
||||
static struct clk auxclk4_ck = {
|
||||
.name = "auxclk4_ck",
|
||||
.parent = &auxclk4_src_ck,
|
||||
.clksel = auxclk4_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK4,
|
||||
.clksel_mask = OMAP4_CLKDIV_MASK,
|
||||
.ops = &clkops_null,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
.round_rate = &omap2_clksel_round_rate,
|
||||
.set_rate = &omap2_clksel_set_rate,
|
||||
};
|
||||
|
||||
static struct clk auxclk5_src_ck = {
|
||||
.name = "auxclk5_src_ck",
|
||||
.parent = &sys_clkin_ck,
|
||||
.init = &omap2_init_clksel_parent,
|
||||
.ops = &clkops_omap2_dflt,
|
||||
.clksel = auxclk_sel,
|
||||
.clksel = auxclk_src_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK5,
|
||||
.clksel_mask = OMAP4_SRCSELECT_MASK,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
@ -2893,6 +2998,23 @@ static struct clk auxclk5_ck = {
|
||||
.enable_bit = OMAP4_ENABLE_SHIFT,
|
||||
};
|
||||
|
||||
static const struct clksel auxclk5_sel[] = {
|
||||
{ .parent = &auxclk5_src_ck, .rates = div16_1to16_rates },
|
||||
{ .parent = NULL },
|
||||
};
|
||||
|
||||
static struct clk auxclk5_ck = {
|
||||
.name = "auxclk5_ck",
|
||||
.parent = &auxclk5_src_ck,
|
||||
.clksel = auxclk5_sel,
|
||||
.clksel_reg = OMAP4_SCRM_AUXCLK5,
|
||||
.clksel_mask = OMAP4_CLKDIV_MASK,
|
||||
.ops = &clkops_null,
|
||||
.recalc = &omap2_clksel_recalc,
|
||||
.round_rate = &omap2_clksel_round_rate,
|
||||
.set_rate = &omap2_clksel_set_rate,
|
||||
};
|
||||
|
||||
static const struct clksel auxclkreq_sel[] = {
|
||||
{ .parent = &auxclk0_ck, .rates = div_1_0_rates },
|
||||
{ .parent = &auxclk1_ck, .rates = div_1_1_rates },
|
||||
@ -3093,12 +3215,12 @@ static struct omap_clk omap44xx_clks[] = {
|
||||
CLK(NULL, "gpio6_ick", &gpio6_ick, CK_443X),
|
||||
CLK(NULL, "gpmc_ick", &gpmc_ick, CK_443X),
|
||||
CLK(NULL, "gpu_fck", &gpu_fck, CK_443X),
|
||||
CLK("omap2_hdq.0", "fck", &hdq1w_fck, CK_443X),
|
||||
CLK(NULL, "hdq1w_fck", &hdq1w_fck, CK_443X),
|
||||
CLK(NULL, "hsi_fck", &hsi_fck, CK_443X),
|
||||
CLK("omap_i2c.1", "fck", &i2c1_fck, CK_443X),
|
||||
CLK("omap_i2c.2", "fck", &i2c2_fck, CK_443X),
|
||||
CLK("omap_i2c.3", "fck", &i2c3_fck, CK_443X),
|
||||
CLK("omap_i2c.4", "fck", &i2c4_fck, CK_443X),
|
||||
CLK(NULL, "i2c1_fck", &i2c1_fck, CK_443X),
|
||||
CLK(NULL, "i2c2_fck", &i2c2_fck, CK_443X),
|
||||
CLK(NULL, "i2c3_fck", &i2c3_fck, CK_443X),
|
||||
CLK(NULL, "i2c4_fck", &i2c4_fck, CK_443X),
|
||||
CLK(NULL, "ipu_fck", &ipu_fck, CK_443X),
|
||||
CLK(NULL, "iss_ctrlclk", &iss_ctrlclk, CK_443X),
|
||||
CLK(NULL, "iss_fck", &iss_fck, CK_443X),
|
||||
@ -3109,23 +3231,23 @@ static struct omap_clk omap44xx_clks[] = {
|
||||
CLK(NULL, "mcasp_sync_mux_ck", &mcasp_sync_mux_ck, CK_443X),
|
||||
CLK(NULL, "mcasp_fck", &mcasp_fck, CK_443X),
|
||||
CLK(NULL, "mcbsp1_sync_mux_ck", &mcbsp1_sync_mux_ck, CK_443X),
|
||||
CLK("omap-mcbsp.1", "fck", &mcbsp1_fck, CK_443X),
|
||||
CLK(NULL, "mcbsp1_fck", &mcbsp1_fck, CK_443X),
|
||||
CLK(NULL, "mcbsp2_sync_mux_ck", &mcbsp2_sync_mux_ck, CK_443X),
|
||||
CLK("omap-mcbsp.2", "fck", &mcbsp2_fck, CK_443X),
|
||||
CLK(NULL, "mcbsp2_fck", &mcbsp2_fck, CK_443X),
|
||||
CLK(NULL, "mcbsp3_sync_mux_ck", &mcbsp3_sync_mux_ck, CK_443X),
|
||||
CLK("omap-mcbsp.3", "fck", &mcbsp3_fck, CK_443X),
|
||||
CLK(NULL, "mcbsp3_fck", &mcbsp3_fck, CK_443X),
|
||||
CLK(NULL, "mcbsp4_sync_mux_ck", &mcbsp4_sync_mux_ck, CK_443X),
|
||||
CLK("omap-mcbsp.4", "fck", &mcbsp4_fck, CK_443X),
|
||||
CLK(NULL, "mcbsp4_fck", &mcbsp4_fck, CK_443X),
|
||||
CLK(NULL, "mcpdm_fck", &mcpdm_fck, CK_443X),
|
||||
CLK("omap2_mcspi.1", "fck", &mcspi1_fck, CK_443X),
|
||||
CLK("omap2_mcspi.2", "fck", &mcspi2_fck, CK_443X),
|
||||
CLK("omap2_mcspi.3", "fck", &mcspi3_fck, CK_443X),
|
||||
CLK("omap2_mcspi.4", "fck", &mcspi4_fck, CK_443X),
|
||||
CLK("omap_hsmmc.0", "fck", &mmc1_fck, CK_443X),
|
||||
CLK("omap_hsmmc.1", "fck", &mmc2_fck, CK_443X),
|
||||
CLK("omap_hsmmc.2", "fck", &mmc3_fck, CK_443X),
|
||||
CLK("omap_hsmmc.3", "fck", &mmc4_fck, CK_443X),
|
||||
CLK("omap_hsmmc.4", "fck", &mmc5_fck, CK_443X),
|
||||
CLK(NULL, "mcspi1_fck", &mcspi1_fck, CK_443X),
|
||||
CLK(NULL, "mcspi2_fck", &mcspi2_fck, CK_443X),
|
||||
CLK(NULL, "mcspi3_fck", &mcspi3_fck, CK_443X),
|
||||
CLK(NULL, "mcspi4_fck", &mcspi4_fck, CK_443X),
|
||||
CLK(NULL, "mmc1_fck", &mmc1_fck, CK_443X),
|
||||
CLK(NULL, "mmc2_fck", &mmc2_fck, CK_443X),
|
||||
CLK(NULL, "mmc3_fck", &mmc3_fck, CK_443X),
|
||||
CLK(NULL, "mmc4_fck", &mmc4_fck, CK_443X),
|
||||
CLK(NULL, "mmc5_fck", &mmc5_fck, CK_443X),
|
||||
CLK(NULL, "ocp2scp_usb_phy_phy_48m", &ocp2scp_usb_phy_phy_48m, CK_443X),
|
||||
CLK(NULL, "ocp2scp_usb_phy_ick", &ocp2scp_usb_phy_ick, CK_443X),
|
||||
CLK(NULL, "ocp_wp_noc_ick", &ocp_wp_noc_ick, CK_443X),
|
||||
@ -3182,21 +3304,27 @@ static struct omap_clk omap44xx_clks[] = {
|
||||
CLK(NULL, "usim_ck", &usim_ck, CK_443X),
|
||||
CLK(NULL, "usim_fclk", &usim_fclk, CK_443X),
|
||||
CLK(NULL, "usim_fck", &usim_fck, CK_443X),
|
||||
CLK("omap_wdt", "fck", &wd_timer2_fck, CK_443X),
|
||||
CLK(NULL, "wd_timer2_fck", &wd_timer2_fck, CK_443X),
|
||||
CLK(NULL, "wd_timer3_fck", &wd_timer3_fck, CK_443X),
|
||||
CLK(NULL, "stm_clk_div_ck", &stm_clk_div_ck, CK_443X),
|
||||
CLK(NULL, "trace_clk_div_ck", &trace_clk_div_ck, CK_443X),
|
||||
CLK(NULL, "auxclk0_src_ck", &auxclk0_src_ck, CK_443X),
|
||||
CLK(NULL, "auxclk0_ck", &auxclk0_ck, CK_443X),
|
||||
CLK(NULL, "auxclk1_ck", &auxclk1_ck, CK_443X),
|
||||
CLK(NULL, "auxclk2_ck", &auxclk2_ck, CK_443X),
|
||||
CLK(NULL, "auxclk3_ck", &auxclk3_ck, CK_443X),
|
||||
CLK(NULL, "auxclk4_ck", &auxclk4_ck, CK_443X),
|
||||
CLK(NULL, "auxclk5_ck", &auxclk5_ck, CK_443X),
|
||||
CLK(NULL, "auxclkreq0_ck", &auxclkreq0_ck, CK_443X),
|
||||
CLK(NULL, "auxclk1_src_ck", &auxclk1_src_ck, CK_443X),
|
||||
CLK(NULL, "auxclk1_ck", &auxclk1_ck, CK_443X),
|
||||
CLK(NULL, "auxclkreq1_ck", &auxclkreq1_ck, CK_443X),
|
||||
CLK(NULL, "auxclk2_src_ck", &auxclk2_src_ck, CK_443X),
|
||||
CLK(NULL, "auxclk2_ck", &auxclk2_ck, CK_443X),
|
||||
CLK(NULL, "auxclkreq2_ck", &auxclkreq2_ck, CK_443X),
|
||||
CLK(NULL, "auxclk3_src_ck", &auxclk3_src_ck, CK_443X),
|
||||
CLK(NULL, "auxclk3_ck", &auxclk3_ck, CK_443X),
|
||||
CLK(NULL, "auxclkreq3_ck", &auxclkreq3_ck, CK_443X),
|
||||
CLK(NULL, "auxclk4_src_ck", &auxclk4_src_ck, CK_443X),
|
||||
CLK(NULL, "auxclk4_ck", &auxclk4_ck, CK_443X),
|
||||
CLK(NULL, "auxclkreq4_ck", &auxclkreq4_ck, CK_443X),
|
||||
CLK(NULL, "auxclk5_src_ck", &auxclk5_src_ck, CK_443X),
|
||||
CLK(NULL, "auxclk5_ck", &auxclk5_ck, CK_443X),
|
||||
CLK(NULL, "auxclkreq5_ck", &auxclkreq5_ck, CK_443X),
|
||||
CLK(NULL, "gpmc_ck", &dummy_ck, CK_443X),
|
||||
CLK(NULL, "gpt1_ick", &dummy_ck, CK_443X),
|
||||
@ -3251,6 +3379,7 @@ int __init omap4xxx_clk_init(void)
|
||||
}
|
||||
|
||||
clk_init(&omap2_clk_functions);
|
||||
omap2_clk_disable_clkdm_control();
|
||||
|
||||
for (c = omap44xx_clks; c < omap44xx_clks + ARRAY_SIZE(omap44xx_clks);
|
||||
c++)
|
||||
|
@ -1,8 +1,8 @@
|
||||
/*
|
||||
* OMAP2/3/4 clockdomain framework functions
|
||||
*
|
||||
* Copyright (C) 2008-2010 Texas Instruments, Inc.
|
||||
* Copyright (C) 2008-2010 Nokia Corporation
|
||||
* Copyright (C) 2008-2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2008-2011 Nokia Corporation
|
||||
*
|
||||
* Written by Paul Walmsley and Jouni Högander
|
||||
* Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
|
||||
@ -92,6 +92,8 @@ static int _clkdm_register(struct clockdomain *clkdm)
|
||||
|
||||
pwrdm_add_clkdm(pwrdm, clkdm);
|
||||
|
||||
spin_lock_init(&clkdm->lock);
|
||||
|
||||
pr_debug("clockdomain: registered %s\n", clkdm->name);
|
||||
|
||||
return 0;
|
||||
@ -690,6 +692,9 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
|
||||
*/
|
||||
int clkdm_sleep(struct clockdomain *clkdm)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
if (!clkdm)
|
||||
return -EINVAL;
|
||||
|
||||
@ -704,7 +709,11 @@ int clkdm_sleep(struct clockdomain *clkdm)
|
||||
|
||||
pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
|
||||
|
||||
return arch_clkdm->clkdm_sleep(clkdm);
|
||||
spin_lock_irqsave(&clkdm->lock, flags);
|
||||
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
|
||||
ret = arch_clkdm->clkdm_sleep(clkdm);
|
||||
spin_unlock_irqrestore(&clkdm->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -718,6 +727,9 @@ int clkdm_sleep(struct clockdomain *clkdm)
|
||||
*/
|
||||
int clkdm_wakeup(struct clockdomain *clkdm)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
if (!clkdm)
|
||||
return -EINVAL;
|
||||
|
||||
@ -732,7 +744,11 @@ int clkdm_wakeup(struct clockdomain *clkdm)
|
||||
|
||||
pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
|
||||
|
||||
return arch_clkdm->clkdm_wakeup(clkdm);
|
||||
spin_lock_irqsave(&clkdm->lock, flags);
|
||||
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
|
||||
ret = arch_clkdm->clkdm_wakeup(clkdm);
|
||||
spin_unlock_irqrestore(&clkdm->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -747,6 +763,8 @@ int clkdm_wakeup(struct clockdomain *clkdm)
|
||||
*/
|
||||
void clkdm_allow_idle(struct clockdomain *clkdm)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!clkdm)
|
||||
return;
|
||||
|
||||
@ -762,8 +780,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm)
|
||||
pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
|
||||
clkdm->name);
|
||||
|
||||
spin_lock_irqsave(&clkdm->lock, flags);
|
||||
clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED;
|
||||
arch_clkdm->clkdm_allow_idle(clkdm);
|
||||
pwrdm_clkdm_state_switch(clkdm);
|
||||
spin_unlock_irqrestore(&clkdm->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -777,6 +798,8 @@ void clkdm_allow_idle(struct clockdomain *clkdm)
|
||||
*/
|
||||
void clkdm_deny_idle(struct clockdomain *clkdm)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!clkdm)
|
||||
return;
|
||||
|
||||
@ -792,11 +815,90 @@ void clkdm_deny_idle(struct clockdomain *clkdm)
|
||||
pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
|
||||
clkdm->name);
|
||||
|
||||
spin_lock_irqsave(&clkdm->lock, flags);
|
||||
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
|
||||
arch_clkdm->clkdm_deny_idle(clkdm);
|
||||
spin_unlock_irqrestore(&clkdm->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* clkdm_in_hwsup - is clockdomain @clkdm have hardware-supervised idle enabled?
|
||||
* @clkdm: struct clockdomain *
|
||||
*
|
||||
* Returns true if clockdomain @clkdm currently has
|
||||
* hardware-supervised idle enabled, or false if it does not or if
|
||||
* @clkdm is NULL. It is only valid to call this function after
|
||||
* clkdm_init() has been called. This function does not actually read
|
||||
* bits from the hardware; it instead tests an in-memory flag that is
|
||||
* changed whenever the clockdomain code changes the auto-idle mode.
|
||||
*/
|
||||
bool clkdm_in_hwsup(struct clockdomain *clkdm)
|
||||
{
|
||||
bool ret;
|
||||
unsigned long flags;
|
||||
|
||||
/* 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
|
||||
@ -819,25 +921,10 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
|
||||
* downstream clocks for debugging purposes?
|
||||
*/
|
||||
|
||||
if (!clkdm || !clk)
|
||||
if (!clk)
|
||||
return -EINVAL;
|
||||
|
||||
if (!arch_clkdm || !arch_clkdm->clkdm_clk_enable)
|
||||
return -EINVAL;
|
||||
|
||||
if (atomic_inc_return(&clkdm->usecount) > 1)
|
||||
return 0;
|
||||
|
||||
/* Clockdomain now has one enabled downstream clock */
|
||||
|
||||
pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name,
|
||||
clk->name);
|
||||
|
||||
arch_clkdm->clkdm_clk_enable(clkdm);
|
||||
pwrdm_wait_transition(clkdm->pwrdm.ptr);
|
||||
pwrdm_clkdm_state_switch(clkdm);
|
||||
|
||||
return 0;
|
||||
return _clkdm_clk_hwmod_enable(clkdm);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -850,9 +937,8 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
|
||||
* clockdomain usecount goes to 0, put the clockdomain to sleep
|
||||
* (software-supervised mode) or remove the clkdm autodependencies
|
||||
* (hardware-supervised mode). Returns -EINVAL if passed null
|
||||
* pointers; -ERANGE if the @clkdm usecount underflows and debugging
|
||||
* is enabled; or returns 0 upon success or if the clockdomain is in
|
||||
* hwsup idle mode.
|
||||
* pointers; -ERANGE if the @clkdm usecount underflows; or returns 0
|
||||
* upon success or if the clockdomain is in hwsup idle mode.
|
||||
*/
|
||||
int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
|
||||
{
|
||||
@ -861,30 +947,72 @@ int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
|
||||
* downstream clocks for debugging purposes?
|
||||
*/
|
||||
|
||||
if (!clkdm || !clk)
|
||||
if (!clk)
|
||||
return -EINVAL;
|
||||
|
||||
if (!arch_clkdm || !arch_clkdm->clkdm_clk_disable)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (atomic_read(&clkdm->usecount) == 0) {
|
||||
WARN_ON(1); /* underflow */
|
||||
return -ERANGE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (atomic_dec_return(&clkdm->usecount) > 0)
|
||||
return 0;
|
||||
|
||||
/* All downstream clocks of this clockdomain are now disabled */
|
||||
|
||||
pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name,
|
||||
clk->name);
|
||||
|
||||
arch_clkdm->clkdm_clk_disable(clkdm);
|
||||
pwrdm_clkdm_state_switch(clkdm);
|
||||
|
||||
return 0;
|
||||
return _clkdm_clk_hwmod_disable(clkdm);
|
||||
}
|
||||
|
||||
/**
|
||||
* clkdm_hwmod_enable - add an enabled downstream hwmod to this clkdm
|
||||
* @clkdm: struct clockdomain *
|
||||
* @oh: struct omap_hwmod * of the enabled downstream hwmod
|
||||
*
|
||||
* Increment the usecount of the clockdomain @clkdm and ensure that it
|
||||
* is awake before @oh is enabled. Intended to be called by
|
||||
* module_enable() code.
|
||||
* If the clockdomain is in software-supervised idle mode, force the
|
||||
* clockdomain to wake. If the clockdomain is in hardware-supervised idle
|
||||
* mode, add clkdm-pwrdm autodependencies, to ensure that devices in the
|
||||
* clockdomain can be read from/written to by on-chip processors.
|
||||
* Returns -EINVAL if passed null pointers;
|
||||
* returns 0 upon success or if the clockdomain is in hwsup idle mode.
|
||||
*/
|
||||
int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh)
|
||||
{
|
||||
/* The clkdm attribute does not exist yet prior OMAP4 */
|
||||
if (cpu_is_omap24xx() || cpu_is_omap34xx())
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* XXX Rewrite this code to maintain a list of enabled
|
||||
* downstream hwmods for debugging purposes?
|
||||
*/
|
||||
|
||||
if (!oh)
|
||||
return -EINVAL;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,11 @@
|
||||
#define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "powerdomain.h"
|
||||
#include <plat/clock.h>
|
||||
#include <plat/omap_hwmod.h>
|
||||
#include <plat/cpu.h>
|
||||
|
||||
/*
|
||||
@ -82,6 +84,9 @@ struct clkdm_dep {
|
||||
const struct omap_chip_id omap_chip;
|
||||
};
|
||||
|
||||
/* Possible flags for struct clockdomain._flags */
|
||||
#define _CLKDM_FLAG_HWSUP_ENABLED BIT(0)
|
||||
|
||||
/**
|
||||
* struct clockdomain - OMAP clockdomain
|
||||
* @name: clockdomain name
|
||||
@ -89,6 +94,7 @@ struct clkdm_dep {
|
||||
* @clktrctrl_reg: CLKSTCTRL reg for the given clock domain
|
||||
* @clktrctrl_mask: CLKTRCTRL/AUTOSTATE field mask in CM_CLKSTCTRL reg
|
||||
* @flags: Clockdomain capability flags
|
||||
* @_flags: Flags for use only by internal clockdomain code
|
||||
* @dep_bit: Bit shift of this clockdomain's PM_WKDEP/CM_SLEEPDEP bit
|
||||
* @prcm_partition: (OMAP4 only) PRCM partition ID for this clkdm's registers
|
||||
* @cm_inst: (OMAP4 only) CM instance register offset
|
||||
@ -113,6 +119,7 @@ struct clockdomain {
|
||||
} pwrdm;
|
||||
const u16 clktrctrl_mask;
|
||||
const u8 flags;
|
||||
u8 _flags;
|
||||
const u8 dep_bit;
|
||||
const u8 prcm_partition;
|
||||
const s16 cm_inst;
|
||||
@ -122,6 +129,7 @@ struct clockdomain {
|
||||
const struct omap_chip_id omap_chip;
|
||||
atomic_t usecount;
|
||||
struct list_head node;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -177,12 +185,15 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm);
|
||||
|
||||
void clkdm_allow_idle(struct clockdomain *clkdm);
|
||||
void clkdm_deny_idle(struct clockdomain *clkdm);
|
||||
bool clkdm_in_hwsup(struct clockdomain *clkdm);
|
||||
|
||||
int clkdm_wakeup(struct clockdomain *clkdm);
|
||||
int clkdm_sleep(struct clockdomain *clkdm);
|
||||
|
||||
int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk);
|
||||
int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk);
|
||||
int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh);
|
||||
int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh);
|
||||
|
||||
extern void __init omap2xxx_clockdomains_init(void);
|
||||
extern void __init omap3xxx_clockdomains_init(void);
|
||||
|
@ -183,7 +183,8 @@ static int omap2_clkdm_clk_enable(struct clockdomain *clkdm)
|
||||
_clkdm_add_autodeps(clkdm);
|
||||
_enable_hwsup(clkdm);
|
||||
} else {
|
||||
clkdm_wakeup(clkdm);
|
||||
if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
|
||||
omap2_clkdm_wakeup(clkdm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -205,7 +206,8 @@ static int omap2_clkdm_clk_disable(struct clockdomain *clkdm)
|
||||
_clkdm_del_autodeps(clkdm);
|
||||
_enable_hwsup(clkdm);
|
||||
} else {
|
||||
clkdm_sleep(clkdm);
|
||||
if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
|
||||
omap2_clkdm_sleep(clkdm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -95,13 +95,8 @@ static void omap4_clkdm_deny_idle(struct clockdomain *clkdm)
|
||||
|
||||
static int omap4_clkdm_clk_enable(struct clockdomain *clkdm)
|
||||
{
|
||||
bool hwsup = false;
|
||||
|
||||
hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition,
|
||||
clkdm->cm_inst, clkdm->clkdm_offs);
|
||||
|
||||
if (!hwsup)
|
||||
clkdm_wakeup(clkdm);
|
||||
if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
|
||||
return omap4_clkdm_wakeup(clkdm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -113,8 +108,8 @@ static int omap4_clkdm_clk_disable(struct clockdomain *clkdm)
|
||||
hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition,
|
||||
clkdm->cm_inst, clkdm->clkdm_offs);
|
||||
|
||||
if (!hwsup)
|
||||
clkdm_sleep(clkdm);
|
||||
if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
|
||||
omap4_clkdm_sleep(clkdm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -565,7 +565,7 @@ static struct clockdomain ducati_44xx_clkdm = {
|
||||
};
|
||||
|
||||
static struct clockdomain mpu_44xx_clkdm = {
|
||||
.name = "mpu_clkdm",
|
||||
.name = "mpuss_clkdm",
|
||||
.pwrdm = { .name = "mpu_pwrdm" },
|
||||
.prcm_partition = OMAP4430_CM1_PARTITION,
|
||||
.cm_inst = OMAP4430_CM1_MPU_INST,
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* OMAP4 Clock Management (CM) definitions
|
||||
*
|
||||
* Copyright (C) 2007-2009 Texas Instruments, Inc.
|
||||
* Copyright (C) 2007-2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2007-2009 Nokia Corporation
|
||||
*
|
||||
* Written by Paul Walmsley
|
||||
@ -23,10 +23,4 @@
|
||||
#define OMAP4_CM_CLKSTCTRL 0x0000
|
||||
#define OMAP4_CM_STATICDEP 0x0004
|
||||
|
||||
/* Function prototypes */
|
||||
# ifndef __ASSEMBLER__
|
||||
|
||||
extern int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg);
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
@ -2,6 +2,7 @@
|
||||
* OMAP4 CM instance functions
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Paul Walmsley
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -32,6 +33,22 @@
|
||||
#include "prm44xx.h"
|
||||
#include "prcm_mpu44xx.h"
|
||||
|
||||
/*
|
||||
* CLKCTRL_IDLEST_*: possible values for the CM_*_CLKCTRL.IDLEST bitfield:
|
||||
*
|
||||
* 0x0 func: Module is fully functional, including OCP
|
||||
* 0x1 trans: Module is performing transition: wakeup, or sleep, or sleep
|
||||
* abortion
|
||||
* 0x2 idle: Module is in Idle mode (only OCP part). It is functional if
|
||||
* using separate functional clock
|
||||
* 0x3 disabled: Module is disabled and cannot be accessed
|
||||
*
|
||||
*/
|
||||
#define CLKCTRL_IDLEST_FUNCTIONAL 0x0
|
||||
#define CLKCTRL_IDLEST_INTRANSITION 0x1
|
||||
#define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2
|
||||
#define CLKCTRL_IDLEST_DISABLED 0x3
|
||||
|
||||
static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = {
|
||||
[OMAP4430_INVALID_PRCM_PARTITION] = 0,
|
||||
[OMAP4430_PRM_PARTITION] = OMAP4430_PRM_BASE,
|
||||
@ -41,6 +58,48 @@ static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = {
|
||||
[OMAP4430_PRCM_MPU_PARTITION] = OMAP4430_PRCM_MPU_BASE,
|
||||
};
|
||||
|
||||
/* Private functions */
|
||||
|
||||
/**
|
||||
* _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield
|
||||
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
|
||||
* @inst: CM instance register offset (*_INST macro)
|
||||
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
|
||||
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
|
||||
*
|
||||
* Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to
|
||||
* bit 0.
|
||||
*/
|
||||
static u32 _clkctrl_idlest(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
|
||||
{
|
||||
u32 v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs);
|
||||
v &= OMAP4430_IDLEST_MASK;
|
||||
v >>= OMAP4430_IDLEST_SHIFT;
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* _is_module_ready - can module registers be accessed without causing an abort?
|
||||
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
|
||||
* @inst: CM instance register offset (*_INST macro)
|
||||
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
|
||||
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
|
||||
*
|
||||
* Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either
|
||||
* *FUNCTIONAL or *INTERFACE_IDLE; false otherwise.
|
||||
*/
|
||||
static bool _is_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
v = _clkctrl_idlest(part, inst, cdoffs, clkctrl_offs);
|
||||
|
||||
return (v == CLKCTRL_IDLEST_FUNCTIONAL ||
|
||||
v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false;
|
||||
}
|
||||
|
||||
/* Public functions */
|
||||
|
||||
/* Read a register in a CM instance */
|
||||
u32 omap4_cminst_read_inst_reg(u8 part, s16 inst, u16 idx)
|
||||
{
|
||||
@ -200,36 +259,93 @@ void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs)
|
||||
*/
|
||||
|
||||
/**
|
||||
* omap4_cm_wait_module_ready - wait for a module to be in 'func' state
|
||||
* @clkctrl_reg: CLKCTRL module address
|
||||
* omap4_cminst_wait_module_ready - wait for a module to be in 'func' state
|
||||
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
|
||||
* @inst: CM instance register offset (*_INST macro)
|
||||
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
|
||||
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
|
||||
*
|
||||
* Wait for the module IDLEST to be functional. If the idle state is in any
|
||||
* the non functional state (trans, idle or disabled), module and thus the
|
||||
* sysconfig cannot be accessed and will probably lead to an "imprecise
|
||||
* external abort"
|
||||
*
|
||||
* 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;
|
||||
|
||||
if (!clkctrl_reg)
|
||||
if (!clkctrl_offs)
|
||||
return 0;
|
||||
|
||||
omap_test_timeout((
|
||||
((__raw_readl(clkctrl_reg) & OMAP4430_IDLEST_MASK) == 0) ||
|
||||
(((__raw_readl(clkctrl_reg) & OMAP4430_IDLEST_MASK) >>
|
||||
OMAP4430_IDLEST_SHIFT) == 0x2)),
|
||||
MAX_MODULE_READY_TIME, i);
|
||||
omap_test_timeout(_is_module_ready(part, inst, cdoffs, clkctrl_offs),
|
||||
MAX_MODULE_READY_TIME, i);
|
||||
|
||||
return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap4_cminst_wait_module_idle - wait for a module to be in 'disabled'
|
||||
* state
|
||||
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
|
||||
* @inst: CM instance register offset (*_INST macro)
|
||||
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
|
||||
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
@ -17,6 +17,14 @@ extern void omap4_cminst_clkdm_disable_hwsup(u8 part, s16 inst, u16 cdoffs);
|
||||
extern void omap4_cminst_clkdm_force_sleep(u8 part, s16 inst, u16 cdoffs);
|
||||
extern void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs);
|
||||
|
||||
extern int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
|
||||
extern int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
|
||||
|
||||
extern void omap4_cminst_module_enable(u8 mode, u8 part, u16 inst, s16 cdoffs,
|
||||
u16 clkctrl_offs);
|
||||
extern void omap4_cminst_module_disable(u8 part, u16 inst, s16 cdoffs,
|
||||
u16 clkctrl_offs);
|
||||
|
||||
/*
|
||||
* In an ideal world, we would not export these low-level functions,
|
||||
* but this will probably take some time to fix properly
|
||||
@ -32,6 +40,4 @@ extern u32 omap4_cminst_clear_inst_reg_bits(u32 bits, u8 part, s16 inst,
|
||||
extern u32 omap4_cminst_read_inst_reg_bits(u8 part, u16 inst, s16 idx,
|
||||
u32 mask);
|
||||
|
||||
extern int omap4_cm_wait_module_ready(void __iomem *clkctrl_reg);
|
||||
|
||||
#endif
|
||||
|
@ -146,9 +146,10 @@
|
||||
#include <plat/prcm.h>
|
||||
|
||||
#include "cm2xxx_3xxx.h"
|
||||
#include "cm44xx.h"
|
||||
#include "cminst44xx.h"
|
||||
#include "prm2xxx_3xxx.h"
|
||||
#include "prm44xx.h"
|
||||
#include "prminst44xx.h"
|
||||
#include "mux.h"
|
||||
|
||||
/* Maximum microseconds to wait for OMAP module to softreset */
|
||||
@ -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
|
||||
* @oh: struct omap_hwmod *oh
|
||||
@ -990,9 +1041,40 @@ static struct omap_hwmod *_lookup(const char *name)
|
||||
|
||||
return oh;
|
||||
}
|
||||
/**
|
||||
* _init_clkdm - look up a clockdomain name, store pointer in omap_hwmod
|
||||
* @oh: struct omap_hwmod *
|
||||
*
|
||||
* Convert a clockdomain name stored in a struct omap_hwmod into a
|
||||
* clockdomain pointer, and save it into the struct omap_hwmod.
|
||||
* return -EINVAL if clkdm_name does not exist or if the lookup failed.
|
||||
*/
|
||||
static int _init_clkdm(struct omap_hwmod *oh)
|
||||
{
|
||||
if (cpu_is_omap24xx() || cpu_is_omap34xx())
|
||||
return 0;
|
||||
|
||||
if (!oh->clkdm_name) {
|
||||
pr_warning("omap_hwmod: %s: no clkdm_name\n", oh->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
oh->clkdm = clkdm_lookup(oh->clkdm_name);
|
||||
if (!oh->clkdm) {
|
||||
pr_warning("omap_hwmod: %s: could not associate to clkdm %s\n",
|
||||
oh->name, oh->clkdm_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("omap_hwmod: %s: associated to clkdm %s\n",
|
||||
oh->name, oh->clkdm_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* _init_clocks - clk_get() all clocks associated with this hwmod
|
||||
* _init_clocks - clk_get() all clocks associated with this hwmod. Retrieve as
|
||||
* well the clockdomain.
|
||||
* @oh: struct omap_hwmod *
|
||||
* @data: not used; pass NULL
|
||||
*
|
||||
@ -1012,6 +1094,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data)
|
||||
ret |= _init_main_clk(oh);
|
||||
ret |= _init_interface_clks(oh);
|
||||
ret |= _init_opt_clks(oh);
|
||||
ret |= _init_clkdm(oh);
|
||||
|
||||
if (!ret)
|
||||
oh->_state = _HWMOD_STATE_CLKS_INITED;
|
||||
@ -1028,7 +1111,7 @@ static int _init_clocks(struct omap_hwmod *oh, void *data)
|
||||
* Wait for a module @oh to leave slave idle. Returns 0 if the module
|
||||
* does not have an IDLEST bit or if the module successfully leaves
|
||||
* slave idle; otherwise, pass along the return value of the
|
||||
* appropriate *_cm_wait_module_ready() function.
|
||||
* appropriate *_cm*_wait_module_ready() function.
|
||||
*/
|
||||
static int _wait_target_ready(struct omap_hwmod *oh)
|
||||
{
|
||||
@ -1055,7 +1138,13 @@ static int _wait_target_ready(struct omap_hwmod *oh)
|
||||
oh->prcm.omap2.idlest_reg_id,
|
||||
oh->prcm.omap2.idlest_idle_bit);
|
||||
} else if (cpu_is_omap44xx()) {
|
||||
ret = omap4_cm_wait_module_ready(oh->prcm.omap4.clkctrl_reg);
|
||||
if (!oh->clkdm)
|
||||
return -EINVAL;
|
||||
|
||||
ret = omap4_cminst_wait_module_ready(oh->clkdm->prcm_partition,
|
||||
oh->clkdm->cm_inst,
|
||||
oh->clkdm->clkdm_offs,
|
||||
oh->prcm.omap4.clkctrl_offs);
|
||||
} else {
|
||||
BUG();
|
||||
};
|
||||
@ -1063,6 +1152,36 @@ static int _wait_target_ready(struct omap_hwmod *oh)
|
||||
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
|
||||
* @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,
|
||||
ohri.rst_shift);
|
||||
else if (cpu_is_omap44xx())
|
||||
return omap4_prm_assert_hardreset(oh->prcm.omap4.rstctrl_reg,
|
||||
ohri.rst_shift);
|
||||
return omap4_prminst_assert_hardreset(ohri.rst_shift,
|
||||
oh->clkdm->pwrdm.ptr->prcm_partition,
|
||||
oh->clkdm->pwrdm.ptr->prcm_offs,
|
||||
oh->prcm.omap4.rstctrl_offs);
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1155,8 +1276,10 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
|
||||
if (ohri.st_shift)
|
||||
pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n",
|
||||
oh->name, name);
|
||||
ret = omap4_prm_deassert_hardreset(oh->prcm.omap4.rstctrl_reg,
|
||||
ohri.rst_shift);
|
||||
ret = omap4_prminst_deassert_hardreset(ohri.rst_shift,
|
||||
oh->clkdm->pwrdm.ptr->prcm_partition,
|
||||
oh->clkdm->pwrdm.ptr->prcm_offs,
|
||||
oh->prcm.omap4.rstctrl_offs);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1191,8 +1314,10 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name)
|
||||
return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs,
|
||||
ohri.st_shift);
|
||||
} else if (cpu_is_omap44xx()) {
|
||||
return omap4_prm_is_hardreset_asserted(oh->prcm.omap4.rstctrl_reg,
|
||||
ohri.rst_shift);
|
||||
return omap4_prminst_is_hardreset_asserted(ohri.rst_shift,
|
||||
oh->clkdm->pwrdm.ptr->prcm_partition,
|
||||
oh->clkdm->pwrdm.ptr->prcm_offs,
|
||||
oh->prcm.omap4.rstctrl_offs);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1312,6 +1437,7 @@ static int _reset(struct omap_hwmod *oh)
|
||||
static int _enable(struct omap_hwmod *oh)
|
||||
{
|
||||
int r;
|
||||
int hwsup = 0;
|
||||
|
||||
pr_debug("omap_hwmod: %s: enabling\n", oh->name);
|
||||
|
||||
@ -1323,14 +1449,6 @@ static int _enable(struct omap_hwmod *oh)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Mux pins for device runtime if populated */
|
||||
if (oh->mux && (!oh->mux->enabled ||
|
||||
((oh->_state == _HWMOD_STATE_IDLE) &&
|
||||
oh->mux->pads_dynamic)))
|
||||
omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
|
||||
|
||||
_add_initiator_dep(oh, mpu_oh);
|
||||
_enable_clocks(oh);
|
||||
|
||||
/*
|
||||
* If an IP contains only one HW reset line, then de-assert it in order
|
||||
@ -1341,22 +1459,56 @@ static int _enable(struct omap_hwmod *oh)
|
||||
oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1)
|
||||
_deassert_hardreset(oh, oh->rst_lines[0].name);
|
||||
|
||||
r = _wait_target_ready(oh);
|
||||
if (r) {
|
||||
pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
|
||||
oh->name, r);
|
||||
_disable_clocks(oh);
|
||||
/* Mux pins for device runtime if populated */
|
||||
if (oh->mux && (!oh->mux->enabled ||
|
||||
((oh->_state == _HWMOD_STATE_IDLE) &&
|
||||
oh->mux->pads_dynamic)))
|
||||
omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
|
||||
|
||||
return r;
|
||||
_add_initiator_dep(oh, mpu_oh);
|
||||
|
||||
if (oh->clkdm) {
|
||||
/*
|
||||
* A clockdomain must be in SW_SUP before enabling
|
||||
* completely the module. The clockdomain can be set
|
||||
* in HW_AUTO only when the module become ready.
|
||||
*/
|
||||
hwsup = clkdm_in_hwsup(oh->clkdm);
|
||||
r = clkdm_hwmod_enable(oh->clkdm, oh);
|
||||
if (r) {
|
||||
WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
|
||||
oh->name, oh->clkdm->name, r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
oh->_state = _HWMOD_STATE_ENABLED;
|
||||
_enable_clocks(oh);
|
||||
_enable_module(oh);
|
||||
|
||||
/* Access the sysconfig only if the target is ready */
|
||||
if (oh->class->sysc) {
|
||||
if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED))
|
||||
_update_sysc_cache(oh);
|
||||
_enable_sysc(oh);
|
||||
r = _wait_target_ready(oh);
|
||||
if (!r) {
|
||||
/*
|
||||
* Set the clockdomain to HW_AUTO only if the target is ready,
|
||||
* assuming that the previous state was HW_AUTO
|
||||
*/
|
||||
if (oh->clkdm && hwsup)
|
||||
clkdm_allow_idle(oh->clkdm);
|
||||
|
||||
oh->_state = _HWMOD_STATE_ENABLED;
|
||||
|
||||
/* Access the sysconfig only if the target is ready */
|
||||
if (oh->class->sysc) {
|
||||
if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED))
|
||||
_update_sysc_cache(oh);
|
||||
_enable_sysc(oh);
|
||||
}
|
||||
} else {
|
||||
_disable_clocks(oh);
|
||||
pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
|
||||
oh->name, r);
|
||||
|
||||
if (oh->clkdm)
|
||||
clkdm_hwmod_disable(oh->clkdm, oh);
|
||||
}
|
||||
|
||||
return r;
|
||||
@ -1372,6 +1524,8 @@ static int _enable(struct omap_hwmod *oh)
|
||||
*/
|
||||
static int _idle(struct omap_hwmod *oh)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_debug("omap_hwmod: %s: idling\n", oh->name);
|
||||
|
||||
if (oh->_state != _HWMOD_STATE_ENABLED) {
|
||||
@ -1383,7 +1537,20 @@ static int _idle(struct omap_hwmod *oh)
|
||||
if (oh->class->sysc)
|
||||
_idle_sysc(oh);
|
||||
_del_initiator_dep(oh, mpu_oh);
|
||||
_disable_module(oh);
|
||||
ret = _wait_target_disable(oh);
|
||||
if (ret)
|
||||
pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
|
||||
oh->name);
|
||||
/*
|
||||
* The module must be in idle mode before disabling any parents
|
||||
* clocks. Otherwise, the parent clock might be disabled before
|
||||
* the module transition is done, and thus will prevent the
|
||||
* transition to complete properly.
|
||||
*/
|
||||
_disable_clocks(oh);
|
||||
if (oh->clkdm)
|
||||
clkdm_hwmod_disable(oh->clkdm, oh);
|
||||
|
||||
/* Mux pins for device idle if populated */
|
||||
if (oh->mux && oh->mux->pads_dynamic)
|
||||
@ -1475,7 +1642,14 @@ static int _shutdown(struct omap_hwmod *oh)
|
||||
if (oh->_state == _HWMOD_STATE_ENABLED) {
|
||||
_del_initiator_dep(oh, mpu_oh);
|
||||
/* XXX what about the other system initiators here? dma, dsp */
|
||||
_disable_module(oh);
|
||||
ret = _wait_target_disable(oh);
|
||||
if (ret)
|
||||
pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
|
||||
oh->name);
|
||||
_disable_clocks(oh);
|
||||
if (oh->clkdm)
|
||||
clkdm_hwmod_disable(oh->clkdm, oh);
|
||||
}
|
||||
/* XXX Should this code also force-disable the optional clocks? */
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -108,6 +108,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
|
||||
u32 cur_state;
|
||||
int sleep_switch = -1;
|
||||
int ret = 0;
|
||||
int hwsup = 0;
|
||||
|
||||
if (pwrdm == NULL || IS_ERR(pwrdm))
|
||||
return -EINVAL;
|
||||
@ -127,6 +128,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
|
||||
(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) {
|
||||
sleep_switch = LOWPOWERSTATE_SWITCH;
|
||||
} else {
|
||||
hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
|
||||
clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);
|
||||
pwrdm_wait_transition(pwrdm);
|
||||
sleep_switch = FORCEWAKEUP_SWITCH;
|
||||
@ -142,7 +144,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
|
||||
|
||||
switch (sleep_switch) {
|
||||
case FORCEWAKEUP_SWITCH:
|
||||
if (pwrdm->pwrdm_clkdms[0]->flags & CLKDM_CAN_ENABLE_AUTO)
|
||||
if (hwsup)
|
||||
clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);
|
||||
else
|
||||
clkdm_sleep(pwrdm->pwrdm_clkdms[0]);
|
||||
|
@ -70,7 +70,7 @@ static void omap_prcm_arch_reset(char mode, const char *cmd)
|
||||
prcm_offs = OMAP3430_GR_MOD;
|
||||
omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0));
|
||||
} else if (cpu_is_omap44xx()) {
|
||||
omap4_prm_global_warm_sw_reset(); /* never returns */
|
||||
omap4_prminst_global_warm_sw_reset(); /* never returns */
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* OMAP4 PRM module functions
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments, Inc.
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2010 Nokia Corporation
|
||||
* Benoît Cousson
|
||||
* Paul Walmsley
|
||||
@ -24,12 +24,6 @@
|
||||
#include "prm44xx.h"
|
||||
#include "prm-regbits-44xx.h"
|
||||
|
||||
/*
|
||||
* Address offset (in bytes) between the reset control and the reset
|
||||
* status registers: 4 bytes on OMAP4
|
||||
*/
|
||||
#define OMAP4_RST_CTRL_ST_OFFSET 4
|
||||
|
||||
/* PRM low-level functions */
|
||||
|
||||
/* Read a register in a CM/PRM instance in the PRM module */
|
||||
@ -56,140 +50,3 @@ u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 reg)
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Read a PRM register, AND it, and shift the result down to bit 0 */
|
||||
/* XXX deprecated */
|
||||
u32 omap4_prm_read_bits_shift(void __iomem *reg, u32 mask)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
v = __raw_readl(reg);
|
||||
v &= mask;
|
||||
v >>= __ffs(mask);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Read-modify-write a register in a PRM module. Caller must lock */
|
||||
/* XXX deprecated */
|
||||
u32 omap4_prm_rmw_reg_bits(u32 mask, u32 bits, void __iomem *reg)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
v = __raw_readl(reg);
|
||||
v &= ~mask;
|
||||
v |= bits;
|
||||
__raw_writel(v, reg);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
u32 omap4_prm_set_inst_reg_bits(u32 bits, s16 inst, s16 reg)
|
||||
{
|
||||
return omap4_prm_rmw_inst_reg_bits(bits, bits, inst, reg);
|
||||
}
|
||||
|
||||
u32 omap4_prm_clear_inst_reg_bits(u32 bits, s16 inst, s16 reg)
|
||||
{
|
||||
return omap4_prm_rmw_inst_reg_bits(bits, 0x0, inst, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap4_prm_is_hardreset_asserted - read the HW reset line state of
|
||||
* submodules contained in the hwmod module
|
||||
* @rstctrl_reg: RM_RSTCTRL register address for this module
|
||||
* @shift: register bit shift corresponding to the reset line to check
|
||||
*
|
||||
* Returns 1 if the (sub)module hardreset line is currently asserted,
|
||||
* 0 if the (sub)module hardreset line is not currently asserted, or
|
||||
* -EINVAL upon parameter error.
|
||||
*/
|
||||
int omap4_prm_is_hardreset_asserted(void __iomem *rstctrl_reg, u8 shift)
|
||||
{
|
||||
if (!cpu_is_omap44xx() || !rstctrl_reg)
|
||||
return -EINVAL;
|
||||
|
||||
return omap4_prm_read_bits_shift(rstctrl_reg, (1 << shift));
|
||||
}
|
||||
|
||||
/**
|
||||
* omap4_prm_assert_hardreset - assert the HW reset line of a submodule
|
||||
* @rstctrl_reg: RM_RSTCTRL register address for this module
|
||||
* @shift: register bit shift corresponding to the reset line to assert
|
||||
*
|
||||
* Some IPs like dsp, ipu or iva contain processors that require an HW
|
||||
* reset line to be asserted / deasserted in order to fully enable the
|
||||
* IP. These modules may have multiple hard-reset lines that reset
|
||||
* different 'submodules' inside the IP block. This function will
|
||||
* place the submodule into reset. Returns 0 upon success or -EINVAL
|
||||
* upon an argument error.
|
||||
*/
|
||||
int omap4_prm_assert_hardreset(void __iomem *rstctrl_reg, u8 shift)
|
||||
{
|
||||
u32 mask;
|
||||
|
||||
if (!cpu_is_omap44xx() || !rstctrl_reg)
|
||||
return -EINVAL;
|
||||
|
||||
mask = 1 << shift;
|
||||
omap4_prm_rmw_reg_bits(mask, mask, rstctrl_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap4_prm_deassert_hardreset - deassert a submodule hardreset line and wait
|
||||
* @rstctrl_reg: RM_RSTCTRL register address for this module
|
||||
* @shift: register bit shift corresponding to the reset line to deassert
|
||||
*
|
||||
* Some IPs like dsp, ipu or iva contain processors that require an HW
|
||||
* reset line to be asserted / deasserted in order to fully enable the
|
||||
* IP. These modules may have multiple hard-reset lines that reset
|
||||
* different 'submodules' inside the IP block. This function will
|
||||
* take the submodule out of reset and wait until the PRCM indicates
|
||||
* that the reset has completed before returning. Returns 0 upon success or
|
||||
* -EINVAL upon an argument error, -EEXIST if the submodule was already out
|
||||
* of reset, or -EBUSY if the submodule did not exit reset promptly.
|
||||
*/
|
||||
int omap4_prm_deassert_hardreset(void __iomem *rstctrl_reg, u8 shift)
|
||||
{
|
||||
u32 mask;
|
||||
void __iomem *rstst_reg;
|
||||
int c;
|
||||
|
||||
if (!cpu_is_omap44xx() || !rstctrl_reg)
|
||||
return -EINVAL;
|
||||
|
||||
rstst_reg = rstctrl_reg + OMAP4_RST_CTRL_ST_OFFSET;
|
||||
|
||||
mask = 1 << shift;
|
||||
|
||||
/* Check the current status to avoid de-asserting the line twice */
|
||||
if (omap4_prm_read_bits_shift(rstctrl_reg, mask) == 0)
|
||||
return -EEXIST;
|
||||
|
||||
/* Clear the reset status by writing 1 to the status bit */
|
||||
omap4_prm_rmw_reg_bits(0xffffffff, mask, rstst_reg);
|
||||
/* de-assert the reset control line */
|
||||
omap4_prm_rmw_reg_bits(mask, 0, rstctrl_reg);
|
||||
/* wait the status to be set */
|
||||
omap_test_timeout(omap4_prm_read_bits_shift(rstst_reg, mask),
|
||||
MAX_MODULE_HARDRESET_WAIT, c);
|
||||
|
||||
return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
|
||||
}
|
||||
|
||||
void omap4_prm_global_warm_sw_reset(void)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
v = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_RM_RSTCTRL);
|
||||
v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
|
||||
omap4_prm_write_inst_reg(v, OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_RM_RSTCTRL);
|
||||
|
||||
/* OCP barrier */
|
||||
v = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_RM_RSTCTRL);
|
||||
}
|
||||
|
@ -750,16 +750,6 @@
|
||||
extern u32 omap4_prm_read_inst_reg(s16 inst, u16 idx);
|
||||
extern void omap4_prm_write_inst_reg(u32 val, s16 inst, u16 idx);
|
||||
extern u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx);
|
||||
extern u32 omap4_prm_rmw_reg_bits(u32 mask, u32 bits, void __iomem *reg);
|
||||
extern u32 omap4_prm_set_inst_reg_bits(u32 bits, s16 inst, s16 idx);
|
||||
extern u32 omap4_prm_clear_inst_reg_bits(u32 bits, s16 inst, s16 idx);
|
||||
extern u32 omap4_prm_read_bits_shift(void __iomem *reg, u32 mask);
|
||||
|
||||
extern int omap4_prm_is_hardreset_asserted(void __iomem *rstctrl_reg, u8 shift);
|
||||
extern int omap4_prm_assert_hardreset(void __iomem *rstctrl_reg, u8 shift);
|
||||
extern int omap4_prm_deassert_hardreset(void __iomem *rstctrl_reg, u8 shift);
|
||||
|
||||
extern void omap4_prm_global_warm_sw_reset(void);
|
||||
|
||||
# endif
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
* OMAP4 PRM instance functions
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Paul Walmsley
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -53,7 +54,7 @@ void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx)
|
||||
|
||||
/* Read-modify-write a register in PRM. Caller must lock */
|
||||
u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst,
|
||||
s16 idx)
|
||||
u16 idx)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
@ -64,3 +65,112 @@ u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst,
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* Address offset (in bytes) between the reset control and the reset
|
||||
* status registers: 4 bytes on OMAP4
|
||||
*/
|
||||
#define OMAP4_RST_CTRL_ST_OFFSET 4
|
||||
|
||||
/**
|
||||
* omap4_prminst_is_hardreset_asserted - read the HW reset line state of
|
||||
* submodules contained in the hwmod module
|
||||
* @rstctrl_reg: RM_RSTCTRL register address for this module
|
||||
* @shift: register bit shift corresponding to the reset line to check
|
||||
*
|
||||
* Returns 1 if the (sub)module hardreset line is currently asserted,
|
||||
* 0 if the (sub)module hardreset line is not currently asserted, or
|
||||
* -EINVAL upon parameter error.
|
||||
*/
|
||||
int omap4_prminst_is_hardreset_asserted(u8 shift, u8 part, s16 inst,
|
||||
u16 rstctrl_offs)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
v = omap4_prminst_read_inst_reg(part, inst, rstctrl_offs);
|
||||
v &= 1 << shift;
|
||||
v >>= shift;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap4_prminst_assert_hardreset - assert the HW reset line of a submodule
|
||||
* @rstctrl_reg: RM_RSTCTRL register address for this module
|
||||
* @shift: register bit shift corresponding to the reset line to assert
|
||||
*
|
||||
* Some IPs like dsp, ipu or iva contain processors that require an HW
|
||||
* reset line to be asserted / deasserted in order to fully enable the
|
||||
* IP. These modules may have multiple hard-reset lines that reset
|
||||
* different 'submodules' inside the IP block. This function will
|
||||
* place the submodule into reset. Returns 0 upon success or -EINVAL
|
||||
* upon an argument error.
|
||||
*/
|
||||
int omap4_prminst_assert_hardreset(u8 shift, u8 part, s16 inst,
|
||||
u16 rstctrl_offs)
|
||||
{
|
||||
u32 mask = 1 << shift;
|
||||
|
||||
omap4_prminst_rmw_inst_reg_bits(mask, mask, part, inst, rstctrl_offs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap4_prminst_deassert_hardreset - deassert a submodule hardreset line and
|
||||
* wait
|
||||
* @rstctrl_reg: RM_RSTCTRL register address for this module
|
||||
* @shift: register bit shift corresponding to the reset line to deassert
|
||||
*
|
||||
* Some IPs like dsp, ipu or iva contain processors that require an HW
|
||||
* reset line to be asserted / deasserted in order to fully enable the
|
||||
* IP. These modules may have multiple hard-reset lines that reset
|
||||
* different 'submodules' inside the IP block. This function will
|
||||
* take the submodule out of reset and wait until the PRCM indicates
|
||||
* that the reset has completed before returning. Returns 0 upon success or
|
||||
* -EINVAL upon an argument error, -EEXIST if the submodule was already out
|
||||
* of reset, or -EBUSY if the submodule did not exit reset promptly.
|
||||
*/
|
||||
int omap4_prminst_deassert_hardreset(u8 shift, u8 part, s16 inst,
|
||||
u16 rstctrl_offs)
|
||||
{
|
||||
int c;
|
||||
u32 mask = 1 << shift;
|
||||
u16 rstst_offs = rstctrl_offs + OMAP4_RST_CTRL_ST_OFFSET;
|
||||
|
||||
/* Check the current status to avoid de-asserting the line twice */
|
||||
if (omap4_prminst_is_hardreset_asserted(shift, part, inst,
|
||||
rstctrl_offs) == 0)
|
||||
return -EEXIST;
|
||||
|
||||
/* Clear the reset status by writing 1 to the status bit */
|
||||
omap4_prminst_rmw_inst_reg_bits(0xffffffff, mask, part, inst,
|
||||
rstst_offs);
|
||||
/* de-assert the reset control line */
|
||||
omap4_prminst_rmw_inst_reg_bits(mask, 0, part, inst, rstctrl_offs);
|
||||
/* wait the status to be set */
|
||||
omap_test_timeout(omap4_prminst_is_hardreset_asserted(shift, part, inst,
|
||||
rstst_offs),
|
||||
MAX_MODULE_HARDRESET_WAIT, c);
|
||||
|
||||
return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
|
||||
}
|
||||
|
||||
|
||||
void omap4_prminst_global_warm_sw_reset(void)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
|
||||
OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_PRM_RSTCTRL_OFFSET);
|
||||
v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK;
|
||||
omap4_prminst_write_inst_reg(v, OMAP4430_PRM_PARTITION,
|
||||
OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_PRM_RSTCTRL_OFFSET);
|
||||
|
||||
/* OCP barrier */
|
||||
v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
|
||||
OMAP4430_PRM_DEVICE_INST,
|
||||
OMAP4_PRM_RSTCTRL_OFFSET);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
* OMAP4 Power/Reset Management (PRM) function prototypes
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Paul Walmsley
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -18,8 +19,15 @@
|
||||
extern u32 omap4_prminst_read_inst_reg(u8 part, s16 inst, u16 idx);
|
||||
extern void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx);
|
||||
extern u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part,
|
||||
s16 inst, s16 idx);
|
||||
s16 inst, u16 idx);
|
||||
|
||||
extern void omap4_prm_global_warm_sw_reset(void);
|
||||
extern void omap4_prminst_global_warm_sw_reset(void);
|
||||
|
||||
extern int omap4_prminst_is_hardreset_asserted(u8 shift, u8 part, s16 inst,
|
||||
u16 rstctrl_offs);
|
||||
extern int omap4_prminst_assert_hardreset(u8 shift, u8 part, s16 inst,
|
||||
u16 rstctrl_offs);
|
||||
extern int omap4_prminst_deassert_hardreset(u8 shift, u8 part, s16 inst,
|
||||
u16 rstctrl_offs);
|
||||
|
||||
#endif
|
||||
|
@ -80,11 +80,11 @@ static struct twl4030_madc_platform_data omap3_madc_pdata = {
|
||||
.irq_line = 1,
|
||||
};
|
||||
|
||||
static struct twl4030_codec_audio_data omap3_audio;
|
||||
static struct twl4030_codec_data omap3_codec;
|
||||
|
||||
static struct twl4030_codec_data omap3_codec_pdata = {
|
||||
static struct twl4030_audio_data omap3_audio_pdata = {
|
||||
.audio_mclk = 26000000,
|
||||
.audio = &omap3_audio,
|
||||
.codec = &omap3_codec,
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply omap3_vdda_dac_supplies[] = {
|
||||
@ -292,8 +292,8 @@ void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data,
|
||||
if (pdata_flags & TWL_COMMON_PDATA_MADC && !pmic_data->madc)
|
||||
pmic_data->madc = &omap3_madc_pdata;
|
||||
|
||||
if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->codec)
|
||||
pmic_data->codec = &omap3_codec_pdata;
|
||||
if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->audio)
|
||||
pmic_data->audio = &omap3_audio_pdata;
|
||||
|
||||
/* Common regulator configurations */
|
||||
if (regulators_flags & TWL_COMMON_REGULATOR_VDAC && !pmic_data->vdac)
|
||||
|
@ -475,8 +475,41 @@ int __init clk_init(struct clk_functions * custom_clocks)
|
||||
/*
|
||||
* debugfs support to trace clock tree hierarchy and attributes
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
static struct dentry *clk_debugfs_root;
|
||||
|
||||
static int clk_dbg_show_summary(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct clk *c;
|
||||
struct clk *pa;
|
||||
|
||||
seq_printf(s, "%-30s %-30s %-10s %s\n",
|
||||
"clock-name", "parent-name", "rate", "use-count");
|
||||
|
||||
list_for_each_entry(c, &clocks, node) {
|
||||
pa = c->parent;
|
||||
seq_printf(s, "%-30s %-30s %-10lu %d\n",
|
||||
c->name, pa ? pa->name : "none", c->rate, c->usecount);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_dbg_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, clk_dbg_show_summary, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations debug_clock_fops = {
|
||||
.open = clk_dbg_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int clk_debugfs_register_one(struct clk *c)
|
||||
{
|
||||
int err;
|
||||
@ -545,6 +578,12 @@ static int __init clk_debugfs_init(void)
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
d = debugfs_create_file("summary", S_IRUGO,
|
||||
d, NULL, &debug_clock_fops);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
err_out:
|
||||
debugfs_remove_recursive(clk_debugfs_root);
|
||||
|
@ -407,11 +407,19 @@
|
||||
#endif
|
||||
#define TWL6030_IRQ_END (TWL6030_IRQ_BASE + TWL6030_BASE_NR_IRQS)
|
||||
|
||||
#define TWL6040_CODEC_IRQ_BASE TWL6030_IRQ_END
|
||||
#ifdef CONFIG_TWL6040_CODEC
|
||||
#define TWL6040_CODEC_NR_IRQS 6
|
||||
#else
|
||||
#define TWL6040_CODEC_NR_IRQS 0
|
||||
#endif
|
||||
#define TWL6040_CODEC_IRQ_END (TWL6040_CODEC_IRQ_BASE + TWL6040_CODEC_NR_IRQS)
|
||||
|
||||
/* Total number of interrupts depends on the enabled blocks above */
|
||||
#if (TWL4030_GPIO_IRQ_END > TWL6030_IRQ_END)
|
||||
#if (TWL4030_GPIO_IRQ_END > TWL6040_CODEC_IRQ_END)
|
||||
#define TWL_IRQ_END TWL4030_GPIO_IRQ_END
|
||||
#else
|
||||
#define TWL_IRQ_END TWL6030_IRQ_END
|
||||
#define TWL_IRQ_END TWL6040_CODEC_IRQ_END
|
||||
#endif
|
||||
|
||||
/* GPMC related */
|
||||
|
@ -2,6 +2,7 @@
|
||||
* omap_hwmod macros, structures
|
||||
*
|
||||
* Copyright (C) 2009-2011 Nokia Corporation
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Paul Walmsley
|
||||
*
|
||||
* Created in collaboration with (alphabetical order): Benoît Cousson,
|
||||
@ -79,6 +80,11 @@ extern struct omap_hwmod_sysc_fields omap_hwmod_sysc_type2;
|
||||
#define HWMOD_IDLEMODE_SMART (1 << 2)
|
||||
#define HWMOD_IDLEMODE_SMART_WKUP (1 << 3)
|
||||
|
||||
/* modulemode control type (SW or HW) */
|
||||
#define MODULEMODE_HWCTRL 1
|
||||
#define MODULEMODE_SWCTRL 2
|
||||
|
||||
|
||||
/**
|
||||
* struct omap_hwmod_mux_info - hwmod specific mux configuration
|
||||
* @pads: array of omap_device_pad entries
|
||||
@ -360,9 +366,11 @@ struct omap_hwmod_omap2_prcm {
|
||||
* @submodule_wkdep_bit: bit shift of the WKDEP range
|
||||
*/
|
||||
struct omap_hwmod_omap4_prcm {
|
||||
void __iomem *clkctrl_reg;
|
||||
void __iomem *rstctrl_reg;
|
||||
u16 clkctrl_offs;
|
||||
u16 rstctrl_offs;
|
||||
u16 context_offs;
|
||||
u8 submodule_wkdep_bit;
|
||||
u8 modulemode;
|
||||
};
|
||||
|
||||
|
||||
@ -515,6 +523,8 @@ struct omap_hwmod {
|
||||
const char *main_clk;
|
||||
struct clk *_clk;
|
||||
struct omap_hwmod_opt_clk *opt_clks;
|
||||
char *clkdm_name;
|
||||
struct clockdomain *clkdm;
|
||||
char *vdd_name;
|
||||
struct voltagedomain *voltdm;
|
||||
struct omap_hwmod_ocp_if **masters; /* connect to *_IA */
|
||||
|
@ -236,56 +236,71 @@ static int _omap_device_deactivate(struct omap_device *od, u8 ignore_lat)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _add_clkdev(struct omap_device *od, const char *clk_alias,
|
||||
const char *clk_name)
|
||||
{
|
||||
struct clk *r;
|
||||
struct clk_lookup *l;
|
||||
|
||||
if (!clk_alias || !clk_name)
|
||||
return;
|
||||
|
||||
pr_debug("omap_device: %s: Creating %s -> %s\n",
|
||||
dev_name(&od->pdev.dev), clk_alias, clk_name);
|
||||
|
||||
r = clk_get_sys(dev_name(&od->pdev.dev), clk_alias);
|
||||
if (!IS_ERR(r)) {
|
||||
pr_warning("omap_device: %s: alias %s already exists\n",
|
||||
dev_name(&od->pdev.dev), clk_alias);
|
||||
clk_put(r);
|
||||
return;
|
||||
}
|
||||
|
||||
r = omap_clk_get_by_name(clk_name);
|
||||
if (IS_ERR(r)) {
|
||||
pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
|
||||
dev_name(&od->pdev.dev), clk_name);
|
||||
return;
|
||||
}
|
||||
|
||||
l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev.dev));
|
||||
if (!l) {
|
||||
pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
|
||||
dev_name(&od->pdev.dev), clk_alias);
|
||||
return;
|
||||
}
|
||||
|
||||
clkdev_add(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* _add_optional_clock_clkdev - Add clkdev entry for hwmod optional clocks
|
||||
* _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks
|
||||
* and main clock
|
||||
* @od: struct omap_device *od
|
||||
* @oh: struct omap_hwmod *oh
|
||||
*
|
||||
* For every optional clock present per hwmod per omap_device, this function
|
||||
* adds an entry in the clkdev table of the form <dev-id=dev_name, con-id=role>
|
||||
* if it does not exist already.
|
||||
* For the main clock and every optional clock present per hwmod per
|
||||
* omap_device, this function adds an entry in the clkdev table of the
|
||||
* form <dev-id=dev_name, con-id=role> if it does not exist already.
|
||||
*
|
||||
* The function is called from inside omap_device_build_ss(), after
|
||||
* omap_device_register.
|
||||
*
|
||||
* This allows drivers to get a pointer to its optional clocks based on its role
|
||||
* by calling clk_get(<dev*>, <role>).
|
||||
* In the case of the main clock, a "fck" alias is used.
|
||||
*
|
||||
* No return value.
|
||||
*/
|
||||
static void _add_optional_clock_clkdev(struct omap_device *od,
|
||||
struct omap_hwmod *oh)
|
||||
static void _add_hwmod_clocks_clkdev(struct omap_device *od,
|
||||
struct omap_hwmod *oh)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < oh->opt_clks_cnt; i++) {
|
||||
struct omap_hwmod_opt_clk *oc;
|
||||
struct clk *r;
|
||||
struct clk_lookup *l;
|
||||
_add_clkdev(od, "fck", oh->main_clk);
|
||||
|
||||
oc = &oh->opt_clks[i];
|
||||
|
||||
if (!oc->_clk)
|
||||
continue;
|
||||
|
||||
r = clk_get_sys(dev_name(&od->pdev.dev), oc->role);
|
||||
if (!IS_ERR(r))
|
||||
continue; /* clkdev entry exists */
|
||||
|
||||
r = omap_clk_get_by_name((char *)oc->clk);
|
||||
if (IS_ERR(r)) {
|
||||
pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
|
||||
dev_name(&od->pdev.dev), oc->clk);
|
||||
continue;
|
||||
}
|
||||
|
||||
l = clkdev_alloc(r, oc->role, dev_name(&od->pdev.dev));
|
||||
if (!l) {
|
||||
pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
|
||||
dev_name(&od->pdev.dev), oc->role);
|
||||
return;
|
||||
}
|
||||
clkdev_add(l);
|
||||
}
|
||||
for (i = 0; i < oh->opt_clks_cnt; i++)
|
||||
_add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk);
|
||||
}
|
||||
|
||||
|
||||
@ -492,7 +507,7 @@ struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id,
|
||||
|
||||
for (i = 0; i < oh_cnt; i++) {
|
||||
hwmods[i]->od = od;
|
||||
_add_optional_clock_clkdev(od, hwmods[i]);
|
||||
_add_hwmod_clocks_clkdev(od, hwmods[i]);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
|
@ -267,7 +267,7 @@ config INPUT_TWL4030_PWRBUTTON
|
||||
config INPUT_TWL4030_VIBRA
|
||||
tristate "Support for TWL4030 Vibrator"
|
||||
depends on TWL4030_CORE
|
||||
select TWL4030_CODEC
|
||||
select MFD_TWL4030_AUDIO
|
||||
select INPUT_FF_MEMLESS
|
||||
help
|
||||
This option enables support for TWL4030 Vibrator Driver.
|
||||
@ -275,6 +275,17 @@ config INPUT_TWL4030_VIBRA
|
||||
To compile this driver as a module, choose M here. The module will
|
||||
be called twl4030_vibra.
|
||||
|
||||
config INPUT_TWL6040_VIBRA
|
||||
tristate "Support for TWL6040 Vibrator"
|
||||
depends on TWL4030_CORE
|
||||
select TWL6040_CORE
|
||||
select INPUT_FF_MEMLESS
|
||||
help
|
||||
This option enables support for TWL6040 Vibrator Driver.
|
||||
|
||||
To compile this driver as a module, choose M here. The module will
|
||||
be called twl6040_vibra.
|
||||
|
||||
config INPUT_UINPUT
|
||||
tristate "User level driver support"
|
||||
help
|
||||
|
@ -40,6 +40,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
|
||||
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
|
||||
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
|
||||
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
|
||||
obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o
|
||||
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
|
||||
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
|
||||
obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
#include <linux/mfd/twl4030-codec.h>
|
||||
#include <linux/mfd/twl4030-audio.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -67,7 +67,7 @@ static void vibra_enable(struct vibra_info *info)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
|
||||
twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
|
||||
|
||||
/* turn H-Bridge on */
|
||||
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
|
||||
@ -75,7 +75,7 @@ static void vibra_enable(struct vibra_info *info)
|
||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
|
||||
(reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
|
||||
|
||||
twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
|
||||
twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL);
|
||||
|
||||
info->enabled = true;
|
||||
}
|
||||
@ -90,8 +90,8 @@ static void vibra_disable(struct vibra_info *info)
|
||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
|
||||
(reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
|
||||
|
||||
twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
|
||||
twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
|
||||
twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL);
|
||||
twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
|
||||
|
||||
info->enabled = false;
|
||||
}
|
||||
@ -196,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
|
||||
|
||||
static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data;
|
||||
struct twl4030_vibra_data *pdata = pdev->dev.platform_data;
|
||||
struct vibra_info *info;
|
||||
int ret;
|
||||
|
||||
|
423
drivers/input/misc/twl6040-vibra.c
Normal file
423
drivers/input/misc/twl6040-vibra.c
Normal 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>");
|
@ -218,7 +218,7 @@ config TWL4030_POWER
|
||||
and load scripts controlling which resources are switched off/on
|
||||
or reset when a sleep, wakeup or warm reset event occurs.
|
||||
|
||||
config TWL4030_CODEC
|
||||
config MFD_TWL4030_AUDIO
|
||||
bool
|
||||
depends on TWL4030_CORE
|
||||
select MFD_CORE
|
||||
@ -233,6 +233,12 @@ config TWL6030_PWM
|
||||
Say yes here if you want support for TWL6030 PWM.
|
||||
This is used to control charging LED brightness.
|
||||
|
||||
config TWL6040_CORE
|
||||
bool
|
||||
depends on TWL4030_CORE && GENERIC_HARDIRQS
|
||||
select MFD_CORE
|
||||
default n
|
||||
|
||||
config MFD_STMPE
|
||||
bool "Support STMicroelectronics STMPE"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
|
@ -40,8 +40,9 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
|
||||
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
|
||||
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
|
||||
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
|
||||
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
|
||||
obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
|
||||
obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
|
||||
obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
|
||||
|
||||
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
|
||||
|
||||
|
@ -110,7 +110,7 @@
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
|
||||
defined(CONFIG_SND_SOC_TWL6040) || defined(CONFIG_SND_SOC_TWL6040_MODULE)
|
||||
defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE)
|
||||
#define twl_has_codec() true
|
||||
#else
|
||||
#define twl_has_codec() false
|
||||
@ -815,20 +815,19 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
if (twl_has_codec() && pdata->codec && twl_class_is_4030()) {
|
||||
if (twl_has_codec() && pdata->audio && twl_class_is_4030()) {
|
||||
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
|
||||
child = add_child(sub_chip_id, "twl4030-audio",
|
||||
pdata->codec, sizeof(*pdata->codec),
|
||||
pdata->audio, sizeof(*pdata->audio),
|
||||
false, 0, 0);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
/* Phoenix codec driver is probed directly atm */
|
||||
if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
|
||||
if (twl_has_codec() && pdata->audio && twl_class_is_6030()) {
|
||||
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
|
||||
child = add_child(sub_chip_id, "twl6040-codec",
|
||||
pdata->codec, sizeof(*pdata->codec),
|
||||
child = add_child(sub_chip_id, "twl6040",
|
||||
pdata->audio, sizeof(*pdata->audio),
|
||||
false, 0, 0);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
277
drivers/mfd/twl4030-audio.c
Normal file
277
drivers/mfd/twl4030-audio.c
Normal 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");
|
@ -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
620
drivers/mfd/twl6040-core.c
Normal 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
191
drivers/mfd/twl6040-irq.c
Normal 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);
|
@ -657,28 +657,41 @@ struct twl4030_power_data {
|
||||
extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
|
||||
extern int twl4030_remove_script(u8 flags);
|
||||
|
||||
struct twl4030_codec_audio_data {
|
||||
struct twl4030_codec_data {
|
||||
unsigned int digimic_delay; /* in ms */
|
||||
unsigned int ramp_delay_value;
|
||||
unsigned int offset_cncl_path;
|
||||
unsigned int check_defaults:1;
|
||||
unsigned int reset_registers:1;
|
||||
unsigned int hs_extmute:1;
|
||||
u16 hs_left_step;
|
||||
u16 hs_right_step;
|
||||
u16 hf_left_step;
|
||||
u16 hf_right_step;
|
||||
void (*set_hs_extmute)(int mute);
|
||||
};
|
||||
|
||||
struct twl4030_codec_vibra_data {
|
||||
struct twl4030_vibra_data {
|
||||
unsigned int coexist;
|
||||
|
||||
/* twl6040 */
|
||||
unsigned int vibldrv_res; /* left driver resistance */
|
||||
unsigned int vibrdrv_res; /* right driver resistance */
|
||||
unsigned int viblmotor_res; /* left motor resistance */
|
||||
unsigned int vibrmotor_res; /* right motor resistance */
|
||||
int vddvibl_uV; /* VDDVIBL volt, set 0 for fixed reg */
|
||||
int vddvibr_uV; /* VDDVIBR volt, set 0 for fixed reg */
|
||||
};
|
||||
|
||||
struct twl4030_codec_data {
|
||||
struct twl4030_audio_data {
|
||||
unsigned int audio_mclk;
|
||||
struct twl4030_codec_audio_data *audio;
|
||||
struct twl4030_codec_vibra_data *vibra;
|
||||
struct twl4030_codec_data *codec;
|
||||
struct twl4030_vibra_data *vibra;
|
||||
|
||||
/* twl6040 */
|
||||
int audpwron_gpio; /* audio power-on gpio */
|
||||
int naudint_irq; /* audio interrupt */
|
||||
unsigned int irq_base;
|
||||
};
|
||||
|
||||
struct twl4030_platform_data {
|
||||
@ -690,7 +703,7 @@ struct twl4030_platform_data {
|
||||
struct twl4030_keypad_data *keypad;
|
||||
struct twl4030_usb_data *usb;
|
||||
struct twl4030_power_data *power;
|
||||
struct twl4030_codec_data *codec;
|
||||
struct twl4030_audio_data *audio;
|
||||
|
||||
/* Common LDO regulators for TWL4030/TWL6030 */
|
||||
struct regulator_init_data *vdac;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* MFD driver for twl4030 codec submodule
|
||||
* MFD driver for twl4030 audio submodule
|
||||
*
|
||||
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
|
||||
*
|
||||
@ -259,14 +259,14 @@
|
||||
#define TWL4030_VIBRA_DIR_SEL 0x20
|
||||
|
||||
/* TWL4030 codec resource IDs */
|
||||
enum twl4030_codec_res {
|
||||
TWL4030_CODEC_RES_POWER = 0,
|
||||
TWL4030_CODEC_RES_APLL,
|
||||
TWL4030_CODEC_RES_MAX,
|
||||
enum twl4030_audio_res {
|
||||
TWL4030_AUDIO_RES_POWER = 0,
|
||||
TWL4030_AUDIO_RES_APLL,
|
||||
TWL4030_AUDIO_RES_MAX,
|
||||
};
|
||||
|
||||
int twl4030_codec_disable_resource(enum twl4030_codec_res id);
|
||||
int twl4030_codec_enable_resource(enum twl4030_codec_res id);
|
||||
unsigned int twl4030_codec_get_mclk(void);
|
||||
int twl4030_audio_disable_resource(enum twl4030_audio_res id);
|
||||
int twl4030_audio_enable_resource(enum twl4030_audio_res id);
|
||||
unsigned int twl4030_audio_get_mclk(void);
|
||||
|
||||
#endif /* End of __TWL4030_CODEC_H__ */
|
228
include/linux/mfd/twl6040.h
Normal file
228
include/linux/mfd/twl6040.h
Normal 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__ */
|
@ -250,10 +250,11 @@ config SND_SOC_TLV320DAC33
|
||||
tristate
|
||||
|
||||
config SND_SOC_TWL4030
|
||||
select TWL4030_CODEC
|
||||
select MFD_TWL4030_AUDIO
|
||||
tristate
|
||||
|
||||
config SND_SOC_TWL6040
|
||||
select TWL6040_CORE
|
||||
tristate
|
||||
|
||||
config SND_SOC_UDA134X
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include <sound/tlv.h>
|
||||
|
||||
/* Register descriptions are here */
|
||||
#include <linux/mfd/twl4030-codec.h>
|
||||
#include <linux/mfd/twl4030-audio.h>
|
||||
|
||||
/* Shadow register used by the audio driver */
|
||||
#define TWL4030_REG_SW_SHADOW 0x4A
|
||||
@ -251,9 +251,9 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
|
||||
return;
|
||||
|
||||
if (enable)
|
||||
mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
|
||||
mode = twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
|
||||
else
|
||||
mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
|
||||
mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
|
||||
|
||||
if (mode >= 0) {
|
||||
twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode);
|
||||
@ -297,7 +297,7 @@ static inline void twl4030_reset_registers(struct snd_soc_codec *codec)
|
||||
|
||||
static void twl4030_init_chip(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct twl4030_codec_audio_data *pdata = dev_get_platdata(codec->dev);
|
||||
struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev);
|
||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||
u8 reg, byte;
|
||||
int i = 0;
|
||||
@ -375,13 +375,13 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
|
||||
if (enable) {
|
||||
twl4030->apll_enabled++;
|
||||
if (twl4030->apll_enabled == 1)
|
||||
status = twl4030_codec_enable_resource(
|
||||
TWL4030_CODEC_RES_APLL);
|
||||
status = twl4030_audio_enable_resource(
|
||||
TWL4030_AUDIO_RES_APLL);
|
||||
} else {
|
||||
twl4030->apll_enabled--;
|
||||
if (!twl4030->apll_enabled)
|
||||
status = twl4030_codec_disable_resource(
|
||||
TWL4030_CODEC_RES_APLL);
|
||||
status = twl4030_audio_disable_resource(
|
||||
TWL4030_AUDIO_RES_APLL);
|
||||
}
|
||||
|
||||
if (status >= 0)
|
||||
@ -732,7 +732,7 @@ static int aif_event(struct snd_soc_dapm_widget *w,
|
||||
|
||||
static void headset_ramp(struct snd_soc_codec *codec, int ramp)
|
||||
{
|
||||
struct twl4030_codec_audio_data *pdata = codec->dev->platform_data;
|
||||
struct twl4030_codec_data *pdata = codec->dev->platform_data;
|
||||
unsigned char hs_gain, hs_pop;
|
||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||
/* Base values for ramp delay calculation: 2^19 - 2^26 */
|
||||
@ -2260,7 +2260,7 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec)
|
||||
}
|
||||
snd_soc_codec_set_drvdata(codec, twl4030);
|
||||
/* Set the defaults, and power up the codec */
|
||||
twl4030->sysclk = twl4030_codec_get_mclk() / 1000;
|
||||
twl4030->sysclk = twl4030_audio_get_mclk() / 1000;
|
||||
codec->dapm.idle_bias_off = 1;
|
||||
|
||||
twl4030_init_chip(codec);
|
||||
@ -2297,7 +2297,7 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
|
||||
|
||||
static int __devinit twl4030_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data;
|
||||
struct twl4030_codec_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "platform_data is missing\n");
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,125 +22,8 @@
|
||||
#ifndef __TWL6040_H__
|
||||
#define __TWL6040_H__
|
||||
|
||||
#define TWL6040_REG_ASICID 0x01
|
||||
#define TWL6040_REG_ASICREV 0x02
|
||||
#define TWL6040_REG_INTID 0x03
|
||||
#define TWL6040_REG_INTMR 0x04
|
||||
#define TWL6040_REG_NCPCTL 0x05
|
||||
#define TWL6040_REG_LDOCTL 0x06
|
||||
#define TWL6040_REG_HPPLLCTL 0x07
|
||||
#define TWL6040_REG_LPPLLCTL 0x08
|
||||
#define TWL6040_REG_LPPLLDIV 0x09
|
||||
#define TWL6040_REG_AMICBCTL 0x0A
|
||||
#define TWL6040_REG_DMICBCTL 0x0B
|
||||
#define TWL6040_REG_MICLCTL 0x0C
|
||||
#define TWL6040_REG_MICRCTL 0x0D
|
||||
#define TWL6040_REG_MICGAIN 0x0E
|
||||
#define TWL6040_REG_LINEGAIN 0x0F
|
||||
#define TWL6040_REG_HSLCTL 0x10
|
||||
#define TWL6040_REG_HSRCTL 0x11
|
||||
#define TWL6040_REG_HSGAIN 0x12
|
||||
#define TWL6040_REG_EARCTL 0x13
|
||||
#define TWL6040_REG_HFLCTL 0x14
|
||||
#define TWL6040_REG_HFLGAIN 0x15
|
||||
#define TWL6040_REG_HFRCTL 0x16
|
||||
#define TWL6040_REG_HFRGAIN 0x17
|
||||
#define TWL6040_REG_VIBCTLL 0x18
|
||||
#define TWL6040_REG_VIBDATL 0x19
|
||||
#define TWL6040_REG_VIBCTLR 0x1A
|
||||
#define TWL6040_REG_VIBDATR 0x1B
|
||||
#define TWL6040_REG_HKCTL1 0x1C
|
||||
#define TWL6040_REG_HKCTL2 0x1D
|
||||
#define TWL6040_REG_GPOCTL 0x1E
|
||||
#define TWL6040_REG_ALB 0x1F
|
||||
#define TWL6040_REG_DLB 0x20
|
||||
#define TWL6040_REG_TRIM1 0x28
|
||||
#define TWL6040_REG_TRIM2 0x29
|
||||
#define TWL6040_REG_TRIM3 0x2A
|
||||
#define TWL6040_REG_HSOTRIM 0x2B
|
||||
#define TWL6040_REG_HFOTRIM 0x2C
|
||||
#define TWL6040_REG_ACCCTL 0x2D
|
||||
#define TWL6040_REG_STATUS 0x2E
|
||||
|
||||
#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1)
|
||||
|
||||
#define TWL6040_VIOREGNUM 18
|
||||
#define TWL6040_VDDREGNUM 21
|
||||
|
||||
/* INTID (0x03) fields */
|
||||
|
||||
#define TWL6040_THINT 0x01
|
||||
#define TWL6040_PLUGINT 0x02
|
||||
#define TWL6040_UNPLUGINT 0x04
|
||||
#define TWL6040_HOOKINT 0x08
|
||||
#define TWL6040_HFINT 0x10
|
||||
#define TWL6040_VIBINT 0x20
|
||||
#define TWL6040_READYINT 0x40
|
||||
|
||||
/* INTMR (0x04) fields */
|
||||
|
||||
#define TWL6040_PLUGMSK 0x02
|
||||
#define TWL6040_READYMSK 0x40
|
||||
#define TWL6040_ALLINT_MSK 0x7B
|
||||
|
||||
/* NCPCTL (0x05) fields */
|
||||
|
||||
#define TWL6040_NCPENA 0x01
|
||||
#define TWL6040_NCPOPEN 0x40
|
||||
|
||||
/* LDOCTL (0x06) fields */
|
||||
|
||||
#define TWL6040_LSLDOENA 0x01
|
||||
#define TWL6040_HSLDOENA 0x04
|
||||
#define TWL6040_REFENA 0x40
|
||||
#define TWL6040_OSCENA 0x80
|
||||
|
||||
/* HPPLLCTL (0x07) fields */
|
||||
|
||||
#define TWL6040_HPLLENA 0x01
|
||||
#define TWL6040_HPLLRST 0x02
|
||||
#define TWL6040_HPLLBP 0x04
|
||||
#define TWL6040_HPLLSQRENA 0x08
|
||||
#define TWL6040_HPLLSQRBP 0x10
|
||||
#define TWL6040_MCLK_12000KHZ (0 << 5)
|
||||
#define TWL6040_MCLK_19200KHZ (1 << 5)
|
||||
#define TWL6040_MCLK_26000KHZ (2 << 5)
|
||||
#define TWL6040_MCLK_38400KHZ (3 << 5)
|
||||
#define TWL6040_MCLK_MSK 0x60
|
||||
|
||||
/* LPPLLCTL (0x08) fields */
|
||||
|
||||
#define TWL6040_LPLLENA 0x01
|
||||
#define TWL6040_LPLLRST 0x02
|
||||
#define TWL6040_LPLLSEL 0x04
|
||||
#define TWL6040_LPLLFIN 0x08
|
||||
#define TWL6040_HPLLSEL 0x10
|
||||
|
||||
/* HSLCTL (0x10) fields */
|
||||
|
||||
#define TWL6040_HSDACMODEL 0x02
|
||||
#define TWL6040_HSDRVMODEL 0x08
|
||||
|
||||
/* HSRCTL (0x11) fields */
|
||||
|
||||
#define TWL6040_HSDACMODER 0x02
|
||||
#define TWL6040_HSDRVMODER 0x08
|
||||
|
||||
/* ACCCTL (0x2D) fields */
|
||||
|
||||
#define TWL6040_RESETSPLIT 0x04
|
||||
|
||||
#define TWL6040_SYSCLK_SEL_LPPLL 1
|
||||
#define TWL6040_SYSCLK_SEL_HPPLL 2
|
||||
|
||||
#define TWL6040_HPPLL_ID 1
|
||||
#define TWL6040_LPPLL_ID 2
|
||||
|
||||
/* STATUS (0x2E) fields */
|
||||
|
||||
#define TWL6040_PLUGCOMP 0x02
|
||||
|
||||
void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
|
||||
struct snd_soc_jack *jack, int report);
|
||||
int twl6040_get_clk_id(struct snd_soc_codec *codec);
|
||||
|
||||
#endif /* End of __TWL6040_H__ */
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include <plat/mcbsp.h>
|
||||
|
||||
/* Register descriptions for twl4030 codec part */
|
||||
#include <linux/mfd/twl4030-codec.h>
|
||||
#include <linux/mfd/twl4030-audio.h>
|
||||
|
||||
#include "omap-mcbsp.h"
|
||||
#include "omap-pcm.h"
|
||||
|
@ -21,6 +21,8 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/twl6040.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
@ -34,8 +36,6 @@
|
||||
#include "omap-pcm.h"
|
||||
#include "../codecs/twl6040.h"
|
||||
|
||||
static int twl6040_power_mode;
|
||||
|
||||
static int sdp4430_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
@ -44,13 +44,13 @@ static int sdp4430_hw_params(struct snd_pcm_substream *substream,
|
||||
int clk_id, freq;
|
||||
int ret;
|
||||
|
||||
if (twl6040_power_mode) {
|
||||
clk_id = TWL6040_SYSCLK_SEL_HPPLL;
|
||||
clk_id = twl6040_get_clk_id(rtd->codec);
|
||||
if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
|
||||
freq = 38400000;
|
||||
} else {
|
||||
clk_id = TWL6040_SYSCLK_SEL_LPPLL;
|
||||
else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
|
||||
freq = 32768;
|
||||
}
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* set the codec mclk */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq,
|
||||
@ -81,35 +81,6 @@ static struct snd_soc_jack_pin hs_jack_pins[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static int sdp4430_get_power_mode(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = twl6040_power_mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdp4430_set_power_mode(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
if (twl6040_power_mode == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
twl6040_power_mode = ucontrol->value.integer.value[0];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *power_texts[] = {"Low-Power", "High-Performance"};
|
||||
|
||||
static const struct soc_enum sdp4430_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(2, power_texts),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new sdp4430_controls[] = {
|
||||
SOC_ENUM_EXT("TWL6040 Power Mode", sdp4430_enum[0],
|
||||
sdp4430_get_power_mode, sdp4430_set_power_mode),
|
||||
};
|
||||
|
||||
/* SDP4430 machine DAPM */
|
||||
static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_MIC("Ext Mic", NULL),
|
||||
@ -152,12 +123,6 @@ static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd)
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
int ret;
|
||||
|
||||
/* Add SDP4430 specific controls */
|
||||
ret = snd_soc_add_controls(codec, sdp4430_controls,
|
||||
ARRAY_SIZE(sdp4430_controls));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Add SDP4430 specific widgets */
|
||||
ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets,
|
||||
ARRAY_SIZE(sdp4430_twl6040_dapm_widgets));
|
||||
@ -237,9 +202,6 @@ static int __init sdp4430_soc_init(void)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* Codec starts in HP mode */
|
||||
twl6040_power_mode = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include <plat/mcbsp.h>
|
||||
|
||||
/* Register descriptions for twl4030 codec part */
|
||||
#include <linux/mfd/twl4030-codec.h>
|
||||
#include <linux/mfd/twl4030-audio.h>
|
||||
|
||||
#include "omap-mcbsp.h"
|
||||
#include "omap-pcm.h"
|
||||
|
Loading…
Reference in New Issue
Block a user