// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) EETS GmbH, 2017, Felix Brack */ #include #include #include #include #include #include #define VOUT_CHOICE_COUNT 4 /* * struct regulator_props - Properties of a LDO and VIO SMPS regulator * * All of these regulators allow setting one out of four output voltages. * These output voltages are only achievable when supplying the regulator * with a minimum input voltage. * * @vin_min[]: minimum supply input voltage in uV required to achieve the * corresponding vout[] voltage * @vout[]: regulator output voltage in uV * @reg: I2C register used to set regulator voltage */ struct regulator_props { int vin_min[VOUT_CHOICE_COUNT]; int vout[VOUT_CHOICE_COUNT]; int reg; }; static const struct regulator_props ldo_props_vdig1 = { .vin_min = { 1700000, 2100000, 2700000, 3200000 }, .vout = { 1200000, 1500000, 1800000, 2700000 }, .reg = TPS65910_REG_VDIG1 }; static const struct regulator_props ldo_props_vdig2 = { .vin_min = { 1700000, 1700000, 1700000, 2700000 }, .vout = { 1000000, 1100000, 1200000, 1800000 }, .reg = TPS65910_REG_VDIG2 }; static const struct regulator_props ldo_props_vpll = { .vin_min = { 2700000, 2700000, 2700000, 3000000 }, .vout = { 1000000, 1100000, 1800000, 2500000 }, .reg = TPS65910_REG_VPLL }; static const struct regulator_props ldo_props_vdac = { .vin_min = { 2700000, 3000000, 3200000, 3200000 }, .vout = { 1800000, 2600000, 2800000, 2850000 }, .reg = TPS65910_REG_VDAC }; static const struct regulator_props ldo_props_vaux1 = { .vin_min = { 2700000, 3200000, 3200000, 3200000 }, .vout = { 1800000, 2500000, 2800000, 2850000 }, .reg = TPS65910_REG_VAUX1 }; static const struct regulator_props ldo_props_vaux2 = { .vin_min = { 2700000, 3200000, 3200000, 3600000 }, .vout = { 1800000, 2800000, 2900000, 3300000 }, .reg = TPS65910_REG_VAUX2 }; static const struct regulator_props ldo_props_vaux33 = { .vin_min = { 2700000, 2700000, 3200000, 3600000 }, .vout = { 1800000, 2000000, 2800000, 3300000 }, .reg = TPS65910_REG_VAUX33 }; static const struct regulator_props ldo_props_vmmc = { .vin_min = { 2700000, 3200000, 3200000, 3600000 }, .vout = { 1800000, 2800000, 3000000, 3300000 }, .reg = TPS65910_REG_VMMC }; static const struct regulator_props smps_props_vio = { .vin_min = { 3200000, 3200000, 4000000, 4400000 }, .vout = { 1500000, 1800000, 2500000, 3300000 }, .reg = TPS65910_REG_VIO }; /* lookup table of control registers indexed by regulator unit number */ static const int ctrl_regs[] = { TPS65910_REG_VRTC, TPS65910_REG_VIO, TPS65910_REG_VDD1, TPS65910_REG_VDD2, TPS65910_REG_VDD3, TPS65910_REG_VDIG1, TPS65910_REG_VDIG2, TPS65910_REG_VPLL, TPS65910_REG_VDAC, TPS65910_REG_VAUX1, TPS65910_REG_VAUX2, TPS65910_REG_VAUX33, TPS65910_REG_VMMC }; /* supply names as used in DT */ static const char * const supply_names[] = { "vccio-supply", "vcc1-supply", "vcc2-supply", "vcc3-supply", "vcc4-supply", "vcc5-supply", "vcc6-supply", "vcc7-supply" }; /* lookup table of regulator supplies indexed by regulator unit number */ static const int regulator_supplies[] = { TPS65910_SUPPLY_VCC7, TPS65910_SUPPLY_VCCIO, TPS65910_SUPPLY_VCC1, TPS65910_SUPPLY_VCC2, TPS65910_SUPPLY_VCC7, TPS65910_SUPPLY_VCC6, TPS65910_SUPPLY_VCC6, TPS65910_SUPPLY_VCC5, TPS65910_SUPPLY_VCC5, TPS65910_SUPPLY_VCC4, TPS65910_SUPPLY_VCC4, TPS65910_SUPPLY_VCC3, TPS65910_SUPPLY_VCC3 }; static int get_ctrl_reg_from_unit_addr(const uint unit_addr) { if (unit_addr < ARRAY_SIZE(ctrl_regs)) return ctrl_regs[unit_addr]; return -ENXIO; } static int tps65910_regulator_get_value(struct udevice *dev, const struct regulator_props *rgp) { int sel, val, vout; struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); int vin = pdata->supply; val = pmic_reg_read(dev->parent, rgp->reg); if (val < 0) return val; sel = (val & TPS65910_SEL_MASK) >> 2; vout = (vin >= *(rgp->vin_min + sel)) ? *(rgp->vout + sel) : 0; vout = ((val & TPS65910_SUPPLY_STATE_MASK) == 1) ? vout : 0; return vout; } static int tps65910_ldo_get_value(struct udevice *dev) { struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); int vin; if (!pdata) return 0; vin = pdata->supply; switch (pdata->unit) { case TPS65910_UNIT_VRTC: /* VRTC is fixed and can't be turned off */ return (vin >= 2500000) ? 1830000 : 0; case TPS65910_UNIT_VDIG1: return tps65910_regulator_get_value(dev, &ldo_props_vdig1); case TPS65910_UNIT_VDIG2: return tps65910_regulator_get_value(dev, &ldo_props_vdig2); case TPS65910_UNIT_VPLL: return tps65910_regulator_get_value(dev, &ldo_props_vpll); case TPS65910_UNIT_VDAC: return tps65910_regulator_get_value(dev, &ldo_props_vdac); case TPS65910_UNIT_VAUX1: return tps65910_regulator_get_value(dev, &ldo_props_vaux1); case TPS65910_UNIT_VAUX2: return tps65910_regulator_get_value(dev, &ldo_props_vaux2); case TPS65910_UNIT_VAUX33: return tps65910_regulator_get_value(dev, &ldo_props_vaux33); case TPS65910_UNIT_VMMC: return tps65910_regulator_get_value(dev, &ldo_props_vmmc); default: return 0; } } static int tps65910_regulator_set_value(struct udevice *dev, const struct regulator_props *ldo, int uV) { int val; int sel = 0; struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); do { /* we only allow exact voltage matches */ if (uV == *(ldo->vout + sel)) break; } while (++sel < VOUT_CHOICE_COUNT); if (sel == VOUT_CHOICE_COUNT) return -EINVAL; if (pdata->supply < *(ldo->vin_min + sel)) return -EINVAL; val = pmic_reg_read(dev->parent, ldo->reg); if (val < 0) return val; val &= ~TPS65910_SEL_MASK; val |= sel << 2; return pmic_reg_write(dev->parent, ldo->reg, val); } static int tps65910_ldo_set_value(struct udevice *dev, int uV) { struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); int vin = pdata->supply; switch (pdata->unit) { case TPS65910_UNIT_VRTC: /* VRTC is fixed to 1.83V and can't be turned off */ if (vin < 2500000) return -EINVAL; return 0; case TPS65910_UNIT_VDIG1: return tps65910_regulator_set_value(dev, &ldo_props_vdig1, uV); case TPS65910_UNIT_VDIG2: return tps65910_regulator_set_value(dev, &ldo_props_vdig2, uV); case TPS65910_UNIT_VPLL: return tps65910_regulator_set_value(dev, &ldo_props_vpll, uV); case TPS65910_UNIT_VDAC: return tps65910_regulator_set_value(dev, &ldo_props_vdac, uV); case TPS65910_UNIT_VAUX1: return tps65910_regulator_set_value(dev, &ldo_props_vaux1, uV); case TPS65910_UNIT_VAUX2: return tps65910_regulator_set_value(dev, &ldo_props_vaux2, uV); case TPS65910_UNIT_VAUX33: return tps65910_regulator_set_value(dev, &ldo_props_vaux33, uV); case TPS65910_UNIT_VMMC: return tps65910_regulator_set_value(dev, &ldo_props_vmmc, uV); default: return 0; } } static int tps65910_get_enable(struct udevice *dev) { int reg, val; struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); reg = get_ctrl_reg_from_unit_addr(pdata->unit); if (reg < 0) return reg; val = pmic_reg_read(dev->parent, reg); if (val < 0) return val; /* bits 1:0 of regulator control register define state */ return ((val & TPS65910_SUPPLY_STATE_MASK) == 1); } static int tps65910_set_enable(struct udevice *dev, bool enable) { int reg; uint clr, set; struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); reg = get_ctrl_reg_from_unit_addr(pdata->unit); if (reg < 0) return reg; if (enable) { clr = TPS65910_SUPPLY_STATE_MASK & ~TPS65910_SUPPLY_STATE_ON; set = TPS65910_SUPPLY_STATE_MASK & TPS65910_SUPPLY_STATE_ON; } else { clr = TPS65910_SUPPLY_STATE_MASK & ~TPS65910_SUPPLY_STATE_OFF; set = TPS65910_SUPPLY_STATE_MASK & TPS65910_SUPPLY_STATE_OFF; } return pmic_clrsetbits(dev->parent, reg, clr, set); } static int buck_get_vdd1_vdd2_value(struct udevice *dev, int reg_vdd) { int gain; int val = pmic_reg_read(dev, reg_vdd); if (val < 0) return val; gain = (val & TPS65910_GAIN_SEL_MASK) >> 6; gain = (gain == 0) ? 1 : gain; val = pmic_reg_read(dev, reg_vdd + 1); if (val < 0) return val; if (val & TPS65910_VDD_SR_MASK) /* use smart reflex value instead */ val = pmic_reg_read(dev, reg_vdd + 2); if (val < 0) return val; return (562500 + (val & TPS65910_VDD_SEL_MASK) * 12500) * gain; } static int tps65910_buck_get_value(struct udevice *dev) { struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); switch (pdata->unit) { case TPS65910_UNIT_VIO: return tps65910_regulator_get_value(dev, &smps_props_vio); case TPS65910_UNIT_VDD1: return buck_get_vdd1_vdd2_value(dev->parent, TPS65910_REG_VDD1); case TPS65910_UNIT_VDD2: return buck_get_vdd1_vdd2_value(dev->parent, TPS65910_REG_VDD2); default: return 0; } } static int buck_set_vdd1_vdd2_value(struct udevice *dev, int uV) { int ret, reg_vdd, gain; int val; struct dm_regulator_uclass_plat *uc_pdata; struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); switch (pdata->unit) { case TPS65910_UNIT_VDD1: reg_vdd = TPS65910_REG_VDD1; break; case TPS65910_UNIT_VDD2: reg_vdd = TPS65910_REG_VDD2; break; default: return -EINVAL; } uc_pdata = dev_get_uclass_plat(dev); /* check setpoint is within limits */ if (uV < uc_pdata->min_uV) { pr_err("voltage %duV for %s too low\n", uV, dev->name); return -EINVAL; } if (uV > uc_pdata->max_uV) { pr_err("voltage %duV for %s too high\n", uV, dev->name); return -EINVAL; } val = pmic_reg_read(dev->parent, reg_vdd); if (val < 0) return val; gain = (val & TPS65910_GAIN_SEL_MASK) >> 6; gain = (gain == 0) ? 1 : gain; val = ((uV / gain) - 562500) / 12500; if (val < TPS65910_VDD_SEL_MIN || val > TPS65910_VDD_SEL_MAX) /* * Neither do we change the gain, nor do we allow shutdown or * any approximate value (for now) */ return -EPERM; val &= TPS65910_VDD_SEL_MASK; ret = pmic_reg_write(dev->parent, reg_vdd + 1, val); if (ret) return ret; return 0; } static int tps65910_buck_set_value(struct udevice *dev, int uV) { struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); if (pdata->unit == TPS65910_UNIT_VIO) return tps65910_regulator_set_value(dev, &smps_props_vio, uV); return buck_set_vdd1_vdd2_value(dev, uV); } static int tps65910_boost_get_value(struct udevice *dev) { int vout; struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); vout = (pdata->supply >= 3000000) ? 5000000 : 0; return vout; } static int tps65910_regulator_ofdata_to_platdata(struct udevice *dev) { struct udevice *supply; int ret; const char *supply_name; struct tps65910_regulator_pdata *pdata = dev_get_plat(dev); pdata->unit = dev_get_driver_data(dev); if (pdata->unit > TPS65910_UNIT_VMMC) return -EINVAL; supply_name = supply_names[regulator_supplies[pdata->unit]]; debug("Looking up supply power %s\n", supply_name); ret = device_get_supply_regulator(dev->parent, supply_name, &supply); if (ret) { debug(" missing supply power %s\n", supply_name); return ret; } pdata->supply = regulator_get_value(supply); if (pdata->supply < 0) { debug(" invalid supply voltage for regulator %s\n", supply->name); return -EINVAL; } return 0; } static const struct dm_regulator_ops tps65910_boost_ops = { .get_value = tps65910_boost_get_value, .get_enable = tps65910_get_enable, .set_enable = tps65910_set_enable, }; U_BOOT_DRIVER(tps65910_boost) = { .name = TPS65910_BOOST_DRIVER, .id = UCLASS_REGULATOR, .ops = &tps65910_boost_ops, .plat_auto = sizeof(struct tps65910_regulator_pdata), .ofdata_to_platdata = tps65910_regulator_ofdata_to_platdata, }; static const struct dm_regulator_ops tps65910_buck_ops = { .get_value = tps65910_buck_get_value, .set_value = tps65910_buck_set_value, .get_enable = tps65910_get_enable, .set_enable = tps65910_set_enable, }; U_BOOT_DRIVER(tps65910_buck) = { .name = TPS65910_BUCK_DRIVER, .id = UCLASS_REGULATOR, .ops = &tps65910_buck_ops, .plat_auto = sizeof(struct tps65910_regulator_pdata), .ofdata_to_platdata = tps65910_regulator_ofdata_to_platdata, }; static const struct dm_regulator_ops tps65910_ldo_ops = { .get_value = tps65910_ldo_get_value, .set_value = tps65910_ldo_set_value, .get_enable = tps65910_get_enable, .set_enable = tps65910_set_enable, }; U_BOOT_DRIVER(tps65910_ldo) = { .name = TPS65910_LDO_DRIVER, .id = UCLASS_REGULATOR, .ops = &tps65910_ldo_ops, .plat_auto = sizeof(struct tps65910_regulator_pdata), .ofdata_to_platdata = tps65910_regulator_ofdata_to_platdata, };