diff options
Diffstat (limited to 'drivers/regulator/s2mpa01.c')
| -rw-r--r-- | drivers/regulator/s2mpa01.c | 482 | 
1 files changed, 482 insertions, 0 deletions
diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c new file mode 100644 index 00000000000..ee83b487642 --- /dev/null +++ b/drivers/regulator/s2mpa01.c @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd + *		http://www.samsung.com + * + *  This program is free software; you can redistribute  it and/or modify it + *  under  the terms of  the GNU General  Public License as published by the + *  Free Software Foundation;  either version 2 of the  License, or (at your + *  option) any later version. + * + */ + +#include <linux/bug.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/mfd/samsung/core.h> +#include <linux/mfd/samsung/s2mpa01.h> + +#define S2MPA01_REGULATOR_CNT ARRAY_SIZE(regulators) + +struct s2mpa01_info { +	int ramp_delay24; +	int ramp_delay3; +	int ramp_delay5; +	int ramp_delay16; +	int ramp_delay7; +	int ramp_delay8910; +}; + +static int get_ramp_delay(int ramp_delay) +{ +	unsigned char cnt = 0; + +	ramp_delay /= 6250; + +	while (true) { +		ramp_delay = ramp_delay >> 1; +		if (ramp_delay == 0) +			break; +		cnt++; +	} + +	if (cnt > 3) +		cnt = 3; + +	return cnt; +} + +static int s2mpa01_regulator_set_voltage_time_sel(struct regulator_dev *rdev, +				   unsigned int old_selector, +				   unsigned int new_selector) +{ +	struct s2mpa01_info *s2mpa01 = rdev_get_drvdata(rdev); +	unsigned int ramp_delay = 0; +	int old_volt, new_volt; + +	switch (rdev_get_id(rdev)) { +	case S2MPA01_BUCK2: +	case S2MPA01_BUCK4: +		ramp_delay = s2mpa01->ramp_delay24; +		break; +	case S2MPA01_BUCK3: +		ramp_delay = s2mpa01->ramp_delay3; +		break; +	case S2MPA01_BUCK5: +		ramp_delay = s2mpa01->ramp_delay5; +		break; +	case S2MPA01_BUCK1: +	case S2MPA01_BUCK6: +		ramp_delay = s2mpa01->ramp_delay16; +		break; +	case S2MPA01_BUCK7: +		ramp_delay = s2mpa01->ramp_delay7; +		break; +	case S2MPA01_BUCK8: +	case S2MPA01_BUCK9: +	case S2MPA01_BUCK10: +		ramp_delay = s2mpa01->ramp_delay8910; +		break; +	} + +	if (ramp_delay == 0) +		ramp_delay = rdev->desc->ramp_delay; + +	old_volt = rdev->desc->min_uV + (rdev->desc->uV_step * old_selector); +	new_volt = rdev->desc->min_uV + (rdev->desc->uV_step * new_selector); + +	return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay); +} + +static int s2mpa01_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ +	struct s2mpa01_info *s2mpa01 = rdev_get_drvdata(rdev); +	unsigned int ramp_val, ramp_shift, ramp_reg = S2MPA01_REG_RAMP2; +	unsigned int ramp_enable = 1, enable_shift = 0; +	int ret; + +	switch (rdev_get_id(rdev)) { +	case S2MPA01_BUCK1: +		enable_shift = S2MPA01_BUCK1_RAMP_EN_SHIFT; +		if (!ramp_delay) { +			ramp_enable = 0; +			break; +		} + +		if (ramp_delay > s2mpa01->ramp_delay16) +			s2mpa01->ramp_delay16 = ramp_delay; +		else +			ramp_delay = s2mpa01->ramp_delay16; + +		ramp_shift = S2MPA01_BUCK16_RAMP_SHIFT; +		break; +	case S2MPA01_BUCK2: +		enable_shift = S2MPA01_BUCK2_RAMP_EN_SHIFT; +		if (!ramp_delay) { +			ramp_enable = 0; +			break; +		} + +		if (ramp_delay > s2mpa01->ramp_delay24) +			s2mpa01->ramp_delay24 = ramp_delay; +		else +			ramp_delay = s2mpa01->ramp_delay24; + +		ramp_shift = S2MPA01_BUCK24_RAMP_SHIFT; +		ramp_reg = S2MPA01_REG_RAMP1; +		break; +	case S2MPA01_BUCK3: +		enable_shift = S2MPA01_BUCK3_RAMP_EN_SHIFT; +		if (!ramp_delay) { +			ramp_enable = 0; +			break; +		} + +		s2mpa01->ramp_delay3 = ramp_delay; +		ramp_shift = S2MPA01_BUCK3_RAMP_SHIFT; +		ramp_reg = S2MPA01_REG_RAMP1; +		break; +	case S2MPA01_BUCK4: +		enable_shift = S2MPA01_BUCK4_RAMP_EN_SHIFT; +		if (!ramp_delay) { +			ramp_enable = 0; +			break; +		} + +		if (ramp_delay > s2mpa01->ramp_delay24) +			s2mpa01->ramp_delay24 = ramp_delay; +		else +			ramp_delay = s2mpa01->ramp_delay24; + +		ramp_shift = S2MPA01_BUCK24_RAMP_SHIFT; +		ramp_reg = S2MPA01_REG_RAMP1; +		break; +	case S2MPA01_BUCK5: +		s2mpa01->ramp_delay5 = ramp_delay; +		ramp_shift = S2MPA01_BUCK5_RAMP_SHIFT; +		break; +	case S2MPA01_BUCK6: +		if (ramp_delay > s2mpa01->ramp_delay16) +			s2mpa01->ramp_delay16 = ramp_delay; +		else +			ramp_delay = s2mpa01->ramp_delay16; + +		ramp_shift = S2MPA01_BUCK16_RAMP_SHIFT; +		break; +	case S2MPA01_BUCK7: +		s2mpa01->ramp_delay7 = ramp_delay; +		ramp_shift = S2MPA01_BUCK7_RAMP_SHIFT; +		break; +	case S2MPA01_BUCK8: +	case S2MPA01_BUCK9: +	case S2MPA01_BUCK10: +		if (ramp_delay > s2mpa01->ramp_delay8910) +			s2mpa01->ramp_delay8910 = ramp_delay; +		else +			ramp_delay = s2mpa01->ramp_delay8910; + +		ramp_shift = S2MPA01_BUCK8910_RAMP_SHIFT; +		break; +	default: +		return 0; +	} + +	if (!ramp_enable) +		goto ramp_disable; + +	/* Ramp delay can be enabled/disabled only for buck[1234] */ +	if (rdev_get_id(rdev) >= S2MPA01_BUCK1 && +			rdev_get_id(rdev) <= S2MPA01_BUCK4) { +		ret = regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1, +					 1 << enable_shift, 1 << enable_shift); +		if (ret) { +			dev_err(&rdev->dev, "failed to enable ramp rate\n"); +			return ret; +		} +	} + +	ramp_val = get_ramp_delay(ramp_delay); + +	return regmap_update_bits(rdev->regmap, ramp_reg, 0x3 << ramp_shift, +				  ramp_val << ramp_shift); + +ramp_disable: +	return regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1, +				  1 << enable_shift, 0); +} + +static struct regulator_ops s2mpa01_ldo_ops = { +	.list_voltage		= regulator_list_voltage_linear, +	.map_voltage		= regulator_map_voltage_linear, +	.is_enabled		= regulator_is_enabled_regmap, +	.enable			= regulator_enable_regmap, +	.disable		= regulator_disable_regmap, +	.get_voltage_sel	= regulator_get_voltage_sel_regmap, +	.set_voltage_sel	= regulator_set_voltage_sel_regmap, +	.set_voltage_time_sel	= regulator_set_voltage_time_sel, +}; + +static struct regulator_ops s2mpa01_buck_ops = { +	.list_voltage		= regulator_list_voltage_linear, +	.map_voltage		= regulator_map_voltage_linear, +	.is_enabled		= regulator_is_enabled_regmap, +	.enable			= regulator_enable_regmap, +	.disable		= regulator_disable_regmap, +	.get_voltage_sel	= regulator_get_voltage_sel_regmap, +	.set_voltage_sel	= regulator_set_voltage_sel_regmap, +	.set_voltage_time_sel	= s2mpa01_regulator_set_voltage_time_sel, +	.set_ramp_delay		= s2mpa01_set_ramp_delay, +}; + +#define regulator_desc_ldo1(num)	{		\ +	.name		= "LDO"#num,			\ +	.id		= S2MPA01_LDO##num,		\ +	.ops		= &s2mpa01_ldo_ops,		\ +	.type		= REGULATOR_VOLTAGE,		\ +	.owner		= THIS_MODULE,			\ +	.min_uV		= S2MPA01_LDO_MIN,		\ +	.uV_step	= S2MPA01_LDO_STEP1,		\ +	.n_voltages	= S2MPA01_LDO_N_VOLTAGES,	\ +	.vsel_reg	= S2MPA01_REG_L1CTRL + num - 1,	\ +	.vsel_mask	= S2MPA01_LDO_VSEL_MASK,	\ +	.enable_reg	= S2MPA01_REG_L1CTRL + num - 1,	\ +	.enable_mask	= S2MPA01_ENABLE_MASK		\ +} +#define regulator_desc_ldo2(num)	{		\ +	.name		= "LDO"#num,			\ +	.id		= S2MPA01_LDO##num,		\ +	.ops		= &s2mpa01_ldo_ops,		\ +	.type		= REGULATOR_VOLTAGE,		\ +	.owner		= THIS_MODULE,			\ +	.min_uV		= S2MPA01_LDO_MIN,		\ +	.uV_step	= S2MPA01_LDO_STEP2,		\ +	.n_voltages	= S2MPA01_LDO_N_VOLTAGES,	\ +	.vsel_reg	= S2MPA01_REG_L1CTRL + num - 1,	\ +	.vsel_mask	= S2MPA01_LDO_VSEL_MASK,	\ +	.enable_reg	= S2MPA01_REG_L1CTRL + num - 1,	\ +	.enable_mask	= S2MPA01_ENABLE_MASK		\ +} + +#define regulator_desc_buck1_4(num)	{			\ +	.name		= "BUCK"#num,				\ +	.id		= S2MPA01_BUCK##num,			\ +	.ops		= &s2mpa01_buck_ops,			\ +	.type		= REGULATOR_VOLTAGE,			\ +	.owner		= THIS_MODULE,				\ +	.min_uV		= S2MPA01_BUCK_MIN1,			\ +	.uV_step	= S2MPA01_BUCK_STEP1,			\ +	.n_voltages	= S2MPA01_BUCK_N_VOLTAGES,		\ +	.ramp_delay	= S2MPA01_RAMP_DELAY,			\ +	.vsel_reg	= S2MPA01_REG_B1CTRL2 + (num - 1) * 2,	\ +	.vsel_mask	= S2MPA01_BUCK_VSEL_MASK,		\ +	.enable_reg	= S2MPA01_REG_B1CTRL1 + (num - 1) * 2,	\ +	.enable_mask	= S2MPA01_ENABLE_MASK			\ +} + +#define regulator_desc_buck5	{				\ +	.name		= "BUCK5",				\ +	.id		= S2MPA01_BUCK5,			\ +	.ops		= &s2mpa01_buck_ops,			\ +	.type		= REGULATOR_VOLTAGE,			\ +	.owner		= THIS_MODULE,				\ +	.min_uV		= S2MPA01_BUCK_MIN2,			\ +	.uV_step	= S2MPA01_BUCK_STEP1,			\ +	.n_voltages	= S2MPA01_BUCK_N_VOLTAGES,		\ +	.ramp_delay	= S2MPA01_RAMP_DELAY,			\ +	.vsel_reg	= S2MPA01_REG_B5CTRL2,			\ +	.vsel_mask	= S2MPA01_BUCK_VSEL_MASK,		\ +	.enable_reg	= S2MPA01_REG_B5CTRL1,			\ +	.enable_mask	= S2MPA01_ENABLE_MASK			\ +} + +#define regulator_desc_buck6_7(num)	{			\ +	.name		= "BUCK"#num,				\ +	.id		= S2MPA01_BUCK##num,			\ +	.ops		= &s2mpa01_buck_ops,			\ +	.type		= REGULATOR_VOLTAGE,			\ +	.owner		= THIS_MODULE,				\ +	.min_uV		= S2MPA01_BUCK_MIN1,			\ +	.uV_step	= S2MPA01_BUCK_STEP1,			\ +	.n_voltages	= S2MPA01_BUCK_N_VOLTAGES,		\ +	.ramp_delay	= S2MPA01_RAMP_DELAY,			\ +	.vsel_reg	= S2MPA01_REG_B6CTRL2 + (num - 6) * 2,	\ +	.vsel_mask	= S2MPA01_BUCK_VSEL_MASK,		\ +	.enable_reg	= S2MPA01_REG_B6CTRL1 + (num - 6) * 2,	\ +	.enable_mask	= S2MPA01_ENABLE_MASK			\ +} + +#define regulator_desc_buck8	{				\ +	.name		= "BUCK8",				\ +	.id		= S2MPA01_BUCK8,			\ +	.ops		= &s2mpa01_buck_ops,			\ +	.type		= REGULATOR_VOLTAGE,			\ +	.owner		= THIS_MODULE,				\ +	.min_uV		= S2MPA01_BUCK_MIN2,			\ +	.uV_step	= S2MPA01_BUCK_STEP2,			\ +	.n_voltages	= S2MPA01_BUCK_N_VOLTAGES,		\ +	.ramp_delay	= S2MPA01_RAMP_DELAY,			\ +	.vsel_reg	= S2MPA01_REG_B8CTRL2,			\ +	.vsel_mask	= S2MPA01_BUCK_VSEL_MASK,		\ +	.enable_reg	= S2MPA01_REG_B8CTRL1,			\ +	.enable_mask	= S2MPA01_ENABLE_MASK			\ +} + +#define regulator_desc_buck9	{				\ +	.name		= "BUCK9",				\ +	.id		= S2MPA01_BUCK9,			\ +	.ops		= &s2mpa01_buck_ops,			\ +	.type		= REGULATOR_VOLTAGE,			\ +	.owner		= THIS_MODULE,				\ +	.min_uV		= S2MPA01_BUCK_MIN4,			\ +	.uV_step	= S2MPA01_BUCK_STEP2,			\ +	.n_voltages	= S2MPA01_BUCK_N_VOLTAGES,		\ +	.ramp_delay	= S2MPA01_RAMP_DELAY,			\ +	.vsel_reg	= S2MPA01_REG_B9CTRL2,			\ +	.vsel_mask	= S2MPA01_BUCK_VSEL_MASK,		\ +	.enable_reg	= S2MPA01_REG_B9CTRL1,			\ +	.enable_mask	= S2MPA01_ENABLE_MASK			\ +} + +#define regulator_desc_buck10	{				\ +	.name		= "BUCK10",				\ +	.id		= S2MPA01_BUCK10,			\ +	.ops		= &s2mpa01_buck_ops,			\ +	.type		= REGULATOR_VOLTAGE,			\ +	.owner		= THIS_MODULE,				\ +	.min_uV		= S2MPA01_BUCK_MIN3,			\ +	.uV_step	= S2MPA01_BUCK_STEP2,			\ +	.n_voltages	= S2MPA01_BUCK_N_VOLTAGES,		\ +	.ramp_delay	= S2MPA01_RAMP_DELAY,			\ +	.vsel_reg	= S2MPA01_REG_B10CTRL2,			\ +	.vsel_mask	= S2MPA01_BUCK_VSEL_MASK,		\ +	.enable_reg	= S2MPA01_REG_B10CTRL1,			\ +	.enable_mask	= S2MPA01_ENABLE_MASK			\ +} + +static struct regulator_desc regulators[] = { +	regulator_desc_ldo2(1), +	regulator_desc_ldo1(2), +	regulator_desc_ldo1(3), +	regulator_desc_ldo1(4), +	regulator_desc_ldo1(5), +	regulator_desc_ldo2(6), +	regulator_desc_ldo1(7), +	regulator_desc_ldo1(8), +	regulator_desc_ldo1(9), +	regulator_desc_ldo1(10), +	regulator_desc_ldo2(11), +	regulator_desc_ldo1(12), +	regulator_desc_ldo1(13), +	regulator_desc_ldo1(14), +	regulator_desc_ldo1(15), +	regulator_desc_ldo1(16), +	regulator_desc_ldo1(17), +	regulator_desc_ldo1(18), +	regulator_desc_ldo1(19), +	regulator_desc_ldo1(20), +	regulator_desc_ldo1(21), +	regulator_desc_ldo2(22), +	regulator_desc_ldo2(23), +	regulator_desc_ldo1(24), +	regulator_desc_ldo1(25), +	regulator_desc_ldo1(26), +	regulator_desc_buck1_4(1), +	regulator_desc_buck1_4(2), +	regulator_desc_buck1_4(3), +	regulator_desc_buck1_4(4), +	regulator_desc_buck5, +	regulator_desc_buck6_7(6), +	regulator_desc_buck6_7(7), +	regulator_desc_buck8, +	regulator_desc_buck9, +	regulator_desc_buck10, +}; + +static int s2mpa01_pmic_probe(struct platform_device *pdev) +{ +	struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); +	struct sec_platform_data *pdata = dev_get_platdata(iodev->dev); +	struct of_regulator_match rdata[S2MPA01_REGULATOR_MAX]; +	struct device_node *reg_np = NULL; +	struct regulator_config config = { }; +	struct s2mpa01_info *s2mpa01; +	int i; + +	s2mpa01 = devm_kzalloc(&pdev->dev, sizeof(*s2mpa01), GFP_KERNEL); +	if (!s2mpa01) +		return -ENOMEM; + +	for (i = 0; i < S2MPA01_REGULATOR_CNT; i++) +		rdata[i].name = regulators[i].name; + +	if (iodev->dev->of_node) { +		reg_np = of_get_child_by_name(iodev->dev->of_node, +							"regulators"); +			if (!reg_np) { +				dev_err(&pdev->dev, +					"could not find regulators sub-node\n"); +				return -EINVAL; +			} + +		of_regulator_match(&pdev->dev, reg_np, rdata, +						S2MPA01_REGULATOR_MAX); +		of_node_put(reg_np); +	} + +	platform_set_drvdata(pdev, s2mpa01); + +	config.dev = &pdev->dev; +	config.regmap = iodev->regmap_pmic; +	config.driver_data = s2mpa01; + +	for (i = 0; i < S2MPA01_REGULATOR_MAX; i++) { +		struct regulator_dev *rdev; +		if (pdata) +			config.init_data = pdata->regulators[i].initdata; +		else +			config.init_data = rdata[i].init_data; + +		if (reg_np) +			config.of_node = rdata[i].of_node; + +		rdev = devm_regulator_register(&pdev->dev, +						®ulators[i], &config); +		if (IS_ERR(rdev)) { +			dev_err(&pdev->dev, "regulator init failed for %d\n", +				i); +			return PTR_ERR(rdev); +		} +	} + +	return 0; +} + +static const struct platform_device_id s2mpa01_pmic_id[] = { +	{ "s2mpa01-pmic", 0}, +	{ }, +}; +MODULE_DEVICE_TABLE(platform, s2mpa01_pmic_id); + +static struct platform_driver s2mpa01_pmic_driver = { +	.driver = { +		.name = "s2mpa01-pmic", +		.owner = THIS_MODULE, +	}, +	.probe = s2mpa01_pmic_probe, +	.id_table = s2mpa01_pmic_id, +}; + +module_platform_driver(s2mpa01_pmic_driver); + +/* Module information */ +MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); +MODULE_AUTHOR("Sachin Kamat <sachin.kamat@samsung.com>"); +MODULE_DESCRIPTION("SAMSUNG S2MPA01 Regulator Driver"); +MODULE_LICENSE("GPL");  | 
