All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sangbeom Kim <sbkim73@samsung.com>
To: "'Liam Girdwood'" <lrg@ti.com>,
	"'Mark Brown'" <broonie@opensource.wolfsonmicro.com>
Cc: linux-kernel@vger.kernel.org, "'Sangbeom Kim'" <sbkim73@samsung.com>
Subject: [PATCH] regulator: Add S5M8767 regulator driver
Date: Sun, 23 Oct 2011 17:12:27 +0900	[thread overview]
Message-ID: <009501cc915b$809ce880$81d6b980$@com> (raw)

S5M8767 is a cost-effective PMIC which is designed for mobile applications.
It includes high efficient 8 Buck converters, 27 LDOs, and an RTC.
This patch implement regulator driver for S5M8767.

Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 drivers/regulator/Kconfig   |    8 +
 drivers/regulator/Makefile  |    1 +
 drivers/regulator/s5m8767.c |  852
+++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 861 insertions(+), 0 deletions(-)
 create mode 100644 drivers/regulator/s5m8767.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index c7fd2c0..9d35b8e 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -125,6 +125,14 @@ config REGULATOR_MAX8998
 	  via I2C bus. The provided regulator is suitable for S3C6410
 	  and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages.
 
+config REGULATOR_S5M8767
+	tristate "Samsung S5M8767 voltage regulator"
+	depends on MFD_S5M_CORE
+	help
+	  This driver controls a Samsung S5M8767 voltage output regulator
+	  via I2C bus. S5M8767 have 8 Bucks and 27 LDOs output and
+	  supports 8 DVS modes ranging from 0.65V to 2.225V by 6.25mV steps.
+
 config REGULATOR_TWL4030
 	bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC"
 	depends on TWL4030_CORE
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 040d5aa..478fc0f 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -45,5 +45,6 @@ obj-$(CONFIG_REGULATOR_AB8500)	+= ab8500.o
 obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
 obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
 obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
+obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
 
 ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c
