|
@@ -620,3 +620,140 @@ static void __init omap_vc_i2c_init(struct voltagedomain *voltdm)
|
|
if (initialized) {
|
|
if (initialized) {
|
|
if (voltdm->pmic->i2c_high_speed != i2c_high_speed)
|
|
if (voltdm->pmic->i2c_high_speed != i2c_high_speed)
|
|
pr_warn("%s: I2C config for vdd_%s does not match other channels (%u).\n",
|
|
pr_warn("%s: I2C config for vdd_%s does not match other channels (%u).\n",
|
|
|
|
+ __func__, voltdm->name, i2c_high_speed);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ i2c_high_speed = voltdm->pmic->i2c_high_speed;
|
|
|
|
+ if (i2c_high_speed)
|
|
|
|
+ voltdm->rmw(vc->common->i2c_cfg_hsen_mask,
|
|
|
|
+ vc->common->i2c_cfg_hsen_mask,
|
|
|
|
+ vc->common->i2c_cfg_reg);
|
|
|
|
+
|
|
|
|
+ mcode = voltdm->pmic->i2c_mcode;
|
|
|
|
+ if (mcode)
|
|
|
|
+ voltdm->rmw(vc->common->i2c_mcode_mask,
|
|
|
|
+ mcode << __ffs(vc->common->i2c_mcode_mask),
|
|
|
|
+ vc->common->i2c_cfg_reg);
|
|
|
|
+
|
|
|
|
+ if (cpu_is_omap44xx())
|
|
|
|
+ omap4_vc_i2c_timing_init(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);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#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
|
|
|
|
+ *
|
|
|
|
+ * 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;
|
|
|
|
+}
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+void __init omap_vc_init_channel(struct voltagedomain *voltdm)
|
|
|
|
+{
|
|
|
|
+ struct omap_vc_channel *vc = voltdm->vc;
|
|
|
|
+ u8 on_vsel, onlp_vsel, ret_vsel, off_vsel;
|
|
|
|
+ u32 val;
|
|
|
|
+
|
|
|
|
+ if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) {
|
|
|
|
+ pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!voltdm->read || !voltdm->write) {
|
|
|
|
+ pr_err("%s: No read/write API for accessing vdd_%s regs\n",
|
|
|
|
+ __func__, voltdm->name);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ vc->cfg_channel = 0;
|
|
|
|
+ if (vc->flags & OMAP_VC_CHANNEL_CFG_MUTANT)
|
|
|
|
+ vc_cfg_bits = &vc_mutant_channel_cfg;
|
|
|
|
+ else
|
|
|
|
+ vc_cfg_bits = &vc_default_channel_cfg;
|
|
|
|
+
|
|
|
|
+ /* get PMIC/board specific settings */
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ /* Configure the i2c slave address for this VC */
|
|
|
|
+ voltdm->rmw(vc->smps_sa_mask,
|
|
|
|
+ vc->i2c_slave_addr << __ffs(vc->smps_sa_mask),
|
|
|
|
+ vc->smps_sa_reg);
|
|
|
|
+ vc->cfg_channel |= vc_cfg_bits->sa;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Configure the PMIC register addresses.
|
|
|
|
+ */
|
|
|
|
+ voltdm->rmw(vc->smps_volra_mask,
|
|
|
|
+ vc->volt_reg_addr << __ffs(vc->smps_volra_mask),
|
|
|
|
+ vc->smps_volra_reg);
|
|
|
|
+ vc->cfg_channel |= vc_cfg_bits->rav;
|
|
|
|
+
|
|
|
|
+ if (vc->cmd_reg_addr) {
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ 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) |
|
|
|
|
+ (off_vsel << vc->common->cmd_off_shift));
|
|
|
|
+ voltdm->write(val, vc->cmdval_reg);
|
|
|
|
+ vc->cfg_channel |= vc_cfg_bits->cmd;
|
|
|
|
+
|
|
|
|
+ /* Channel configuration */
|
|
|
|
+ omap_vc_config_channel(voltdm);
|
|
|
|
+
|
|
|
|
+ omap_vc_i2c_init(voltdm);
|
|
|
|
+
|
|
|
|
+ if (cpu_is_omap34xx())
|
|
|
|
+ omap3_vc_init_channel(voltdm);
|
|
|
|
+ else if (cpu_is_omap44xx())
|
|
|
|
+ omap4_vc_init_channel(voltdm);
|
|
|
|
+}
|
|
|
|
+
|