From 8b5d8c0d718379ce29dad74b4bda8b669fc1f1c2 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 25 Sep 2012 19:33:35 +0300 Subject: ARM: OMAP3+: voltage: introduce omap vc / vp params for voltagedomains These new structs will hold the sleep voltage levels (omap_vc_params) and voltage processor min / max voltages (omap_vp_params.) Previously these were part of the PMIC struct, but they do not really belong there, as they are OMAP chip specific, not PMIC specific parameters. voltdm code is also changed to use the new structs. Signed-off-by: Tero Kristo Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/vc.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) (limited to 'arch/arm/mach-omap2/vc.c') diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index 880249b1701..4c3c41fd263 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -135,6 +135,8 @@ int omap_vc_pre_scale(struct voltagedomain *voltdm, vc_cmdval |= (*target_vsel << vc->common->cmd_on_shift); voltdm->write(vc_cmdval, vc->cmdval_reg); + voltdm->vc_param->on = target_volt; + omap_vp_update_errorgain(voltdm, target_volt); return 0; @@ -284,6 +286,30 @@ static void __init omap_vc_i2c_init(struct voltagedomain *voltdm) initialized = true; } +/** + * omap_vc_calc_vsel - calculate vsel value for a channel + * @voltdm: channel to calculate value for + * @uvolt: microvolt value to convert to vsel + * + * Converts a microvolt value to vsel value for the used PMIC. + * This checks whether the microvolt value is out of bounds, and + * adjusts the value accordingly. If unsupported value detected, + * warning is thrown. + */ +static u8 omap_vc_calc_vsel(struct voltagedomain *voltdm, u32 uvolt) +{ + if (voltdm->pmic->vddmin > uvolt) + uvolt = voltdm->pmic->vddmin; + if (voltdm->pmic->vddmax < uvolt) { + WARN(1, "%s: voltage not supported by pmic: %u vs max %u\n", + __func__, uvolt, voltdm->pmic->vddmax); + /* Lets try maximum value anyway */ + uvolt = voltdm->pmic->vddmax; + } + + return voltdm->pmic->uv_to_vsel(uvolt); +} + void __init omap_vc_init_channel(struct voltagedomain *voltdm) { struct omap_vc_channel *vc = voltdm->vc; @@ -335,10 +361,11 @@ void __init omap_vc_init_channel(struct voltagedomain *voltdm) } /* Set up the on, inactive, retention and off voltage */ - on_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->on_volt); - onlp_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->onlp_volt); - ret_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->ret_volt); - off_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->off_volt); + on_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->on); + onlp_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->onlp); + ret_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->ret); + off_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->off); + val = ((on_vsel << vc->common->cmd_on_shift) | (onlp_vsel << vc->common->cmd_onlp_shift) | (ret_vsel << vc->common->cmd_ret_shift) | -- cgit v1.2.3-70-g09d2 From c589eb3869a8ad6185669f5477bf72d6d46068de Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 25 Sep 2012 19:33:36 +0300 Subject: ARM: OMAP3: VC: calculate ramp times OMAP3 VC code now uses voltage deltas + slew rates for calculating actual ramp times for voltage changes. Previously a static value was used. Two calculation methods are provided: i2c_timings and off_timings. I2C timings are used during retention or off mode transition which is initiated over I2C, and OFF timings are used if PMIC signal (nsleep) is used to control all the off mode voltages at the same time. Signed-off-by: Tero Kristo Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/vc.c | 108 +++++++++++++++++++++++++++++++++++++++-------- arch/arm/mach-omap2/vc.h | 1 - 2 files changed, 91 insertions(+), 18 deletions(-) (limited to 'arch/arm/mach-omap2/vc.c') diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index 4c3c41fd263..73b4bcd6702 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -204,29 +204,109 @@ int omap_vc_bypass_scale(struct voltagedomain *voltdm, return 0; } -static void __init omap3_vfsm_init(struct voltagedomain *voltdm) +/** + * omap3_set_i2c_timings - sets i2c sleep timings for a channel + * @voltdm: channel to configure + * @off_mode: select whether retention or off mode values used + * + * Calculates and sets up voltage controller to use I2C based + * voltage scaling for sleep modes. This can be used for either off mode + * or retention. Off mode has additionally an option to use sys_off_mode + * pad, which uses a global signal to program the whole power IC to + * off-mode. + */ +static void omap3_set_i2c_timings(struct voltagedomain *voltdm, bool off_mode) { + unsigned long voltsetup1; + u32 tgt_volt; + + if (off_mode) + tgt_volt = voltdm->vc_param->off; + else + tgt_volt = voltdm->vc_param->ret; + + voltsetup1 = (voltdm->vc_param->on - tgt_volt) / + voltdm->pmic->slew_rate; + + voltsetup1 = voltsetup1 * voltdm->sys_clk.rate / 8 / 1000000 + 1; + + voltdm->rmw(voltdm->vfsm->voltsetup_mask, + voltsetup1 << __ffs(voltdm->vfsm->voltsetup_mask), + voltdm->vfsm->voltsetup_reg); + /* - * Voltage Manager FSM parameters init - * XXX This data should be passed in from the board file + * pmic is not controlling the voltage scaling during retention, + * thus set voltsetup2 to 0 */ - voltdm->write(OMAP3_CLKSETUP, OMAP3_PRM_CLKSETUP_OFFSET); - voltdm->write(OMAP3_VOLTOFFSET, OMAP3_PRM_VOLTOFFSET_OFFSET); - voltdm->write(OMAP3_VOLTSETUP2, OMAP3_PRM_VOLTSETUP2_OFFSET); + voltdm->write(0, OMAP3_PRM_VOLTSETUP2_OFFSET); } -static void __init omap3_vc_init_channel(struct voltagedomain *voltdm) +/** + * omap3_set_off_timings - sets off-mode timings for a channel + * @voltdm: channel to configure + * + * Calculates and sets up off-mode timings for a channel. Off-mode + * can use either I2C based voltage scaling, or alternatively + * sys_off_mode pad can be used to send a global command to power IC. + * This function first checks which mode is being used, and calls + * omap3_set_i2c_timings() if the system is using I2C control mode. + * sys_off_mode has the additional benefit that voltages can be + * scaled to zero volt level with TWL4030 / TWL5030, I2C can only + * scale to 600mV. + */ +static void omap3_set_off_timings(struct voltagedomain *voltdm) { - static bool is_initialized; + unsigned long clksetup; + unsigned long voltsetup2; + unsigned long voltsetup2_old; + u32 val; - if (is_initialized) + /* check if sys_off_mode is used to control off-mode voltages */ + val = voltdm->read(OMAP3_PRM_VOLTCTRL_OFFSET); + if (!(val & OMAP3430_SEL_OFF_MASK)) { + /* No, omap is controlling them over I2C */ + omap3_set_i2c_timings(voltdm, true); return; + } - omap3_vfsm_init(voltdm); + clksetup = voltdm->read(OMAP3_PRM_CLKSETUP_OFFSET); - is_initialized = true; + /* voltsetup 2 in us */ + voltsetup2 = voltdm->vc_param->on / voltdm->pmic->slew_rate; + + /* convert to 32k clk cycles */ + voltsetup2 = DIV_ROUND_UP(voltsetup2 * 32768, 1000000); + + voltsetup2_old = voltdm->read(OMAP3_PRM_VOLTSETUP2_OFFSET); + + /* + * Update voltsetup2 if higher than current value (needed because + * we have multiple channels with different ramp times), also + * update voltoffset always to value recommended by TRM + */ + if (voltsetup2 > voltsetup2_old) { + voltdm->write(voltsetup2, OMAP3_PRM_VOLTSETUP2_OFFSET); + voltdm->write(clksetup - voltsetup2, + OMAP3_PRM_VOLTOFFSET_OFFSET); + } else + voltdm->write(clksetup - voltsetup2_old, + OMAP3_PRM_VOLTOFFSET_OFFSET); + + /* + * omap is not controlling voltage scaling during off-mode, + * thus set voltsetup1 to 0 + */ + voltdm->rmw(voltdm->vfsm->voltsetup_mask, 0, + voltdm->vfsm->voltsetup_reg); + + /* voltoffset must be clksetup minus voltsetup2 according to TRM */ + voltdm->write(clksetup - voltsetup2, OMAP3_PRM_VOLTOFFSET_OFFSET); } +static void __init omap3_vc_init_channel(struct voltagedomain *voltdm) +{ + omap3_set_off_timings(voltdm); +} /* OMAP4 specific voltage init functions */ static void __init omap4_vc_init_channel(struct voltagedomain *voltdm) @@ -337,7 +417,6 @@ void __init omap_vc_init_channel(struct voltagedomain *voltdm) vc->i2c_slave_addr = voltdm->pmic->i2c_slave_addr; vc->volt_reg_addr = voltdm->pmic->volt_reg_addr; vc->cmd_reg_addr = voltdm->pmic->cmd_reg_addr; - vc->setup_time = voltdm->pmic->volt_setup_time; /* Configure the i2c slave address for this VC */ voltdm->rmw(vc->smps_sa_mask, @@ -376,11 +455,6 @@ void __init omap_vc_init_channel(struct voltagedomain *voltdm) /* Channel configuration */ omap_vc_config_channel(voltdm); - /* Configure the setup times */ - voltdm->rmw(voltdm->vfsm->voltsetup_mask, - vc->setup_time << __ffs(voltdm->vfsm->voltsetup_mask), - voltdm->vfsm->voltsetup_reg); - omap_vc_i2c_init(voltdm); if (cpu_is_omap34xx()) diff --git a/arch/arm/mach-omap2/vc.h b/arch/arm/mach-omap2/vc.h index 7618b69811d..91c8d75bf2e 100644 --- a/arch/arm/mach-omap2/vc.h +++ b/arch/arm/mach-omap2/vc.h @@ -86,7 +86,6 @@ struct omap_vc_channel { u16 i2c_slave_addr; u16 volt_reg_addr; u16 cmd_reg_addr; - u16 setup_time; u8 cfg_channel; bool i2c_high_speed; -- cgit v1.2.3-70-g09d2 From 9a1729cbaaf1a9d1fd27f80cd488ef182fe033a0 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 25 Sep 2012 19:33:38 +0300 Subject: ARM: OMAP4: VC: calculate ramp times OMAP4 VC code now uses voltage deltas + slew rates for calculating actual ramp times for voltage changes. Both retention / sleep + off mode voltage ramp times are setup at the same time during initialization. Signed-off-by: Tero Kristo Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/vc.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) (limited to 'arch/arm/mach-omap2/vc.c') diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index 73b4bcd6702..07e2090d711 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -308,12 +308,106 @@ static void __init omap3_vc_init_channel(struct voltagedomain *voltdm) omap3_set_off_timings(voltdm); } +/** + * omap4_calc_volt_ramp - calculates voltage ramping delays on omap4 + * @voltdm: channel to calculate values for + * @voltage_diff: voltage difference in microvolts + * + * Calculates voltage ramp prescaler + counter values for a voltage + * difference on omap4. Returns a field value suitable for writing to + * VOLTSETUP register for a channel in following format: + * bits[8:9] prescaler ... bits[0:5] counter. See OMAP4 TRM for reference. + */ +static u32 omap4_calc_volt_ramp(struct voltagedomain *voltdm, u32 voltage_diff) +{ + u32 prescaler; + u32 cycles; + u32 time; + + time = voltage_diff / voltdm->pmic->slew_rate; + + cycles = voltdm->sys_clk.rate / 1000 * time / 1000; + + cycles /= 64; + prescaler = 0; + + /* shift to next prescaler until no overflow */ + + /* scale for div 256 = 64 * 4 */ + if (cycles > 63) { + cycles /= 4; + prescaler++; + } + + /* scale for div 512 = 256 * 2 */ + if (cycles > 63) { + cycles /= 2; + prescaler++; + } + + /* scale for div 2048 = 512 * 4 */ + if (cycles > 63) { + cycles /= 4; + prescaler++; + } + + /* check for overflow => invalid ramp time */ + if (cycles > 63) { + pr_warn("%s: invalid setuptime for vdd_%s\n", __func__, + voltdm->name); + return 0; + } + + cycles++; + + return (prescaler << OMAP4430_RAMP_UP_PRESCAL_SHIFT) | + (cycles << OMAP4430_RAMP_UP_COUNT_SHIFT); +} + +/** + * omap4_set_timings - set voltage ramp timings for a channel + * @voltdm: channel to configure + * @off_mode: whether off-mode values are used + * + * Calculates and sets the voltage ramp up / down values for a channel. + */ +static void omap4_set_timings(struct voltagedomain *voltdm, bool off_mode) +{ + u32 val; + u32 ramp; + int offset; + + if (off_mode) { + ramp = omap4_calc_volt_ramp(voltdm, + voltdm->vc_param->on - voltdm->vc_param->off); + offset = voltdm->vfsm->voltsetup_off_reg; + } else { + ramp = omap4_calc_volt_ramp(voltdm, + voltdm->vc_param->on - voltdm->vc_param->ret); + offset = voltdm->vfsm->voltsetup_reg; + } + + if (!ramp) + return; + + val = voltdm->read(offset); + + val |= ramp << OMAP4430_RAMP_DOWN_COUNT_SHIFT; + + val |= ramp << OMAP4430_RAMP_UP_COUNT_SHIFT; + + voltdm->write(val, offset); +} + /* OMAP4 specific voltage init functions */ static void __init omap4_vc_init_channel(struct voltagedomain *voltdm) { static bool is_initialized; u32 vc_val; + omap4_set_timings(voltdm, true); + omap4_set_timings(voltdm, false); + if (is_initialized) return; -- cgit v1.2.3-70-g09d2 From d68ff977b82954fed8e3f4bde8431517455c2003 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 25 Sep 2012 19:33:41 +0300 Subject: ARM: OMAP3+: voltage: use oscillator data to calculate setup times We now use the previously defined oscillator setup / shutdown times to calculate the register values for CLKSETUP. Signed-off-by: Tero Kristo Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/vc.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'arch/arm/mach-omap2/vc.c') diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index 07e2090d711..5d5f9e52f89 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -11,13 +11,19 @@ #include #include #include +#include +#include + +#include "iomap.h" #include "soc.h" #include "voltage.h" #include "vc.h" #include "prm-regbits-34xx.h" #include "prm-regbits-44xx.h" #include "prm44xx.h" +#include "pm.h" +#include "scrm44xx.h" /** * struct omap_vc_channel_cfg - describe the cfg_channel bitfield @@ -204,6 +210,18 @@ int omap_vc_bypass_scale(struct voltagedomain *voltdm, return 0; } +/* Convert microsecond value to number of 32kHz clock cycles */ +static inline u32 omap_usec_to_32k(u32 usec) +{ + return DIV_ROUND_UP_ULL(32768ULL * (u64)usec, 1000000ULL); +} + +/* Set oscillator setup time for omap3 */ +static void omap3_set_clksetup(u32 usec, struct voltagedomain *voltdm) +{ + voltdm->write(omap_usec_to_32k(usec), OMAP3_PRM_CLKSETUP_OFFSET); +} + /** * omap3_set_i2c_timings - sets i2c sleep timings for a channel * @voltdm: channel to configure @@ -220,6 +238,12 @@ static void omap3_set_i2c_timings(struct voltagedomain *voltdm, bool off_mode) unsigned long voltsetup1; u32 tgt_volt; + /* + * Oscillator is shut down only if we are using sys_off_mode pad, + * thus we set a minimal setup time here + */ + omap3_set_clksetup(1, voltdm); + if (off_mode) tgt_volt = voltdm->vc_param->off; else @@ -260,6 +284,7 @@ static void omap3_set_off_timings(struct voltagedomain *voltdm) unsigned long voltsetup2; unsigned long voltsetup2_old; u32 val; + u32 tstart, tshut; /* check if sys_off_mode is used to control off-mode voltages */ val = voltdm->read(OMAP3_PRM_VOLTCTRL_OFFSET); @@ -269,6 +294,9 @@ static void omap3_set_off_timings(struct voltagedomain *voltdm) return; } + omap_pm_get_oscillator(&tstart, &tshut); + omap3_set_clksetup(tstart, voltdm); + clksetup = voltdm->read(OMAP3_PRM_CLKSETUP_OFFSET); /* voltsetup 2 in us */ @@ -364,6 +392,30 @@ static u32 omap4_calc_volt_ramp(struct voltagedomain *voltdm, u32 voltage_diff) (cycles << OMAP4430_RAMP_UP_COUNT_SHIFT); } +/** + * omap4_usec_to_val_scrm - convert microsecond value to SCRM module bitfield + * @usec: microseconds + * @shift: number of bits to shift left + * @mask: bitfield mask + * + * Converts microsecond value to OMAP4 SCRM bitfield. Bitfield is + * shifted to requested position, and checked agains the mask value. + * If larger, forced to the max value of the field (i.e. the mask itself.) + * Returns the SCRM bitfield value. + */ +static u32 omap4_usec_to_val_scrm(u32 usec, int shift, u32 mask) +{ + u32 val; + + val = omap_usec_to_32k(usec) << shift; + + /* Check for overflow, if yes, force to max value */ + if (val > mask) + val = mask; + + return val; +} + /** * omap4_set_timings - set voltage ramp timings for a channel * @voltdm: channel to configure @@ -376,6 +428,7 @@ static void omap4_set_timings(struct voltagedomain *voltdm, bool off_mode) u32 val; u32 ramp; int offset; + u32 tstart, tshut; if (off_mode) { ramp = omap4_calc_volt_ramp(voltdm, @@ -397,6 +450,15 @@ static void omap4_set_timings(struct voltagedomain *voltdm, bool off_mode) val |= ramp << OMAP4430_RAMP_UP_COUNT_SHIFT; voltdm->write(val, offset); + + omap_pm_get_oscillator(&tstart, &tshut); + + val = omap4_usec_to_val_scrm(tstart, OMAP4_SETUPTIME_SHIFT, + OMAP4_SETUPTIME_MASK); + val |= omap4_usec_to_val_scrm(tshut, OMAP4_DOWNTIME_SHIFT, + OMAP4_DOWNTIME_MASK); + + __raw_writel(val, OMAP4_SCRM_CLKSETUPTIME); } /* OMAP4 specific voltage init functions */ -- cgit v1.2.3-70-g09d2 From 2ceec7b25c3cde53c68e49d64950f2ad1cab307d Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 25 Sep 2012 19:33:47 +0300 Subject: ARM: OMAP4: vc: fix channel configuration RACEN bit should only be set if the voltage and command register addresses are the same. Signed-off-by: Tero Kristo Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/vc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'arch/arm/mach-omap2/vc.c') diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index 5d5f9e52f89..d72b787a0d8 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -592,9 +592,12 @@ void __init omap_vc_init_channel(struct voltagedomain *voltdm) voltdm->rmw(vc->smps_cmdra_mask, vc->cmd_reg_addr << __ffs(vc->smps_cmdra_mask), vc->smps_cmdra_reg); - vc->cfg_channel |= vc_cfg_bits->rac | vc_cfg_bits->racen; + vc->cfg_channel |= vc_cfg_bits->rac; } + if (vc->cmd_reg_addr == vc->volt_reg_addr) + vc->cfg_channel |= vc_cfg_bits->racen; + /* Set up the on, inactive, retention and off voltage */ on_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->on); onlp_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->onlp); -- cgit v1.2.3-70-g09d2 From 00bd228ea9f7aad23f7933fa62a13d975d4b213a Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 25 Sep 2012 19:33:48 +0300 Subject: ARM: OMAP4: VC: setup I2C parameters based on board data VC code now provides a table of pre-calculated I2C setup parameters, which will be used based on the capacitance value calculated for the I2C trace on the PCB. A default trace length of 6.3cm is used unless board defines its own value during init. The parameters set will be the I2C internal pull setup and the I2C timing parameters for high speed use mode. Full speed mode is not supported as of now. Signed-off-by: Tero Kristo Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/omap_twl.c | 3 + arch/arm/mach-omap2/pm.h | 2 + arch/arm/mach-omap2/vc.c | 149 ++++++++++++++++++++++++++++++++++++++--- arch/arm/mach-omap2/voltage.h | 1 + 4 files changed, 147 insertions(+), 8 deletions(-) (limited to 'arch/arm/mach-omap2/vc.c') diff --git a/arch/arm/mach-omap2/omap_twl.c b/arch/arm/mach-omap2/omap_twl.c index ecae9890f0f..611cb63d5ce 100644 --- a/arch/arm/mach-omap2/omap_twl.c +++ b/arch/arm/mach-omap2/omap_twl.c @@ -183,6 +183,7 @@ static struct omap_voltdm_pmic omap4_mpu_pmic = { .volt_reg_addr = OMAP4_VDD_MPU_SR_VOLT_REG, .cmd_reg_addr = OMAP4_VDD_MPU_SR_CMD_REG, .i2c_high_speed = true, + .i2c_pad_load = 3, .vsel_to_uv = twl6030_vsel_to_uv, .uv_to_vsel = twl6030_uv_to_vsel, }; @@ -200,6 +201,7 @@ static struct omap_voltdm_pmic omap4_iva_pmic = { .volt_reg_addr = OMAP4_VDD_IVA_SR_VOLT_REG, .cmd_reg_addr = OMAP4_VDD_IVA_SR_CMD_REG, .i2c_high_speed = true, + .i2c_pad_load = 3, .vsel_to_uv = twl6030_vsel_to_uv, .uv_to_vsel = twl6030_uv_to_vsel, }; @@ -216,6 +218,7 @@ static struct omap_voltdm_pmic omap4_core_pmic = { .i2c_slave_addr = OMAP4_SRI2C_SLAVE_ADDR, .volt_reg_addr = OMAP4_VDD_CORE_SR_VOLT_REG, .cmd_reg_addr = OMAP4_VDD_CORE_SR_CMD_REG, + .i2c_pad_load = 3, .vsel_to_uv = twl6030_vsel_to_uv, .uv_to_vsel = twl6030_uv_to_vsel, }; diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h index 42902885210..4db7b238a0d 100644 --- a/arch/arm/mach-omap2/pm.h +++ b/arch/arm/mach-omap2/pm.h @@ -132,9 +132,11 @@ static inline int omap4_twl_init(void) #ifdef CONFIG_PM extern void omap_pm_setup_oscillator(u32 tstart, u32 tshut); extern void omap_pm_get_oscillator(u32 *tstart, u32 *tshut); +extern void omap_pm_setup_sr_i2c_pcb_length(u32 mm); #else static inline void omap_pm_setup_oscillator(u32 tstart, u32 tshut) { } static inline void omap_pm_get_oscillator(u32 *tstart, u32 *tshut) { } +static inline void omap_pm_setup_sr_i2c_pcb_length(u32 mm) { } #endif #endif diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index d72b787a0d8..687aa86c0d5 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -24,6 +24,7 @@ #include "prm44xx.h" #include "pm.h" #include "scrm44xx.h" +#include "control.h" /** * struct omap_vc_channel_cfg - describe the cfg_channel bitfield @@ -69,6 +70,9 @@ static struct omap_vc_channel_cfg vc_mutant_channel_cfg = { }; static struct omap_vc_channel_cfg *vc_cfg_bits; + +/* Default I2C trace length on pcb, 6.3cm. Used for capacitance calculations. */ +static u32 sr_i2c_pcb_length = 63; #define CFG_CHANNEL_MASK 0x1f /** @@ -464,22 +468,135 @@ static void omap4_set_timings(struct voltagedomain *voltdm, bool off_mode) /* OMAP4 specific voltage init functions */ static void __init omap4_vc_init_channel(struct voltagedomain *voltdm) { - static bool is_initialized; - u32 vc_val; - omap4_set_timings(voltdm, true); omap4_set_timings(voltdm, false); +} + +struct i2c_init_data { + u8 loadbits; + u8 load; + u8 hsscll_38_4; + u8 hsscll_26; + u8 hsscll_19_2; + u8 hsscll_16_8; + u8 hsscll_12; +}; + +static const __initdata struct i2c_init_data omap4_i2c_timing_data[] = { + { + .load = 50, + .loadbits = 0x3, + .hsscll_38_4 = 13, + .hsscll_26 = 11, + .hsscll_19_2 = 9, + .hsscll_16_8 = 9, + .hsscll_12 = 8, + }, + { + .load = 25, + .loadbits = 0x2, + .hsscll_38_4 = 13, + .hsscll_26 = 11, + .hsscll_19_2 = 9, + .hsscll_16_8 = 9, + .hsscll_12 = 8, + }, + { + .load = 12, + .loadbits = 0x1, + .hsscll_38_4 = 11, + .hsscll_26 = 10, + .hsscll_19_2 = 9, + .hsscll_16_8 = 9, + .hsscll_12 = 8, + }, + { + .load = 0, + .loadbits = 0x0, + .hsscll_38_4 = 12, + .hsscll_26 = 10, + .hsscll_19_2 = 9, + .hsscll_16_8 = 8, + .hsscll_12 = 8, + }, +}; + +/** + * omap4_vc_i2c_timing_init - sets up board I2C timing parameters + * @voltdm: voltagedomain pointer to get data from + * + * Use PMIC + board supplied settings for calculating the total I2C + * channel capacitance and set the timing parameters based on this. + * Pre-calculated values are provided in data tables, as it is not + * too straightforward to calculate these runtime. + */ +static void __init omap4_vc_i2c_timing_init(struct voltagedomain *voltdm) +{ + u32 capacitance; + u32 val; + u16 hsscll; + const struct i2c_init_data *i2c_data; + + if (!voltdm->pmic->i2c_high_speed) { + pr_warn("%s: only high speed supported!\n", __func__); + return; + } - if (is_initialized) + /* PCB trace capacitance, 0.125pF / mm => mm / 8 */ + capacitance = DIV_ROUND_UP(sr_i2c_pcb_length, 8); + + /* OMAP pad capacitance */ + capacitance += 4; + + /* PMIC pad capacitance */ + capacitance += voltdm->pmic->i2c_pad_load; + + /* Search for capacitance match in the table */ + i2c_data = omap4_i2c_timing_data; + + while (i2c_data->load > capacitance) + i2c_data++; + + /* Select proper values based on sysclk frequency */ + switch (voltdm->sys_clk.rate) { + case 38400000: + hsscll = i2c_data->hsscll_38_4; + break; + case 26000000: + hsscll = i2c_data->hsscll_26; + break; + case 19200000: + hsscll = i2c_data->hsscll_19_2; + break; + case 16800000: + hsscll = i2c_data->hsscll_16_8; + break; + case 12000000: + hsscll = i2c_data->hsscll_12; + break; + default: + pr_warn("%s: unsupported sysclk rate: %d!\n", __func__, + voltdm->sys_clk.rate); return; + } + + /* Loadbits define pull setup for the I2C channels */ + val = i2c_data->loadbits << 25 | i2c_data->loadbits << 29; - /* XXX These are magic numbers and do not belong! */ - vc_val = (0x60 << OMAP4430_SCLL_SHIFT | 0x26 << OMAP4430_SCLH_SHIFT); - voltdm->write(vc_val, OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET); + /* Write to SYSCTRL_PADCONF_WKUP_CTRL_I2C_2 to setup I2C pull */ + __raw_writel(val, OMAP2_L4_IO_ADDRESS(OMAP4_CTRL_MODULE_PAD_WKUP + + OMAP4_CTRL_MODULE_PAD_WKUP_CONTROL_I2C_2)); - is_initialized = true; + /* HSSCLH can always be zero */ + val = hsscll << OMAP4430_HSSCLL_SHIFT; + val |= (0x28 << OMAP4430_SCLL_SHIFT | 0x2c << OMAP4430_SCLH_SHIFT); + + /* Write setup times to I2C config register */ + voltdm->write(val, OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET); } + + /** * omap_vc_i2c_init - initialize I2C interface to PMIC * @voltdm: voltage domain containing VC data @@ -519,6 +636,9 @@ static void __init omap_vc_i2c_init(struct voltagedomain *voltdm) mcode << __ffs(vc->common->i2c_mcode_mask), vc->common->i2c_cfg_reg); + if (cpu_is_omap44xx()) + omap4_vc_i2c_timing_init(voltdm); + initialized = true; } @@ -546,6 +666,19 @@ static u8 omap_vc_calc_vsel(struct voltagedomain *voltdm, u32 uvolt) return voltdm->pmic->uv_to_vsel(uvolt); } +/** + * omap_pm_setup_sr_i2c_pcb_length - set length of SR I2C traces on PCB + * @mm: length of the PCB trace in millimetres + * + * Sets the PCB trace length for the I2C channel. By default uses 63mm. + * This is needed for properly calculating the capacitance value for + * the PCB trace, and for setting the SR I2C channel timing parameters. + */ +void __init omap_pm_setup_sr_i2c_pcb_length(u32 mm) +{ + sr_i2c_pcb_length = mm; +} + void __init omap_vc_init_channel(struct voltagedomain *voltdm) { struct omap_vc_channel *vc = voltdm->vc; diff --git a/arch/arm/mach-omap2/voltage.h b/arch/arm/mach-omap2/voltage.h index 78923f93efd..a0ce4f10ff1 100644 --- a/arch/arm/mach-omap2/voltage.h +++ b/arch/arm/mach-omap2/voltage.h @@ -139,6 +139,7 @@ struct omap_voltdm_pmic { u32 vddmax; u8 vp_timeout_us; bool i2c_high_speed; + u32 i2c_pad_load; u8 i2c_mcode; unsigned long (*vsel_to_uv) (const u8 vsel); u8 (*uv_to_vsel) (unsigned long uV); -- cgit v1.2.3-70-g09d2 From 74d29168e9af59c9db1885e27493fbed4d24ef18 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Wed, 14 Nov 2012 17:13:04 -0800 Subject: ARM: OMAP2+: voltage: fixup oscillator handling when CONFIG_PM=n commit 908b75e8 (ARM: OMAP: add support for oscillator setup) added a new API for oscillator setup, but is broken when CONFIG_PM=n. The new functions have dummy definitions when CONFIG_PM=n, but also have full implementations available, which conflict. To fix, wrap the PM implmentations in #ifdef CONFIG_PM. Cc: Tero Kristo Signed-off-by: Kevin Hilman Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/pm.c | 2 ++ arch/arm/mach-omap2/pm.h | 2 +- arch/arm/mach-omap2/vc.c | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'arch/arm/mach-omap2/vc.c') diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 109a02e02d7..ef668c756db 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -39,6 +39,7 @@ static struct omap_device_pm_latency *pm_lats; */ int (*omap_pm_suspend)(void); +#ifdef CONFIG_PM /** * struct omap2_oscillator - Describe the board main oscillator latencies * @startup_time: oscillator startup latency @@ -68,6 +69,7 @@ void omap_pm_get_oscillator(u32 *tstart, u32 *tshut) *tstart = oscillator.startup_time; *tshut = oscillator.shutdown_time; } +#endif static int __init _init_omap_device(char *name) { diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h index 4db7b238a0d..02c291c8e47 100644 --- a/arch/arm/mach-omap2/pm.h +++ b/arch/arm/mach-omap2/pm.h @@ -135,7 +135,7 @@ extern void omap_pm_get_oscillator(u32 *tstart, u32 *tshut); extern void omap_pm_setup_sr_i2c_pcb_length(u32 mm); #else static inline void omap_pm_setup_oscillator(u32 tstart, u32 tshut) { } -static inline void omap_pm_get_oscillator(u32 *tstart, u32 *tshut) { } +static inline void omap_pm_get_oscillator(u32 *tstart, u32 *tshut) { *tstart = *tshut = 0; } static inline void omap_pm_setup_sr_i2c_pcb_length(u32 mm) { } #endif diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index 687aa86c0d5..a89ec8affed 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -666,6 +666,7 @@ static u8 omap_vc_calc_vsel(struct voltagedomain *voltdm, u32 uvolt) return voltdm->pmic->uv_to_vsel(uvolt); } +#ifdef CONFIG_PM /** * omap_pm_setup_sr_i2c_pcb_length - set length of SR I2C traces on PCB * @mm: length of the PCB trace in millimetres @@ -678,6 +679,7 @@ void __init omap_pm_setup_sr_i2c_pcb_length(u32 mm) { sr_i2c_pcb_length = mm; } +#endif void __init omap_vc_init_channel(struct voltagedomain *voltdm) { -- cgit v1.2.3-70-g09d2