| // SPDX-License-Identifier: GPL-2.0+ | 
 | /* | 
 |  * Copyright (C) EETS GmbH, 2017, Felix Brack <f.brack@eets.ch> | 
 |  */ | 
 |  | 
 | #include <common.h> | 
 | #include <dm.h> | 
 | #include <power/pmic.h> | 
 | #include <power/regulator.h> | 
 | #include <power/tps65910_pmic.h> | 
 |  | 
 | #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_platdata(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_platdata(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_platdata(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_platdata(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_platdata(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_platdata(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_platdata(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_platdata *uc_pdata; | 
 | 	struct tps65910_regulator_pdata *pdata = dev_get_platdata(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_platdata(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_platdata(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_platdata(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_platdata(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, | 
 | 	.platdata_auto_alloc_size = 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, | 
 | 	.platdata_auto_alloc_size = 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, | 
 | 	.platdata_auto_alloc_size = sizeof(struct tps65910_regulator_pdata), | 
 | 	.ofdata_to_platdata = tps65910_regulator_ofdata_to_platdata, | 
 | }; |