regulator: Add support for ramp delay
Changing voltage and enabling regulator might require delays so the regulator stabilizes at expected level. Add support for "regulator-ramp-delay" binding which can introduce required time to both enabling the regulator and to changing the voltage. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Tested-by: Anand Moon <linux.amoon@gmail.com> Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
This commit is contained in:
		
							parent
							
								
									fce8610060
								
							
						
					
					
						commit
						e66d1cb3c2
					
				|  | @ -35,6 +35,7 @@ Optional properties: | ||||||
| - regulator-max-microamp: a maximum allowed Current value | - regulator-max-microamp: a maximum allowed Current value | ||||||
| - regulator-always-on: regulator should never be disabled | - regulator-always-on: regulator should never be disabled | ||||||
| - regulator-boot-on: enabled by bootloader/firmware | - regulator-boot-on: enabled by bootloader/firmware | ||||||
|  | - regulator-ramp-delay: ramp delay for regulator (in uV/us) | ||||||
| 
 | 
 | ||||||
| Note | Note | ||||||
| The "regulator-name" constraint is used for setting the device's uclass | The "regulator-name" constraint is used for setting the device's uclass | ||||||
|  | @ -60,4 +61,5 @@ ldo0 { | ||||||
| 	regulator-max-microamp = <100000>; | 	regulator-max-microamp = <100000>; | ||||||
| 	regulator-always-on; | 	regulator-always-on; | ||||||
| 	regulator-boot-on; | 	regulator-boot-on; | ||||||
|  | 	regulator-ramp-delay = <12000>; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -35,10 +35,22 @@ int regulator_get_value(struct udevice *dev) | ||||||
| 	return ops->get_value(dev); | 	return ops->get_value(dev); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void regulator_set_value_ramp_delay(struct udevice *dev, int old_uV, | ||||||
|  | 					   int new_uV, unsigned int ramp_delay) | ||||||
|  | { | ||||||
|  | 	int delay = DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay); | ||||||
|  | 
 | ||||||
|  | 	debug("regulator %s: delay %u us (%d uV -> %d uV)\n", dev->name, delay, | ||||||
|  | 	      old_uV, new_uV); | ||||||
|  | 
 | ||||||
|  | 	udelay(delay); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int regulator_set_value(struct udevice *dev, int uV) | int regulator_set_value(struct udevice *dev, int uV) | ||||||
| { | { | ||||||
| 	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | 	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | ||||||
| 	struct dm_regulator_uclass_platdata *uc_pdata; | 	struct dm_regulator_uclass_platdata *uc_pdata; | ||||||
|  | 	int ret, old_uV = uV, is_enabled = 0; | ||||||
| 
 | 
 | ||||||
| 	uc_pdata = dev_get_uclass_platdata(dev); | 	uc_pdata = dev_get_uclass_platdata(dev); | ||||||
| 	if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV) | 	if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV) | ||||||
|  | @ -49,7 +61,20 @@ int regulator_set_value(struct udevice *dev, int uV) | ||||||
| 	if (!ops || !ops->set_value) | 	if (!ops || !ops->set_value) | ||||||
| 		return -ENOSYS; | 		return -ENOSYS; | ||||||
| 
 | 
 | ||||||
| 	return ops->set_value(dev, uV); | 	if (uc_pdata->ramp_delay) { | ||||||
|  | 		is_enabled = regulator_get_enable(dev); | ||||||
|  | 		old_uV = regulator_get_value(dev); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ret = ops->set_value(dev, uV); | ||||||
|  | 
 | ||||||
|  | 	if (!ret) { | ||||||
|  | 		if (uc_pdata->ramp_delay && old_uV > 0 && is_enabled) | ||||||
|  | 			regulator_set_value_ramp_delay(dev, old_uV, uV, | ||||||
|  | 						       uc_pdata->ramp_delay); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -107,6 +132,7 @@ int regulator_set_enable(struct udevice *dev, bool enable) | ||||||
| { | { | ||||||
| 	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | 	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | ||||||
| 	struct dm_regulator_uclass_platdata *uc_pdata; | 	struct dm_regulator_uclass_platdata *uc_pdata; | ||||||
|  | 	int ret, old_enable = 0; | ||||||
| 
 | 
 | ||||||
| 	if (!ops || !ops->set_enable) | 	if (!ops || !ops->set_enable) | ||||||
| 		return -ENOSYS; | 		return -ENOSYS; | ||||||
|  | @ -115,7 +141,22 @@ int regulator_set_enable(struct udevice *dev, bool enable) | ||||||
| 	if (!enable && uc_pdata->always_on) | 	if (!enable && uc_pdata->always_on) | ||||||
| 		return -EACCES; | 		return -EACCES; | ||||||
| 
 | 
 | ||||||
| 	return ops->set_enable(dev, enable); | 	if (uc_pdata->ramp_delay) | ||||||
|  | 		old_enable = regulator_get_enable(dev); | ||||||
|  | 
 | ||||||
|  | 	ret = ops->set_enable(dev, enable); | ||||||
|  | 	if (!ret) { | ||||||
|  | 		if (uc_pdata->ramp_delay && !old_enable && enable) { | ||||||
|  | 			int uV = regulator_get_value(dev); | ||||||
|  | 
 | ||||||
|  | 			if (uV > 0) { | ||||||
|  | 				regulator_set_value_ramp_delay(dev, 0, uV, | ||||||
|  | 							       uc_pdata->ramp_delay); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int regulator_set_enable_if_allowed(struct udevice *dev, bool enable) | int regulator_set_enable_if_allowed(struct udevice *dev, bool enable) | ||||||
|  | @ -335,6 +376,8 @@ static int regulator_pre_probe(struct udevice *dev) | ||||||
| 						-ENODATA); | 						-ENODATA); | ||||||
| 	uc_pdata->always_on = dev_read_bool(dev, "regulator-always-on"); | 	uc_pdata->always_on = dev_read_bool(dev, "regulator-always-on"); | ||||||
| 	uc_pdata->boot_on = dev_read_bool(dev, "regulator-boot-on"); | 	uc_pdata->boot_on = dev_read_bool(dev, "regulator-boot-on"); | ||||||
|  | 	uc_pdata->ramp_delay = dev_read_u32_default(dev, "regulator-ramp-delay", | ||||||
|  | 						    0); | ||||||
| 
 | 
 | ||||||
| 	/* Those values are optional (-ENODATA if unset) */ | 	/* Those values are optional (-ENODATA if unset) */ | ||||||
| 	if ((uc_pdata->min_uV != -ENODATA) && | 	if ((uc_pdata->min_uV != -ENODATA) && | ||||||
|  |  | ||||||
|  | @ -150,6 +150,7 @@ enum regulator_flag { | ||||||
|  * @always_on* - bool type, true or false |  * @always_on* - bool type, true or false | ||||||
|  * @boot_on*   - bool type, true or false |  * @boot_on*   - bool type, true or false | ||||||
|  * TODO(sjg@chromium.org): Consider putting the above two into @flags |  * TODO(sjg@chromium.org): Consider putting the above two into @flags | ||||||
|  |  * @ramp_delay - Time to settle down after voltage change (unit: uV/us) | ||||||
|  * @flags:     - flags value (see REGULATOR_FLAG_...) |  * @flags:     - flags value (see REGULATOR_FLAG_...) | ||||||
|  * @name**     - fdt regulator name - should be taken from the device tree |  * @name**     - fdt regulator name - should be taken from the device tree | ||||||
|  * ctrl_reg:   - Control register offset used to enable/disable regulator |  * ctrl_reg:   - Control register offset used to enable/disable regulator | ||||||
|  | @ -169,6 +170,7 @@ struct dm_regulator_uclass_platdata { | ||||||
| 	int max_uV; | 	int max_uV; | ||||||
| 	int min_uA; | 	int min_uA; | ||||||
| 	int max_uA; | 	int max_uA; | ||||||
|  | 	unsigned int ramp_delay; | ||||||
| 	bool always_on; | 	bool always_on; | ||||||
| 	bool boot_on; | 	bool boot_on; | ||||||
| 	const char *name; | 	const char *name; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue