[U-Boot] [PATCH v2 3/5] power: regulator: palmas: Add regulator support

Keerthy a0393675 at ti.com
Mon Sep 19 07:37:57 CEST 2016



On Monday 19 September 2016 09:21 AM, Keerthy wrote:
> The driver provides regulator set/get voltage
> enable/disable functions for palmas family of PMICs.
>
> Signed-off-by: Keerthy <j-keerthy at ti.com>
> Reviewed-by: Simon Glass <sjg at chromium.org>
> ---
>
> Changes in v2:
>
>   * Converted all dm_i2c_reg to dm_i2c_read_reg
>   * Removed an instance of double blank lines.
>   * Added Simon's Reviewed-by.
>
>  drivers/power/regulator/Kconfig            |   8 +
>  drivers/power/regulator/Makefile           |   1 +
>  drivers/power/regulator/palmas_regulator.c | 459 +++++++++++++++++++++++++++++
>  3 files changed, 468 insertions(+)
>  create mode 100644 drivers/power/regulator/palmas_regulator.c
>
> diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
> index 17f22dd..adb710a 100644
> --- a/drivers/power/regulator/Kconfig
> +++ b/drivers/power/regulator/Kconfig
> @@ -115,3 +115,11 @@ config REGULATOR_TPS65090
>  	regulators, one for each FET. The standard regulator interface is
>  	supported, but it is only possible to turn the regulators on or off.
>  	There is no voltage/current control.
> +
> +config DM_REGULATOR_PALMAS
> +	bool "Enable driver for PALMAS PMIC regulators"
> +       depends on PMIC_PALMAS
> +	---help---
> +	This enables implementation of driver-model regulator uclass
> +	features for REGULATOR PALMAS and the family of PALMAS PMICs.
> +	The driver implements get/set api for: value and enable.
> diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
> index 1590d85..75080d4 100644
> --- a/drivers/power/regulator/Makefile
> +++ b/drivers/power/regulator/Makefile
> @@ -14,3 +14,4 @@ obj-$(CONFIG_REGULATOR_RK808) += rk808.o
>  obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
>  obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o
>  obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o
> +obj-$(CONFIG_$(SPL_)DM_REGULATOR_PALMAS) += palmas_regulator.o
> diff --git a/drivers/power/regulator/palmas_regulator.c b/drivers/power/regulator/palmas_regulator.c
> new file mode 100644
> index 0000000..8212bf2
> --- /dev/null
> +++ b/drivers/power/regulator/palmas_regulator.c
> @@ -0,0 +1,459 @@
> +/*
> + * (C) Copyright 2016
> + * Texas Instruments Incorporated, <www.ti.com>
> + *
> + * Keerthy <j-keerthy at ti.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <fdtdec.h>
> +#include <errno.h>
> +#include <dm.h>
> +#include <i2c.h>
> +#include <power/pmic.h>
> +#include <power/regulator.h>
> +#include <power/palmas.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define	REGULATOR_ON		0x1
> +#define	REGULATOR_OFF		0x0
> +
> +#define	SMPS_MODE_MASK		0x3
> +#define	SMPS_MODE_SHIFT		0x0
> +#define	LDO_MODE_MASK		0x1
> +#define	LDO_MODE_SHIFT		0x0
> +
> +static const char palmas_smps_ctrl[][PALMAS_SMPS_NUM] = {
> +	{0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c},
> +	{0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38},
> +	{0x20, 0x24, 0x2c, 0x30, 0x38},
> +};
> +
> +static const char palmas_smps_volt[][PALMAS_SMPS_NUM] = {
> +	{0x23, 0x27, 0x2b, 0x2f, 0x33, 0x37, 0x3b, 0x3c},
> +	{0x23, 0x27, 0x2b, 0x2f, 0x33, 0x37, 0x3b},
> +	{0x23, 0x27, 0x2f, 0x33, 0x3B}
> +};
> +
> +static const char palmas_ldo_ctrl[][PALMAS_LDO_NUM] = {
> +	{0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64},
> +	{0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64},
> +	{0x50, 0x52, 0x54, 0x5e, 0x62}
> +};
> +
> +static const char palmas_ldo_volt[][PALMAS_LDO_NUM] = {
> +	{0x51, 0x53, 0x55, 0x57, 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x65},
> +	{0x51, 0x53, 0x55, 0x57, 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x65},
> +	{0x51, 0x53, 0x55, 0x5f, 0x63}
> +};
> +
> +static int palmas_smps_enable(struct udevice *dev, int op, bool *enable)
> +{
> +	int ret;
> +	uint8_t val;
> +	unsigned int adr;
> +	struct dm_regulator_uclass_platdata *uc_pdata;
> +
> +	uc_pdata = dev_get_uclass_platdata(dev);
> +	adr = uc_pdata->ctrl_reg;
> +
> +	val = dm_i2c_reg_read(dev->parent, adr);
> +		if (val < 0)
> +			return val;
> +
> +	if (op == PMIC_OP_GET) {
> +		val &= PALMAS_SMPS_STATUS_MASK;
> +
> +		if (val)
> +			*enable = true;
> +		else
> +			*enable = false;
> +
> +		return 0;
> +	} else if (op == PMIC_OP_SET) {
> +		if (*enable)
> +			val |= PALMAS_SMPS_MODE_MASK;
> +		else
> +			val &= ~(PALMAS_SMPS_MODE_MASK);
> +
> +		dm_i2c_write(dev->parent, adr, &val, 1);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int palmas_smps_volt2hex(int uV)
> +{
> +	if (uV > PALMAS_LDO_VOLT_MAX)
> +		return -EINVAL;
> +
> +	if (uV > 1650000)
> +		return (uV - 1000000) / 20000 + 0x6;
> +
> +	if (uV == 500000)
> +		return 0x6;
> +	else
> +		return 0x6 + ((uV - 500000) / 10000);
> +}
> +
> +static int palmas_smps_hex2volt(int hex, bool range)
> +{
> +	unsigned int uV = 0;
> +
> +	if (hex > PALMAS_SMPS_VOLT_MAX_HEX)
> +		return -EINVAL;
> +
> +	if (hex < 0x7)
> +		uV = 500000;
> +	else
> +		uV = 500000 + (hex - 0x6) * 10000;
> +
> +	if (range)
> +		uV *= 2;
> +
> +	return uV;
> +}
> +
> +static int palmas_smps_val(struct udevice *dev, int op, int *uV)
> +{
> +	unsigned int hex, adr;
> +	unsigned char val;
> +	int ret;
> +	bool range;
> +	struct dm_regulator_uclass_platdata *uc_pdata;
> +
> +	uc_pdata = dev_get_uclass_platdata(dev);
> +
> +	if (op == PMIC_OP_GET)
> +		*uV = 0;
> +
> +	adr = uc_pdata->volt_reg;
> +
> +	val = dm_i2c_reg_read(dev->parent, adr);

Simon,

I see a kind of issue in the current implementation of
int dm_i2c_reg_read(struct udevice *dev, uint offset)
int dm_i2c_reg_write(struct udevice *dev, uint offset, uint value)

In the current set up my need is to read and then write a 1 byte I2C 
register. It is best to have consistency with read and write.

In the current case i read an integer and now when i want to write i 
need to convert it to an unsigned integer.

Instead of all this why can we have:

int dm_i2c_reg_read(struct udevice *dev, uint offset, u8 *val)

returns error/success with the return value and value is passed by 
reference in val variable.

int dm_i2c_reg_write(struct udevice *dev, uint offset, u8 *val)
returns error/success using the return value and the value to be written 
is sent in val.

Please let me know your thoughts on this. I propose to drop this series 
as i will fix the above and send another version but before that i would 
need your inputs.

- Keerthy

> +	if (val < 0)
> +		return val;
> +
> +	if (op == PMIC_OP_GET) {
> +		if (val & PALMAS_SMPS_RANGE_MASK)
> +			range =  true;
> +		else
> +			range = false;
> +
> +		val &= PALMAS_SMPS_VOLT_MASK;
> +		ret = palmas_smps_hex2volt(val, range);
> +		if (ret < 0)
> +			return ret;
> +		*uV = ret;
> +
> +		return 0;
> +	}
> +
> +	hex = palmas_smps_volt2hex(*uV);
> +	if (hex < 0)
> +		return hex;
> +
> +	val &= ~PALMAS_SMPS_VOLT_MASK;
> +	val |= hex;
> +	if (*uV > 1650000)
> +		val |= PALMAS_SMPS_RANGE_MASK;
> +	ret = dm_i2c_write(dev->parent, adr, &val, 1);
> +
> +	return ret;
> +}
> +
> +static int palmas_ldo_enable(struct udevice *dev, int op, bool *enable)
> +{
> +	int ret;
> +	uint8_t val;
> +	unsigned int adr;
> +	struct dm_regulator_uclass_platdata *uc_pdata;
> +
> +	uc_pdata = dev_get_uclass_platdata(dev);
> +	adr = uc_pdata->ctrl_reg;
> +
> +	val = dm_i2c_reg_read(dev->parent, adr);
> +		if (val < 0)
> +			return val;
> +
> +	if (op == PMIC_OP_GET) {
> +		val &= PALMAS_LDO_STATUS_MASK;
> +
> +		if (val)
> +			*enable = true;
> +		else
> +			*enable = false;
> +
> +		return 0;
> +	} else if (op == PMIC_OP_SET) {
> +		if (*enable)
> +			val |= PALMAS_LDO_MODE_MASK;
> +		else
> +			val &= ~(PALMAS_LDO_MODE_MASK);
> +
> +		dm_i2c_write(dev->parent, adr, &val, 1);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int palmas_ldo_volt2hex(int uV)
> +{
> +	if (uV > PALMAS_LDO_VOLT_MAX)
> +		return -EINVAL;
> +
> +	return (uV - 850000) / 50000;
> +}
> +
> +static int palmas_ldo_hex2volt(int hex)
> +{
> +	if (hex > PALMAS_LDO_VOLT_MAX_HEX)
> +		return -EINVAL;
> +
> +	if (!hex)
> +		return 0;
> +
> +	return (hex * 50000) + 850000;
> +}
> +
> +static int palmas_ldo_val(struct udevice *dev, int op, int *uV)
> +{
> +	unsigned int hex, adr;
> +	unsigned char val;
> +	int ret;
> +
> +	struct dm_regulator_uclass_platdata *uc_pdata;
> +
> +	if (op == PMIC_OP_GET)
> +		*uV = 0;
> +
> +	uc_pdata = dev_get_uclass_platdata(dev);
> +
> +	adr = uc_pdata->volt_reg;
> +
> +	val = dm_i2c_reg_read(dev->parent, adr);
> +	if (val < 0)
> +		return val;
> +
> +	if (op == PMIC_OP_GET) {
> +		val &= PALMAS_LDO_VOLT_MASK;
> +		ret = palmas_ldo_hex2volt(val);
> +		if (ret < 0)
> +			return ret;
> +		*uV = ret;
> +		return 0;
> +	}
> +
> +	hex = palmas_ldo_volt2hex(*uV);
> +	if (hex < 0)
> +		return hex;
> +
> +	val &= ~PALMAS_LDO_VOLT_MASK;
> +	val |= hex;
> +	if (*uV > 1650000)
> +		val |= 0x80;
> +	ret = dm_i2c_write(dev->parent, adr, &val, 1);
> +
> +	return ret;
> +}
> +
> +static int palmas_ldo_probe(struct udevice *dev)
> +{
> +	struct dm_regulator_uclass_platdata *uc_pdata;
> +	struct udevice *parent;
> +
> +	uc_pdata = dev_get_uclass_platdata(dev);
> +
> +	parent = dev_get_parent(dev);
> +	int type = dev_get_driver_data(parent);
> +
> +	uc_pdata->type = REGULATOR_TYPE_LDO;
> +
> +	if (dev->driver_data) {
> +		u8 idx = dev->driver_data - 1;
> +		uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][idx];
> +		uc_pdata->volt_reg = palmas_ldo_volt[type][idx];
> +	} else {
> +		/* check for ldoln and ldousb cases */
> +		if (!strcmp("ldoln", dev->name)) {
> +			uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][9];
> +			uc_pdata->volt_reg = palmas_ldo_volt[type][9];
> +		} else if (!strcmp("ldousb", dev->name)) {
> +			uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][10];
> +			uc_pdata->volt_reg = palmas_ldo_volt[type][10];
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int ldo_get_value(struct udevice *dev)
> +{
> +	int uV;
> +	int ret;
> +
> +	ret = palmas_ldo_val(dev, PMIC_OP_GET, &uV);
> +	if (ret)
> +		return ret;
> +
> +	return uV;
> +}
> +
> +static int ldo_set_value(struct udevice *dev, int uV)
> +{
> +	return palmas_ldo_val(dev, PMIC_OP_SET, &uV);
> +}
> +
> +static bool ldo_get_enable(struct udevice *dev)
> +{
> +	bool enable = false;
> +	int ret;
> +
> +	ret = palmas_ldo_enable(dev, PMIC_OP_GET, &enable);
> +	if (ret)
> +		return ret;
> +
> +	return enable;
> +}
> +
> +static int ldo_set_enable(struct udevice *dev, bool enable)
> +{
> +	return palmas_ldo_enable(dev, PMIC_OP_SET, &enable);
> +}
> +
> +static int palmas_smps_probe(struct udevice *dev)
> +{
> +	struct dm_regulator_uclass_platdata *uc_pdata;
> +	struct udevice *parent;
> +	int idx;
> +
> +	uc_pdata = dev_get_uclass_platdata(dev);
> +
> +	parent = dev_get_parent(dev);
> +	int type = dev_get_driver_data(parent);
> +
> +	uc_pdata->type = REGULATOR_TYPE_BUCK;
> +
> +	switch (type) {
> +	case PALMAS:
> +	case TPS659038:
> +		switch (dev->driver_data) {
> +		case 123:
> +		case 12:
> +			uc_pdata->ctrl_reg = palmas_smps_ctrl[type][0];
> +			uc_pdata->volt_reg = palmas_smps_volt[type][0];
> +			break;
> +		case 3:
> +			uc_pdata->ctrl_reg = palmas_smps_ctrl[type][1];
> +			uc_pdata->volt_reg = palmas_smps_volt[type][1];
> +			break;
> +		case 45:
> +			uc_pdata->ctrl_reg = palmas_smps_ctrl[type][2];
> +			uc_pdata->volt_reg = palmas_smps_volt[type][2];
> +			break;
> +		case 6:
> +		case 7:
> +		case 8:
> +		case 9:
> +		case 10:
> +			idx = dev->driver_data - 4;
> +			uc_pdata->ctrl_reg = palmas_smps_ctrl[type][idx];
> +			uc_pdata->volt_reg = palmas_smps_volt[type][idx];
> +			break;
> +
> +		default:
> +			printf("Wrong ID for regulator\n");
> +		}
> +		break;
> +
> +	case TPS65917:
> +		switch (dev->driver_data) {
> +		case 1:
> +		case 2:
> +		case 3:
> +		case 4:
> +		case 5:
> +			idx = dev->driver_data - 1;
> +			uc_pdata->ctrl_reg = palmas_smps_ctrl[type][idx];
> +			uc_pdata->volt_reg = palmas_smps_volt[type][idx];
> +			break;
> +
> +		default:
> +			printf("Wrong ID for regulator\n");
> +		}
> +		break;
> +
> +	default:
> +			printf("Invalid PMIC ID\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int smps_get_value(struct udevice *dev)
> +{
> +	int uV;
> +	int ret;
> +
> +	ret = palmas_smps_val(dev, PMIC_OP_GET, &uV);
> +	if (ret)
> +		return ret;
> +
> +	return uV;
> +}
> +
> +static int smps_set_value(struct udevice *dev, int uV)
> +{
> +	return palmas_smps_val(dev, PMIC_OP_SET, &uV);
> +}
> +
> +static bool smps_get_enable(struct udevice *dev)
> +{
> +	bool enable = false;
> +	int ret;
> +
> +	ret = palmas_smps_enable(dev, PMIC_OP_GET, &enable);
> +	if (ret)
> +		return ret;
> +
> +	return enable;
> +}
> +
> +static int smps_set_enable(struct udevice *dev, bool enable)
> +{
> +	return palmas_smps_enable(dev, PMIC_OP_SET, &enable);
> +}
> +
> +static const struct dm_regulator_ops palmas_ldo_ops = {
> +	.get_value  = ldo_get_value,
> +	.set_value  = ldo_set_value,
> +	.get_enable = ldo_get_enable,
> +	.set_enable = ldo_set_enable,
> +};
> +
> +U_BOOT_DRIVER(palmas_ldo) = {
> +	.name = PALMAS_LDO_DRIVER,
> +	.id = UCLASS_REGULATOR,
> +	.ops = &palmas_ldo_ops,
> +	.probe = palmas_ldo_probe,
> +};
> +
> +static const struct dm_regulator_ops palmas_smps_ops = {
> +	.get_value  = smps_get_value,
> +	.set_value  = smps_set_value,
> +	.get_enable = smps_get_enable,
> +	.set_enable = smps_set_enable,
> +};
> +
> +U_BOOT_DRIVER(palmas_smps) = {
> +	.name = PALMAS_SMPS_DRIVER,
> +	.id = UCLASS_REGULATOR,
> +	.ops = &palmas_smps_ops,
> +	.probe = palmas_smps_probe,
> +};
>


More information about the U-Boot mailing list