diff options
Diffstat (limited to 'drivers/regulator/tps62360-regulator.c')
| -rw-r--r-- | drivers/regulator/tps62360-regulator.c | 380 | 
1 files changed, 249 insertions, 131 deletions
diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index e2ec73068ee..e534269ed44 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -1,7 +1,7 @@  /*   * tps62360.c -- TI tps62360   * - * Driver for processor core supply tps62360 and tps62361B + * Driver for processor core supply tps62360, tps62361B, tps62362 and tps62363.   *   * Copyright (c) 2012, NVIDIA Corporation.   * @@ -26,13 +26,16 @@  #include <linux/module.h>  #include <linux/init.h>  #include <linux/err.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/regulator/of_regulator.h>  #include <linux/platform_device.h>  #include <linux/regulator/driver.h>  #include <linux/regulator/machine.h>  #include <linux/regulator/tps62360.h>  #include <linux/gpio.h>  #include <linux/i2c.h> -#include <linux/delay.h>  #include <linux/slab.h>  #include <linux/regmap.h> @@ -46,20 +49,20 @@  #define REG_RAMPCTRL		6  #define REG_CHIPID		8 -enum chips {TPS62360, TPS62361}; +#define FORCE_PWM_ENABLE	BIT(7) -#define TPS62360_BASE_VOLTAGE	770 +enum chips {TPS62360, TPS62361, TPS62362, TPS62363}; + +#define TPS62360_BASE_VOLTAGE	770000  #define TPS62360_N_VOLTAGES	64 -#define TPS62361_BASE_VOLTAGE	500 +#define TPS62361_BASE_VOLTAGE	500000  #define TPS62361_N_VOLTAGES	128  /* tps 62360 chip information */  struct tps62360_chip { -	const char *name;  	struct device *dev;  	struct regulator_desc desc; -	struct i2c_client *client;  	struct regulator_dev *rdev;  	struct regmap *regmap;  	int chip_id; @@ -68,12 +71,12 @@ struct tps62360_chip {  	int voltage_base;  	u8 voltage_reg_mask;  	bool en_internal_pulldn; -	bool en_force_pwm;  	bool en_discharge;  	bool valid_gpios;  	int lru_index[4];  	int curr_vset_vsel[4];  	int curr_vset_id; +	int change_uv_per_us;  };  /* @@ -99,6 +102,7 @@ static bool find_voltage_set_register(struct tps62360_chip *tps,  	bool found = false;  	int new_vset_reg = tps->lru_index[3];  	int found_index = 3; +  	for (i = 0; i < 4; ++i) {  		if (tps->curr_vset_vsel[tps->lru_index[i]] == req_vsel) {  			new_vset_reg = tps->lru_index[i]; @@ -117,7 +121,7 @@ update_lru_index:  	return found;  } -static int tps62360_dcdc_get_voltage(struct regulator_dev *dev) +static int tps62360_dcdc_get_voltage_sel(struct regulator_dev *dev)  {  	struct tps62360_chip *tps = rdev_get_drvdata(dev);  	int vsel; @@ -126,196 +130,312 @@ static int tps62360_dcdc_get_voltage(struct regulator_dev *dev)  	ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data);  	if (ret < 0) { -		dev_err(tps->dev, "%s: Error in reading register %d\n", -			__func__, REG_VSET0 + tps->curr_vset_id); +		dev_err(tps->dev, "%s(): register %d read failed with err %d\n", +			__func__, REG_VSET0 + tps->curr_vset_id, ret);  		return ret;  	}  	vsel = (int)data & tps->voltage_reg_mask; -	return (tps->voltage_base + vsel * 10) * 1000; +	return vsel;  } -static int tps62360_dcdc_set_voltage(struct regulator_dev *dev, -	     int min_uV, int max_uV, unsigned *selector) +static int tps62360_dcdc_set_voltage_sel(struct regulator_dev *dev, +					 unsigned selector)  {  	struct tps62360_chip *tps = rdev_get_drvdata(dev); -	int vsel;  	int ret;  	bool found = false;  	int new_vset_id = tps->curr_vset_id; -	if (max_uV < min_uV) -		return -EINVAL; - -	if (min_uV > -		((tps->voltage_base + (tps->desc.n_voltages - 1) * 10) * 1000)) -		return -EINVAL; - -	if (max_uV < tps->voltage_base * 1000) -		return -EINVAL; - -	vsel = DIV_ROUND_UP(min_uV - (tps->voltage_base * 1000), 10000); -	if (selector) -		*selector = (vsel & tps->voltage_reg_mask); -  	/*  	 * If gpios are available to select the VSET register then least  	 * recently used register for new configuration.  	 */  	if (tps->valid_gpios) -		found = find_voltage_set_register(tps, vsel, &new_vset_id); +		found = find_voltage_set_register(tps, selector, &new_vset_id);  	if (!found) {  		ret = regmap_update_bits(tps->regmap, REG_VSET0 + new_vset_id, -				tps->voltage_reg_mask, vsel); +				tps->voltage_reg_mask, selector);  		if (ret < 0) { -			dev_err(tps->dev, "%s: Error in updating register %d\n", -				 __func__, REG_VSET0 + new_vset_id); +			dev_err(tps->dev, +				"%s(): register %d update failed with err %d\n", +				 __func__, REG_VSET0 + new_vset_id, ret);  			return ret;  		}  		tps->curr_vset_id = new_vset_id; -		tps->curr_vset_vsel[new_vset_id] = vsel; +		tps->curr_vset_vsel[new_vset_id] = selector;  	}  	/* Select proper VSET register vio gpios */  	if (tps->valid_gpios) { -		gpio_set_value_cansleep(tps->vsel0_gpio, -					new_vset_id & 0x1); +		gpio_set_value_cansleep(tps->vsel0_gpio, new_vset_id & 0x1);  		gpio_set_value_cansleep(tps->vsel1_gpio,  					(new_vset_id >> 1) & 0x1);  	}  	return 0;  } -static int tps62360_dcdc_list_voltage(struct regulator_dev *dev, -					unsigned selector) +static int tps62360_set_voltage_time_sel(struct regulator_dev *rdev, +		unsigned int old_selector, unsigned int new_selector)  { -	struct tps62360_chip *tps = rdev_get_drvdata(dev); +	struct tps62360_chip *tps = rdev_get_drvdata(rdev); +	int old_uV, new_uV; -	if (selector >= tps->desc.n_voltages) -		return -EINVAL; -	return (tps->voltage_base + selector * 10) * 1000; +	old_uV = regulator_list_voltage_linear(rdev, old_selector); +	if (old_uV < 0) +		return old_uV; + +	new_uV = regulator_list_voltage_linear(rdev, new_selector); +	if (new_uV < 0) +		return new_uV; + +	return DIV_ROUND_UP(abs(old_uV - new_uV), tps->change_uv_per_us);  } -static struct regulator_ops tps62360_dcdc_ops = { -	.get_voltage = tps62360_dcdc_get_voltage, -	.set_voltage = tps62360_dcdc_set_voltage, -	.list_voltage = tps62360_dcdc_list_voltage, -}; +static int tps62360_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ +	struct tps62360_chip *tps = rdev_get_drvdata(rdev); +	int i; +	int val; +	int ret; + +	/* Enable force PWM mode in FAST mode only. */ +	switch (mode) { +	case REGULATOR_MODE_FAST: +		val = FORCE_PWM_ENABLE; +		break; + +	case REGULATOR_MODE_NORMAL: +		val = 0; +		break; + +	default: +		return -EINVAL; +	} + +	if (!tps->valid_gpios) { +		ret = regmap_update_bits(tps->regmap, +			REG_VSET0 + tps->curr_vset_id, FORCE_PWM_ENABLE, val); +		if (ret < 0) +			dev_err(tps->dev, +				"%s(): register %d update failed with err %d\n", +				__func__, REG_VSET0 + tps->curr_vset_id, ret); +		return ret; +	} -static int tps62360_init_force_pwm(struct tps62360_chip *tps, -	struct tps62360_regulator_platform_data *pdata, -	int vset_id) +	/* If gpios are valid then all register set need to be control */ +	for (i = 0; i < 4; ++i) { +		ret = regmap_update_bits(tps->regmap, +					REG_VSET0 + i, FORCE_PWM_ENABLE, val); +		if (ret < 0) { +			dev_err(tps->dev, +				"%s(): register %d update failed with err %d\n", +				__func__, REG_VSET0 + i, ret); +			return ret; +		} +	} +	return ret; +} + +static unsigned int tps62360_get_mode(struct regulator_dev *rdev)  { +	struct tps62360_chip *tps = rdev_get_drvdata(rdev);  	unsigned int data;  	int ret; -	ret = regmap_read(tps->regmap, REG_VSET0 + vset_id, &data); + +	ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data);  	if (ret < 0) { -		dev_err(tps->dev, "%s() fails in writing reg %d\n", -			__func__, REG_VSET0 + vset_id); +		dev_err(tps->dev, "%s(): register %d read failed with err %d\n", +			__func__, REG_VSET0 + tps->curr_vset_id, ret);  		return ret;  	} -	tps->curr_vset_vsel[vset_id] = data & tps->voltage_reg_mask; -	if (pdata->en_force_pwm) -		data |= BIT(7); -	else -		data &= ~BIT(7); -	ret = regmap_write(tps->regmap, REG_VSET0 + vset_id, data); -	if (ret < 0) -		dev_err(tps->dev, "%s() fails in writing reg %d\n", -				__func__, REG_VSET0 + vset_id); -	return ret; +	return (data & FORCE_PWM_ENABLE) ? +				REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;  } -static int tps62360_init_dcdc(struct tps62360_chip *tps, +static struct regulator_ops tps62360_dcdc_ops = { +	.get_voltage_sel	= tps62360_dcdc_get_voltage_sel, +	.set_voltage_sel	= tps62360_dcdc_set_voltage_sel, +	.list_voltage		= regulator_list_voltage_linear, +	.map_voltage		= regulator_map_voltage_linear, +	.set_voltage_time_sel	= tps62360_set_voltage_time_sel, +	.set_mode		= tps62360_set_mode, +	.get_mode		= tps62360_get_mode, +}; + +static int __devinit tps62360_init_dcdc(struct tps62360_chip *tps,  		struct tps62360_regulator_platform_data *pdata)  {  	int ret; -	int i; +	unsigned int ramp_ctrl; -	/* Initailize internal pull up/down control */ +	/* Initialize internal pull up/down control */  	if (tps->en_internal_pulldn)  		ret = regmap_write(tps->regmap, REG_CONTROL, 0xE0);  	else  		ret = regmap_write(tps->regmap, REG_CONTROL, 0x0);  	if (ret < 0) { -		dev_err(tps->dev, "%s() fails in writing reg %d\n", -			__func__, REG_CONTROL); +		dev_err(tps->dev, +			"%s(): register %d write failed with err %d\n", +			__func__, REG_CONTROL, ret);  		return ret;  	} -	/* Initailize force PWM mode */ -	if (tps->valid_gpios) { -		for (i = 0; i < 4; ++i) { -			ret = tps62360_init_force_pwm(tps, pdata, i); -			if (ret < 0) -				return ret; -		} -	} else { -		ret = tps62360_init_force_pwm(tps, pdata, tps->curr_vset_id); -		if (ret < 0) -			return ret; -	} -  	/* Reset output discharge path to reduce power consumption */  	ret = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), 0); -	if (ret < 0) -		dev_err(tps->dev, "%s() fails in updating reg %d\n", -			__func__, REG_RAMPCTRL); +	if (ret < 0) { +		dev_err(tps->dev, +			"%s(): register %d update failed with err %d\n", +			__func__, REG_RAMPCTRL, ret); +		return ret; +	} + +	/* Get ramp value from ramp control register */ +	ret = regmap_read(tps->regmap, REG_RAMPCTRL, &ramp_ctrl); +	if (ret < 0) { +		dev_err(tps->dev, +			"%s(): register %d read failed with err %d\n", +			__func__, REG_RAMPCTRL, ret); +		return ret; +	} +	ramp_ctrl = (ramp_ctrl >> 4) & 0x7; + +	/* ramp mV/us = 32/(2^ramp_ctrl) */ +	tps->change_uv_per_us = DIV_ROUND_UP(32000, BIT(ramp_ctrl));  	return ret;  }  static const struct regmap_config tps62360_regmap_config = { -	.reg_bits = 8, -	.val_bits = 8, +	.reg_bits		= 8, +	.val_bits		= 8, +	.max_register		= REG_CHIPID, +	.cache_type		= REGCACHE_RBTREE,  }; +static struct tps62360_regulator_platform_data * +	of_get_tps62360_platform_data(struct device *dev) +{ +	struct tps62360_regulator_platform_data *pdata; +	struct device_node *np = dev->of_node; + +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) { +		dev_err(dev, "Memory alloc failed for platform data\n"); +		return NULL; +	} + +	pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node); +	if (!pdata->reg_init_data) { +		dev_err(dev, "Not able to get OF regulator init data\n"); +		return NULL; +	} + +	pdata->vsel0_gpio = of_get_named_gpio(np, "vsel0-gpio", 0); +	pdata->vsel1_gpio = of_get_named_gpio(np, "vsel1-gpio", 0); + +	if (of_find_property(np, "ti,vsel0-state-high", NULL)) +		pdata->vsel0_def_state = 1; + +	if (of_find_property(np, "ti,vsel1-state-high", NULL)) +		pdata->vsel1_def_state = 1; + +	if (of_find_property(np, "ti,enable-pull-down", NULL)) +		pdata->en_internal_pulldn = true; + +	if (of_find_property(np, "ti,enable-vout-discharge", NULL)) +		pdata->en_discharge = true; + +	return pdata; +} + +#if defined(CONFIG_OF) +static const struct of_device_id tps62360_of_match[] = { +	 { .compatible = "ti,tps62360", .data = (void *)TPS62360}, +	 { .compatible = "ti,tps62361", .data = (void *)TPS62361}, +	 { .compatible = "ti,tps62362", .data = (void *)TPS62362}, +	 { .compatible = "ti,tps62363", .data = (void *)TPS62363}, +	{}, +}; +MODULE_DEVICE_TABLE(of, tps62360_of_match); +#endif +  static int __devinit tps62360_probe(struct i2c_client *client,  				     const struct i2c_device_id *id)  { +	struct regulator_config config = { };  	struct tps62360_regulator_platform_data *pdata;  	struct regulator_dev *rdev;  	struct tps62360_chip *tps;  	int ret;  	int i; +	int chip_id;  	pdata = client->dev.platform_data; +	chip_id = id->driver_data; + +	if (client->dev.of_node) { +		const struct of_device_id *match; +		match = of_match_device(of_match_ptr(tps62360_of_match), +				&client->dev); +		if (!match) { +			dev_err(&client->dev, "Error: No device match found\n"); +			return -ENODEV; +		} +		chip_id = (int)match->data; +		if (!pdata) +			pdata = of_get_tps62360_platform_data(&client->dev); +	} +  	if (!pdata) { -		dev_err(&client->dev, "%s() Err: Platform data not found\n", +		dev_err(&client->dev, "%s(): Platform data not found\n",  						__func__);  		return -EIO;  	}  	tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);  	if (!tps) { -		dev_err(&client->dev, "%s() Err: Memory allocation fails\n", +		dev_err(&client->dev, "%s(): Memory allocation failed\n",  						__func__);  		return -ENOMEM;  	} -	tps->en_force_pwm = pdata->en_force_pwm;  	tps->en_discharge = pdata->en_discharge;  	tps->en_internal_pulldn = pdata->en_internal_pulldn;  	tps->vsel0_gpio = pdata->vsel0_gpio;  	tps->vsel1_gpio = pdata->vsel1_gpio; -	tps->client = client;  	tps->dev = &client->dev; -	tps->name = id->name; -	tps->voltage_base = (id->driver_data == TPS62360) ? -				TPS62360_BASE_VOLTAGE : TPS62361_BASE_VOLTAGE; -	tps->voltage_reg_mask = (id->driver_data == TPS62360) ? 0x3F : 0x7F; + +	switch (chip_id) { +	case TPS62360: +	case TPS62362: +		tps->voltage_base = TPS62360_BASE_VOLTAGE; +		tps->voltage_reg_mask = 0x3F; +		tps->desc.n_voltages = TPS62360_N_VOLTAGES; +		break; +	case TPS62361: +	case TPS62363: +		tps->voltage_base = TPS62361_BASE_VOLTAGE; +		tps->voltage_reg_mask = 0x7F; +		tps->desc.n_voltages = TPS62361_N_VOLTAGES; +		break; +	default: +		return -ENODEV; +	}  	tps->desc.name = id->name;  	tps->desc.id = 0; -	tps->desc.n_voltages = (id->driver_data == TPS62360) ? -				TPS62360_N_VOLTAGES : TPS62361_N_VOLTAGES;  	tps->desc.ops = &tps62360_dcdc_ops;  	tps->desc.type = REGULATOR_VOLTAGE;  	tps->desc.owner = THIS_MODULE; -	tps->regmap = regmap_init_i2c(client, &tps62360_regmap_config); +	tps->desc.min_uV = tps->voltage_base; +	tps->desc.uV_step = 10000; + +	tps->regmap = devm_regmap_init_i2c(client, &tps62360_regmap_config);  	if (IS_ERR(tps->regmap)) {  		ret = PTR_ERR(tps->regmap); -		dev_err(&client->dev, "%s() Err: Failed to allocate register" -			"map: %d\n", __func__, ret); +		dev_err(&client->dev, +			"%s(): regmap allocation failed with err %d\n", +			__func__, ret);  		return ret;  	}  	i2c_set_clientdata(client, tps); @@ -326,35 +446,26 @@ static int __devinit tps62360_probe(struct i2c_client *client,  	tps->valid_gpios = false;  	if (gpio_is_valid(tps->vsel0_gpio) && gpio_is_valid(tps->vsel1_gpio)) { -		ret = gpio_request(tps->vsel0_gpio, "tps62360-vsel0"); +		int gpio_flags; +		gpio_flags = (pdata->vsel0_def_state) ? +				GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; +		ret = gpio_request_one(tps->vsel0_gpio, +				gpio_flags, "tps62360-vsel0");  		if (ret) {  			dev_err(&client->dev, -				"Err: Could not obtain vsel0 GPIO %d: %d\n", -						tps->vsel0_gpio, ret); -			goto err_gpio0; -		} -		ret = gpio_direction_output(tps->vsel0_gpio, -					pdata->vsel0_def_state); -		if (ret) { -			dev_err(&client->dev, "Err: Could not set direction of" -				"vsel0 GPIO %d: %d\n", tps->vsel0_gpio, ret); -			gpio_free(tps->vsel0_gpio); +				"%s(): Could not obtain vsel0 GPIO %d: %d\n", +				__func__, tps->vsel0_gpio, ret);  			goto err_gpio0;  		} -		ret = gpio_request(tps->vsel1_gpio, "tps62360-vsel1"); +		gpio_flags = (pdata->vsel1_def_state) ? +				GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; +		ret = gpio_request_one(tps->vsel1_gpio, +				gpio_flags, "tps62360-vsel1");  		if (ret) {  			dev_err(&client->dev, -				"Err: Could not obtain vsel1 GPIO %d: %d\n", -						tps->vsel1_gpio, ret); -			goto err_gpio1; -		} -		ret = gpio_direction_output(tps->vsel1_gpio, -					pdata->vsel1_def_state); -		if (ret) { -			dev_err(&client->dev, "Err: Could not set direction of" -				"vsel1 GPIO %d: %d\n", tps->vsel1_gpio, ret); -			gpio_free(tps->vsel1_gpio); +				"%s(): Could not obtain vsel1 GPIO %d: %d\n", +				__func__, tps->vsel1_gpio, ret);  			goto err_gpio1;  		}  		tps->valid_gpios = true; @@ -371,17 +482,22 @@ static int __devinit tps62360_probe(struct i2c_client *client,  	ret = tps62360_init_dcdc(tps, pdata);  	if (ret < 0) { -		dev_err(tps->dev, "%s() Err: Init fails with = %d\n", +		dev_err(tps->dev, "%s(): Init failed with err = %d\n",  				__func__, ret);  		goto err_init;  	} +	config.dev = &client->dev; +	config.init_data = pdata->reg_init_data; +	config.driver_data = tps; +	config.of_node = client->dev.of_node; +  	/* Register the regulators */ -	rdev = regulator_register(&tps->desc, &client->dev, -				&pdata->reg_init_data, tps, NULL); +	rdev = regulator_register(&tps->desc, &config);  	if (IS_ERR(rdev)) { -		dev_err(tps->dev, "%s() Err: Failed to register %s\n", -				__func__, id->name); +		dev_err(tps->dev, +			"%s(): regulator register failed with err %s\n", +			__func__, id->name);  		ret = PTR_ERR(rdev);  		goto err_init;  	} @@ -396,7 +512,6 @@ err_gpio1:  	if (gpio_is_valid(tps->vsel0_gpio))  		gpio_free(tps->vsel0_gpio);  err_gpio0: -	regmap_exit(tps->regmap);  	return ret;  } @@ -417,7 +532,6 @@ static int __devexit tps62360_remove(struct i2c_client *client)  		gpio_free(tps->vsel0_gpio);  	regulator_unregister(tps->rdev); -	regmap_exit(tps->regmap);  	return 0;  } @@ -432,13 +546,16 @@ static void tps62360_shutdown(struct i2c_client *client)  	/* Configure the output discharge path */  	st = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), BIT(2));  	if (st < 0) -		dev_err(tps->dev, "%s() fails in updating reg %d\n", -			__func__, REG_RAMPCTRL); +		dev_err(tps->dev, +			"%s(): register %d update failed with err %d\n", +			__func__, REG_RAMPCTRL, st);  }  static const struct i2c_device_id tps62360_id[] = {  	{.name = "tps62360", .driver_data = TPS62360},  	{.name = "tps62361", .driver_data = TPS62361}, +	{.name = "tps62362", .driver_data = TPS62362}, +	{.name = "tps62363", .driver_data = TPS62363},  	{},  }; @@ -448,6 +565,7 @@ static struct i2c_driver tps62360_i2c_driver = {  	.driver = {  		.name = "tps62360",  		.owner = THIS_MODULE, +		.of_match_table = of_match_ptr(tps62360_of_match),  	},  	.probe = tps62360_probe,  	.remove = __devexit_p(tps62360_remove), @@ -468,5 +586,5 @@ static void __exit tps62360_cleanup(void)  module_exit(tps62360_cleanup);  MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); -MODULE_DESCRIPTION("TPS62360 voltage regulator driver"); +MODULE_DESCRIPTION("TPS6236x voltage regulator driver");  MODULE_LICENSE("GPL v2");  |