new file mode 100644
index 0000000..2c4c10c
--- /dev/null
+++ b/drivers/regulator/s5m8767.c
@@ -0,0 +1,852 @@
+/*
+ * s5m8767.c
+ *
+ * Copyright (c) 2011 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/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/s5m87xx/s5m-pmic.h>
+#include <linux/mfd/s5m87xx/s5m-core.h>
+
+struct s5m8767_info {
+	struct device *dev;
+	struct s5m87xx_dev *iodev;
+	int num_regulators;
+	struct regulator_dev **rdev;
+	int ramp_delay;
+	bool buck2_ramp;
+	bool buck3_ramp;
+	bool buck4_ramp;
+
+	bool buck2_gpiodvs;
+	bool buck3_gpiodvs;
+	bool buck4_gpiodvs;
+	u8 buck2_vol[8];
+	u8 buck3_vol[8];
+	u8 buck4_vol[8];
+	int buck_gpios[3];
+	int buck_gpioindex;
+};
+
+struct s5m_voltage_desc {
+	int max;
+	int min;
+	int step;
+};
+
+static const struct s5m_voltage_desc buck_voltage_val = {
+	.max = 2225000,
+	.min = 650000,
+	.step = 6250,
+};
+
+static const struct s5m_voltage_desc ldo_voltage_val = {
+	.max = 3950000,
+	.min = 800000,
+	.step = 50000,
+};
+
+static const struct s5m_voltage_desc *reg_voltage_map[] = {
+	[S5M8767_LDO1] = &ldo_voltage_val,
+	[S5M8767_LDO2] = &ldo_voltage_val,
+	[S5M8767_LDO3] = &ldo_voltage_val,
+	[S5M8767_LDO4] = &ldo_voltage_val,
+	[S5M8767_LDO5] = &ldo_voltage_val,
+	[S5M8767_LDO6] = &ldo_voltage_val,
+	[S5M8767_LDO7] = &ldo_voltage_val,
+	[S5M8767_LDO8] = &ldo_voltage_val,
+	[S5M8767_LDO9] = &ldo_voltage_val,
+	[S5M8767_LDO10] = &ldo_voltage_val,
+	[S5M8767_LDO11] = &ldo_voltage_val,
+	[S5M8767_LDO12] = &ldo_voltage_val,
+	[S5M8767_LDO13] = &ldo_voltage_val,
+	[S5M8767_LDO14] = &ldo_voltage_val,
+	[S5M8767_LDO15] = &ldo_voltage_val,
+	[S5M8767_LDO16] = &ldo_voltage_val,
+	[S5M8767_LDO17] = &ldo_voltage_val,
+	[S5M8767_LDO18] = &ldo_voltage_val,
+	[S5M8767_LDO19] = &ldo_voltage_val,
+	[S5M8767_LDO20] = &ldo_voltage_val,
+	[S5M8767_LDO21] = &ldo_voltage_val,
+	[S5M8767_LDO22] = &ldo_voltage_val,
+	[S5M8767_LDO23] = &ldo_voltage_val,
+	[S5M8767_LDO24] = &ldo_voltage_val,
+	[S5M8767_LDO25] = &ldo_voltage_val,
+	[S5M8767_LDO26] = &ldo_voltage_val,
+	[S5M8767_LDO27] = &ldo_voltage_val,
+	[S5M8767_BUCK1] = &buck_voltage_val,
+	[S5M8767_BUCK2] = &buck_voltage_val,
+	[S5M8767_BUCK3] = &buck_voltage_val,
+	[S5M8767_BUCK4] = &buck_voltage_val,
+	[S5M8767_BUCK5] = &buck_voltage_val,
+	[S5M8767_BUCK6] = &buck_voltage_val,
+	[S5M8767_BUCK7] = NULL,
+	[S5M8767_BUCK8] = NULL,
+};
+
+static inline int s5m8767_get_regid(struct regulator_dev *rdev)
+{
+	return rdev_get_id(rdev);
+}
+
+static inline int s5m8767_is_buck(struct regulator_dev *rdev)
+{
+	int reg_id = s5m8767_get_regid(rdev);
+
+	return reg_id < S5M8767_LDO27;
+}
+
+static int s5m8767_list_voltage(struct regulator_dev *rdev,
+				unsigned int selector)
+{
+	const struct s5m_voltage_desc *desc;
+	int reg_id = s5m8767_get_regid(rdev);
+	int val;
+
+	if (reg_id >= ARRAY_SIZE(reg_voltage_map) || reg_id < 0)
+		return -EINVAL;
+
+	desc = reg_voltage_map[reg_id];
+	if (desc == NULL)
+		return -EINVAL;
+
+	val = desc->min + desc->step * selector;
+	if (val > desc->max)
+		return -EINVAL;
+
+	return val;
+}
+
+static int s5m8767_get_register(struct regulator_dev *rdev, int *reg)
+{
+	int reg_id = s5m8767_get_regid(rdev);
+
+	switch (reg_id) {
+	case S5M8767_LDO1 ... S5M8767_LDO27:
+		*reg = S5M8767_REG_LDO1CTRL + (reg_id - S5M8767_LDO1);
+		break;
+	case S5M8767_BUCK1:
+		*reg = S5M8767_REG_BUCK1CTRL1;
+		break;
+	case S5M8767_BUCK2 ... S5M8767_BUCK4:
+		*reg = S5M8767_REG_BUCK2CTRL + (reg_id - S5M8767_BUCK2) * 9;
+		break;
+	case S5M8767_BUCK5 ... S5M8767_BUCK8:
+		*reg = S5M8767_REG_BUCK5CTRL1 + (reg_id - S5M8767_BUCK5) *
2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int s5m8767_reg_is_enabled(struct regulator_dev *rdev)
+{
+	struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+	int ret, reg;
+	int mask=0x01, pattern=0x01;
+	u8 val;
+	int reg_id = s5m8767_get_regid(rdev);
+
+	switch (reg_id) {
+	case S5M8767_LDO1 ... S5M8767_LDO27:
+		mask = 0xc0;
+		pattern = 0xc0;
+		break;
+	case S5M8767_BUCK1 ... S5M8767_BUCK8:
+		break;
+	}
+
+	ret = s5m8767_get_register(rdev, &reg);
+	if (ret == -EINVAL)
+		return 1;
+	else if (ret)
+		return ret;
+
+	ret = s5m_reg_read(s5m8767->iodev, reg, &val);
+	if (ret)
+		return ret;
+
+	return (val & mask) == pattern;
+}
+
+static int s5m8767_reg_enable(struct regulator_dev *rdev)
+{
+	struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+	int ret, reg;
+	int mask=0, pattern=0;
+	int reg_id = s5m8767_get_regid(rdev);
+
+	switch (reg_id) {
+	case S5M8767_LDO1 ... S5M8767_LDO27:
+		mask = 0xc0;
+		pattern = 0xc0;
+		break;
+	case S5M8767_BUCK1 ... S5M8767_BUCK8:
+		mask = 0x01;
+		pattern = 0x01;
+		break;
+	}
+
+	ret = s5m8767_get_register(rdev, &reg);
+	if (ret)
+		return ret;
+
+	return s5m_reg_update(s5m8767->iodev, reg, pattern, mask);
+}
+
+static int s5m8767_reg_disable(struct regulator_dev *rdev)
+{
+	struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+	int ret, reg;
+	int  mask=0, pattern=0;
+	int reg_id = s5m8767_get_regid(rdev);
+
+	switch (reg_id) {
+	case S5M8767_LDO1 ... S5M8767_LDO27:
+		mask = 0xc0;
+		pattern = 0xc0;
+		break;
+	case S5M8767_BUCK1 ... S5M8767_BUCK8:
+		mask = 0x01;
+		pattern = 0x01;
+		break;
+	}
+
+	ret = s5m8767_get_register(rdev, &reg);
+	if (ret)
+		return ret;
+
+	return s5m_reg_update(s5m8767->iodev, reg, ~pattern, mask);
+}
+
+static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int
*_reg)
+{
+	int reg_id = s5m8767_get_regid(rdev);
+	int reg;
+
+	switch (reg_id) {
+	case S5M8767_LDO1 ... S5M8767_LDO27:
+		reg = S5M8767_REG_LDO1CTRL + (reg_id - S5M8767_LDO1);
+		break;
+	case S5M8767_BUCK1:
+		reg = S5M8767_REG_BUCK1CTRL2;
+		break;
+	case S5M8767_BUCK2:
+		reg = S5M8767_REG_BUCK2DVS1;
+		break;
+	case S5M8767_BUCK3:
+		reg = S5M8767_REG_BUCK3DVS1;
+		break;
+	case S5M8767_BUCK4:
+		reg = S5M8767_REG_BUCK4DVS1;
+		break;
+	case S5M8767_BUCK5 ... S5M8767_BUCK8:
+		reg = S5M8767_REG_BUCK5CTRL2 + (reg_id - S5M8767_BUCK5) * 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*_reg = reg;
+
+	return 0;
+}
+
+static int s5m8767_get_voltage(struct regulator_dev *rdev)
+{
+	struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+	int reg, mask=0xff, ret;
+	int reg_id = s5m8767_get_regid(rdev);
+	u8 val;
+
+	ret = s5m8767_get_voltage_register(rdev, &reg);
+	if (ret)
+		return ret;
+
+	switch (reg_id) {
+	case S5M8767_LDO1 ... S5M8767_LDO27:
+		mask = 0x3f;
+		break;
+	case S5M8767_BUCK2:
+		if(s5m8767->buck2_gpiodvs)
+			reg += s5m8767->buck_gpioindex;
+		break;
+	case S5M8767_BUCK3:
+		if(s5m8767->buck3_gpiodvs)
+			reg += s5m8767->buck_gpioindex;
+		break;
+	case S5M8767_BUCK4:
+		if(s5m8767->buck4_gpiodvs)
+			reg += s5m8767->buck_gpioindex;
+		break;
+	}
+
+	ret = s5m_reg_read(s5m8767->iodev, reg, &val);
+	if (ret)
+		return ret;
+
+	val &= mask;
+
+	if (rdev->desc && rdev->desc->ops && rdev->desc->ops->list_voltage)
+		return rdev->desc->ops->list_voltage(rdev, val);
+
+	return s5m8767_list_voltage(rdev, val);
+}
+
+static inline int s5m8767_convert_voltage(
+		const struct s5m_voltage_desc *desc,
+		int min_vol, int max_vol)
+{
+	int i = 0;
+
+	if (desc == NULL)
+		return -EINVAL;
+
+	if (max_vol < desc->min || min_vol > desc->max)
+		return -EINVAL;
+
+	i = (min_vol - desc->min) / desc->step;
+
+	if (desc->min + desc->step * i > max_vol)
+		return -EINVAL;
+
+	return i;
+}
+
+static int s5m8767_set_voltage(struct regulator_dev *rdev,
+			       int min_uV, int max_uV, unsigned *selector)
+{
+	struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+	int min_vol = min_uV, max_vol = max_uV;
+	const struct s5m_voltage_desc *desc;
+	int reg_id = s5m8767_get_regid(rdev);
+	int reg, mask, ret;
+	int i;
+	u8 val;
+
+	switch (reg_id) {
+	case S5M8767_LDO1 ... S5M8767_LDO27:
+		mask = 0x3f;
+		break;
+	case S5M8767_BUCK1 ... S5M8767_BUCK6:
+		mask = 0xff;
+		break;
+	case S5M8767_BUCK7 ... S5M8767_BUCK8:
+		return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+
+	desc = reg_voltage_map[reg_id];
+
+	i = s5m8767_convert_voltage(desc, min_vol, max_vol);
+	if (i < 0)
+		return i;
+
+	ret = s5m8767_get_voltage_register(rdev, &reg);
+	if (ret)
+		return ret;
+
+	s5m_reg_read(s5m8767->iodev, reg, &val);
+	val = val & mask;
+
+	ret = s5m_reg_update(s5m8767->iodev, reg, i, mask);
+	*selector = i;
+
+	if (val < i)
+		udelay(DIV_ROUND_UP(desc->step * (i - val),
+			s5m8767->ramp_delay));
+
+	return ret;
+}
+
+static inline void s5m8767_set_high(struct s5m8767_info *s5m8767)
+{
+	int temp_index = s5m8767->buck_gpioindex;
+
+	gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1);
+	gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1);
+	gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1);
+}
+
+static inline void s5m8767_set_low(struct s5m8767_info *s5m8767)
+{
+	int temp_index = s5m8767->buck_gpioindex;
+
+	gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1);
+	gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1);
+	gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1);
+}
+
+static int s5m8767_set_voltage_buck(struct regulator_dev *rdev,
+				    int min_uV, int max_uV, unsigned
*selector)
+{
+	struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+	int reg_id = s5m8767_get_regid(rdev);
+	const struct s5m_voltage_desc *desc;
+	int new_val, old_val, i = 0;
+	int min_vol = min_uV, max_vol = max_uV;
+
+	if (reg_id < S5M8767_BUCK1 || reg_id > S5M8767_BUCK6)
+		return -EINVAL;
+
+	switch (reg_id) {
+	case S5M8767_BUCK1:
+		return s5m8767_set_voltage(rdev, min_uV, max_uV, selector);
+	case S5M8767_BUCK2 ... S5M8767_BUCK4:
+		break;
+	case S5M8767_BUCK5 ... S5M8767_BUCK6:
+		return s5m8767_set_voltage(rdev, min_uV, max_uV, selector);
+	}
+
+	desc = reg_voltage_map[reg_id];
+	new_val = s5m8767_convert_voltage(desc, min_vol, max_vol);
+	if (new_val < 0)
+		return new_val;
+
+	switch (reg_id) {
+	case S5M8767_BUCK2:
+		if(s5m8767->buck2_gpiodvs){
+			while (s5m8767->buck2_vol[i] != new_val)
+				i++;
+		}
+		else
+			return s5m8767_set_voltage(rdev, min_uV, max_uV,
selector);
+		break;
+	case S5M8767_BUCK3:
+		if(s5m8767->buck3_gpiodvs){
+			while (s5m8767->buck3_vol[i] != new_val)
+				i++;
+		} else
+			return s5m8767_set_voltage(rdev, min_uV, max_uV,
selector);
+		break;
+	case S5M8767_BUCK4:
+		if(s5m8767->buck3_gpiodvs){
+			while (s5m8767->buck4_vol[i] != new_val)
+				i++;
+		} else
+			return s5m8767_set_voltage(rdev, min_uV, max_uV,
selector);
+		break;
+	}
+
+	old_val = s5m8767->buck_gpioindex;
+	s5m8767->buck_gpioindex = i;
+
+	if ( i > old_val)
+		s5m8767_set_high(s5m8767);
+	else
+		s5m8767_set_low(s5m8767);
+
+	*selector = new_val;
+	return 0;
+}
+
+static int s5m8767_reg_enable_suspend(struct regulator_dev *rdev)
+{
+	return 0;
+}
+
+static int s5m8767_reg_disable_suspend(struct regulator_dev *rdev)
+{
+	struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+	int ret, reg;
+	int  mask=0, pattern=0;
+	int reg_id = s5m8767_get_regid(rdev);
+
+	switch (reg_id) {
+	case S5M8767_LDO1 ... S5M8767_LDO27:
+		mask = 0xc0;
+		pattern = 0xc0;
+		break;
+	case S5M8767_BUCK1 ... S5M8767_BUCK8:
+		mask = 0x01;
+		pattern = 0x01;
+		break;
+	}
+
+	ret = s5m8767_get_register(rdev, &reg);
+	if (ret)
+		return ret;
+
+	return s5m_reg_update(s5m8767->iodev, reg, ~pattern, mask);
+}
+
+static struct regulator_ops s5m8767_ldo_ops = {
+	.list_voltage		= s5m8767_list_voltage,
+	.is_enabled		= s5m8767_reg_is_enabled,
+	.enable			= s5m8767_reg_enable,
+	.disable		= s5m8767_reg_disable,
+	.get_voltage		= s5m8767_get_voltage,
+	.set_voltage		= s5m8767_set_voltage,
+	.set_suspend_enable	= s5m8767_reg_enable_suspend,
+	.set_suspend_disable	= s5m8767_reg_disable_suspend,
+};
+
+static struct regulator_ops s5m8767_buck_ops = {
+	.list_voltage		= s5m8767_list_voltage,
+	.is_enabled		= s5m8767_reg_is_enabled,
+	.enable			= s5m8767_reg_enable,
+	.disable		= s5m8767_reg_disable,
+	.get_voltage		= s5m8767_get_voltage,
+	.set_voltage		= s5m8767_set_voltage_buck,
+	.set_suspend_enable	= s5m8767_reg_enable_suspend,
+	.set_suspend_disable	= s5m8767_reg_disable_suspend,
+};
+
+static struct regulator_ops s5m8767_others_ops = {
+	.is_enabled		= s5m8767_reg_is_enabled,
+	.enable			= s5m8767_reg_enable,
+	.disable		= s5m8767_reg_disable,
+	.set_suspend_enable	= s5m8767_reg_enable_suspend,
+	.set_suspend_disable	= s5m8767_reg_disable_suspend,
+};
+
+#define regulator_desc_ldo(num)		{	\
+	.name		= "LDO"#num,		\
+	.id		= S5M8767_LDO##num,	\
+	.ops		= &s5m8767_ldo_ops,	\
+	.type		= REGULATOR_VOLTAGE,	\
+	.owner		= THIS_MODULE,		\
+}
+#define regulator_desc_buck(num)	{	\
+	.name		= "BUCK"#num,		\
+	.id		= S5M8767_BUCK##num,	\
+	.ops		= &s5m8767_buck_ops,	\
+	.type		= REGULATOR_VOLTAGE,	\
+	.owner		= THIS_MODULE,		\
+}
+
+static struct regulator_desc regulators[] = {
+	regulator_desc_ldo(1),
+	regulator_desc_ldo(2),
+	regulator_desc_ldo(3),
+	regulator_desc_ldo(4),
+	regulator_desc_ldo(5),
+	regulator_desc_ldo(6),
+	regulator_desc_ldo(7),
+	regulator_desc_ldo(8),
+	regulator_desc_ldo(9),
+	regulator_desc_ldo(10),
+	regulator_desc_ldo(11),
+	regulator_desc_ldo(12),
+	regulator_desc_ldo(13),
+	regulator_desc_ldo(14),
+	regulator_desc_ldo(15),
+	regulator_desc_ldo(16),
+	regulator_desc_ldo(17),
+	regulator_desc_ldo(18),
+	regulator_desc_ldo(19),
+	regulator_desc_ldo(20),
+	regulator_desc_ldo(21),
+	regulator_desc_ldo(22),
+	regulator_desc_ldo(23),
+	regulator_desc_ldo(24),
+	regulator_desc_ldo(25),
+	regulator_desc_ldo(26),
+	regulator_desc_ldo(27),
+	regulator_desc_buck(1),
+	regulator_desc_buck(2),
+	regulator_desc_buck(3),
+	regulator_desc_buck(4),
+	regulator_desc_buck(5),
+	regulator_desc_buck(6),
+	regulator_desc_buck(7),
+	regulator_desc_buck(8),
+	{
+		.name	= "AP 32KHz",
+		.id	= S5M8767_32KHZAP_EN,
+		.ops	= &s5m8767_others_ops,
+		.type	= REGULATOR_VOLTAGE,
+		.owner	= THIS_MODULE,
+	}, {
+		.name	= "CP 32KHz",
+		.id	= S5M8767_32KHZAP_EN,
+		.ops	= &s5m8767_others_ops,
+		.type	= REGULATOR_VOLTAGE,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
+{
+	struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct s5m87xx_platform_data *pdata = dev_get_platdata(iodev->dev);
+	struct regulator_dev **rdev;
+	struct s5m8767_info *s5m8767;
+	struct i2c_client *i2c;
+	int i, ret, size, reg;
+	u8 val;
+
+	if (!pdata) {
+		dev_err(pdev->dev.parent, "Platform data not supplied\n");
+		return -ENODEV;
+	}
+
+	s5m8767 = kzalloc(sizeof(struct s5m8767_info), GFP_KERNEL);
+	if (!s5m8767)
+		return -ENOMEM;
+
+	size = sizeof(struct regulator_dev *) * pdata->num_regulators;
+	s5m8767->rdev = kzalloc(size, GFP_KERNEL);
+	if (!s5m8767->rdev) {
+		kfree(s5m8767);
+		return -ENOMEM;
+	}
+
+	rdev = s5m8767->rdev;
+	s5m8767->dev = &pdev->dev;
+	s5m8767->iodev = iodev;
+	s5m8767->num_regulators = pdata->num_regulators;
+	platform_set_drvdata(pdev, s5m8767);
+	i2c = s5m8767->iodev->i2c;
+
+	s5m8767->buck_gpioindex = pdata->buck_default_idx;
+	s5m8767->buck2_gpiodvs = pdata->buck2_gpiodvs;
+	s5m8767->buck3_gpiodvs = pdata->buck3_gpiodvs;
+	s5m8767->buck4_gpiodvs = pdata->buck4_gpiodvs;
+	s5m8767->buck_gpios[0] = pdata->buck_gpios[0];
+	s5m8767->buck_gpios[1] = pdata->buck_gpios[1];
+	s5m8767->buck_gpios[2] = pdata->buck_gpios[2];
+	s5m8767->ramp_delay = pdata->buck_ramp_delay;
+	s5m8767->buck2_ramp = pdata->buck2_ramp_enable;
+	s5m8767->buck3_ramp = pdata->buck3_ramp_enable;
+	s5m8767->buck4_ramp = pdata->buck4_ramp_enable;
+
+	for (i = 0; i < 8; i++) {
+		if (s5m8767->buck2_gpiodvs) {
+			s5m8767->buck2_vol[i] =
+				s5m8767_convert_voltage(
+						&buck_voltage_val,
+						pdata->buck2_voltage[i],
+						pdata->buck2_voltage[i] +
+						buck_voltage_val.step);
+		}
+
+		if (s5m8767->buck3_gpiodvs) {
+			s5m8767->buck3_vol[i] =
+				s5m8767_convert_voltage(
+						&buck_voltage_val,
+						pdata->buck3_voltage[i],
+						pdata->buck3_voltage[i] +
+						buck_voltage_val.step);
+		}
+
+		if (s5m8767->buck4_gpiodvs) {
+			s5m8767->buck4_vol[i] =
+				s5m8767_convert_voltage(
+						&buck_voltage_val,
+						pdata->buck4_voltage[i],
+						pdata->buck4_voltage[i] +
+						buck_voltage_val.step);
+		}
+
+	}
+
+	if(pdata->buck2_gpiodvs || pdata->buck3_gpiodvs ||
+		pdata->buck4_gpiodvs) {
+		if (gpio_is_valid(pdata->buck_gpios[0]) &&
+		    gpio_is_valid(pdata->buck_gpios[1]) &&
+		    gpio_is_valid(pdata->buck_gpios[2])) {
+			ret = gpio_request(pdata->buck_gpios[0],
+						"S5M8767 SET1");
+			if (ret == -EBUSY)
+				dev_warn(&pdev->dev, "Duplicated gpio
request"
+					" for SET1\n");
+
+			ret = gpio_request(pdata->buck_gpios[1],
+						"S5M8767 SET2");
+			if (ret == -EBUSY)
+				dev_warn(&pdev->dev, "Duplicated gpio
request"
+					" for SET2\n");
+
+			ret = gpio_request(pdata->buck_gpios[2],
+						"S5M8767 SET3");
+			if (ret == -EBUSY)
+				dev_warn(&pdev->dev, "Duplicated gpio
request"
+						" for SET3\n");
+			/* SET1 GPIO */
+			gpio_direction_output(pdata->buck_gpios[0],
+					(s5m8767->buck_gpioindex >> 2) &
0x1);
+			/* SET2 GPIO */
+			gpio_direction_output(pdata->buck_gpios[1],
+					(s5m8767->buck_gpioindex >> 1) &
0x1);
+			/* SET3 GPIO */
+			gpio_direction_output(pdata->buck_gpios[2],
+					(s5m8767->buck_gpioindex >> 0) &
0x1);
+			ret = 0;
+		} else {
+			dev_err(&pdev->dev, "GPIO ISN'T VALID\n");
+			ret = -EINVAL;
+			goto err_alloc;
+		}
+	}
+
+	s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL,
(pdata->buck2_gpiodvs) ?
+			(1 << 1) : (0 << 1), 1 << 1);
+	s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL,
(pdata->buck3_gpiodvs) ?
+			(1 << 1) : (0 << 1), 1 << 1);
+	s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL,
(pdata->buck4_gpiodvs) ?
+			(1 << 1) : (0 << 1), 1 << 1);
+
+	/* Initialize GPIO DVS registers */
+	for (i = 0; i < 8; i++) {
+		if (s5m8767->buck2_gpiodvs) {
+			s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1
+ i,
+					s5m8767->buck2_vol[i]);
+		}
+
+		if (s5m8767->buck3_gpiodvs) {
+			s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1
+ i,
+					s5m8767->buck3_vol[i]);
+		}
+
+		if (s5m8767->buck4_gpiodvs) {
+			s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1
+ i,
+					s5m8767->buck4_vol[i]);
+		}
+	}
+
+	reg = S5M8767_REG_LDO7CTRL;
+
+	do{
+		ret = s5m_reg_read(s5m8767->iodev, reg, &val);
+		if (ret)
+			return ret;
+
+		s5m_reg_update(s5m8767->iodev, reg, ((val & 0x3f) | 0x80),
0xff);
+		reg++;
+		if ((reg == S5M8767_REG_LDO9CTRL)
+		    || (reg == S5M8767_REG_LDO11CTRL)
+		    || (reg == S5M8767_REG_LDO13CTRL)
+		    || (reg == S5M8767_REG_LDO17CTRL))
+			reg++;
+	} while (reg <= S5M8767_REG_LDO27CTRL);
+
+	if (s5m8767->buck2_ramp)
+		s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08,
0x08);
+
+	if (s5m8767->buck3_ramp)
+		s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04,
0x04);
+
+	if (s5m8767->buck4_ramp)
+		s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02,
0x02);
+
+	if (s5m8767->buck2_ramp || s5m8767->buck3_ramp
+	    || s5m8767->buck4_ramp) {
+		if (s5m8767->ramp_delay < 15)
+			s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+				       s5m8767->ramp_delay - 1, 0xf0);
+		else if (s5m8767->ramp_delay == 15)
+			s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+				       0xc0, 0xf0);
+		else if (s5m8767->ramp_delay == 25)
+			s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+				       0xd0, 0xf0);
+		else if (s5m8767->ramp_delay == 50)
+			s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+				       0xe0, 0xf0);
+		else if (s5m8767->ramp_delay == 100)
+			s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+				       0xf0, 0xf0);
+		else
+			s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+				       0x90, 0xf0);
+	}
+
+	for (i = 0; i < pdata->num_regulators; i++) {
+		const struct s5m_voltage_desc *desc;
+		int id = pdata->regulators[i].id;
+
+		desc = reg_voltage_map[id];
+		if (desc)
+			regulators[id].n_voltages =
+				(desc->max - desc->min) / desc->step + 1;
+
+		rdev[i] = regulator_register(&regulators[id], s5m8767->dev,
+					     pdata->regulators[i].initdata,
+					     s5m8767);
+		if (IS_ERR(rdev[i])) {
+			ret = PTR_ERR(rdev[i]);
+			dev_err(s5m8767->dev, "regulator init failed for
%d\n",
+				id);
+			rdev[i] = NULL;
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	for (i = 0; i < s5m8767->num_regulators; i++)
+		if (rdev[i])
+			regulator_unregister(rdev[i]);
+err_alloc:
+	kfree(s5m8767->rdev);
+	kfree(s5m8767);
+
+	return ret;
+}
+
+static int __devexit s5m8767_pmic_remove(struct platform_device *pdev)
+{
+	struct s5m8767_info *s5m8767 = platform_get_drvdata(pdev);
+	struct regulator_dev **rdev = s5m8767->rdev;
+	int i;
+
+	for (i = 0; i < s5m8767->num_regulators; i++)
+		if (rdev[i])
+			regulator_unregister(rdev[i]);
+
+	kfree(s5m8767->rdev);
+	kfree(s5m8767);
+
+	return 0;
+}
+
+static const struct platform_device_id s5m8767_pmic_id[] = {
+	{ "s5m8767-pmic", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, s5m8767_pmic_id);
+
+static struct platform_driver s5m8767_pmic_driver = {
+	.driver = {
+		.name = "s5m8767-pmic",
+		.owner = THIS_MODULE,
+	},
+	.probe = s5m8767_pmic_probe,
+	.remove = __devexit_p(s5m8767_pmic_remove),
+	.id_table = s5m8767_pmic_id,
+};
+
+static int __init s5m8767_pmic_init(void)
+{
+	return platform_driver_register(&s5m8767_pmic_driver);
+}
+subsys_initcall(s5m8767_pmic_init);
+
+static void __exit s5m8767_pmic_exit(void)
+{
+	platform_driver_unregister(&s5m8767_pmic_driver);
+}
+module_exit(s5m8767_pmic_exit);
+
+/* Module information */
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("SAMSUNG S5M8767 Regulator Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:s5m8767-pmic");
-- 
1.7.1



             reply	other threads:[~2011-10-23  8:12 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-10-23  8:12 Sangbeom Kim [this message]
2011-10-23  9:30 ` [PATCH] regulator: Add S5M8767 regulator driver Mark Brown
2011-10-23 10:59   ` Sangbeom Kim
  -- strict thread matches above, loose matches on Subject: below --
2011-10-21 10:45 Sangbeom Kim

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='009501cc915b$809ce880$81d6b980$@com' \
    --to=sbkim73@samsung.com \
    --cc=broonie@opensource.wolfsonmicro.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lrg@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.