diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/regulator | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip |
samsung update 1
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 30 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 4 | ||||
-rw-r--r-- | drivers/regulator/max77686.c | 869 | ||||
-rw-r--r-- | drivers/regulator/max77693.c | 574 | ||||
-rw-r--r-- | drivers/regulator/max8649.c | 54 | ||||
-rw-r--r-- | drivers/regulator/max8698.c | 569 | ||||
-rw-r--r-- | drivers/regulator/max8997.c | 1845 | ||||
-rw-r--r-- | drivers/regulator/s5m8767.c | 1031 | ||||
-rw-r--r-- | drivers/regulator/wm8994-regulator.c | 46 |
9 files changed, 4189 insertions, 833 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index d7ed20f293d..02868c07d85 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -125,6 +125,36 @@ 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_MAX8698 + tristate "Maxim 8698 voltage regulator" + depends on MFD_MAX8698 + help + This driver controls a Maxim 8698 voltage output regulator + via I2C bus. The provided regulator is suitable for S3C6410 + and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages. + +config REGULATOR_MAX77686 + tristate "Maxim 77686 regulator" + depends on MFD_MAX77686 + help + This driver controls a Maxim 77686 regulator + via I2C bus. The provided regulator is suitable for + Exynos-4 chips to control VARM and VINT voltages. + +config REGULATOR_MAX77693 + tristate "Maxim 777693 regulator" + depends on MFD_MAX77693 + help + This driver control a Maxsim 77693 regulator. + +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 9 Bucks and 28 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 3932d2ec38f..7f7be11c91f 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -20,6 +20,9 @@ obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o +obj-$(CONFIG_REGULATOR_MAX8698) += max8698.o +obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o +obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o @@ -43,5 +46,6 @@ obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o +obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c new file mode 100644 index 00000000000..2e17b0c2eb1 --- /dev/null +++ b/drivers/regulator/max77686.c @@ -0,0 +1,869 @@ +/* + * max77686.c - Regulator driver for the Maxim 77686 + * + * Copyright (C) 2011 Samsung Electronics + * Chiwoong Byun <woong.byun@smasung.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997.c + */ + +#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/max77686.h> +#include <linux/mfd/max77686-private.h> + +#define PMIC_DEBUG KERN_DEBUG +#define PMIC_REG_DEBUG KERN_DEBUG + +#define MAX77686_OPMODE_SHIFT 6 +#define MAX77686_OPMODE_BUCK234_SHIFT 4 +#define MAX77686_OPMODE_MASK 0x3 + +#define MAX77686_DVS_VOL_COMP 50000 + +enum MAX77686_DEVICE_ID { + MAX77686_DEVICE_PASS1 = 0x1, + MAX77686_DEVICE_PASS2 = 0x2, +}; + +struct max77686_data { + struct device *dev; + struct max77686_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; + int ramp_delay; /* in mV/us */ + int device_id; + + struct max77686_opmode_data *opmode_data; + + bool buck2_gpiodvs; + bool buck3_gpiodvs; + bool buck4_gpiodvs; + u8 buck2_vol[8]; + u8 buck3_vol[8]; + u8 buck4_vol[8]; + int buck234_gpios_dvs[3]; + int buck234_gpios_selb[3]; + int buck234_gpioindex; + bool ignore_gpiodvs_side_effect; + + u8 saved_states[MAX77686_REG_MAX]; +}; + +static inline void max77686_set_gpio(struct max77686_data *max77686) +{ + int set3 = (max77686->buck234_gpioindex) & 0x1; + int set2 = ((max77686->buck234_gpioindex) >> 1) & 0x1; + int set1 = ((max77686->buck234_gpioindex) >> 2) & 0x1; + + if (max77686->buck234_gpios_dvs[0]) + gpio_set_value(max77686->buck234_gpios_dvs[0], set1); + if (max77686->buck234_gpios_dvs[1]) + gpio_set_value(max77686->buck234_gpios_dvs[1], set2); + if (max77686->buck234_gpios_dvs[2]) + gpio_set_value(max77686->buck234_gpios_dvs[2], set3); +} + +struct voltage_map_desc { + int min; + int max; + int step; + unsigned int n_bits; +}; + +/* LDO3 ~ 5, 9 ~ 14, 16 ~ 26 (uV) */ +static const struct voltage_map_desc ldo_voltage_map_desc = { + .min = 800000, .max = 3950000, .step = 50000, .n_bits = 6, +}; + +/* LDO1 ~ 2, 6 ~ 8, 15 (uV) */ +static const struct voltage_map_desc ldo_low_voltage_map_desc = { + .min = 800000, .max = 2375000, .step = 25000, .n_bits = 6, +}; + +/* Buck2, 3, 4 (uV) */ +static const struct voltage_map_desc buck_dvs_voltage_map_desc = { + .min = 600000, .max = 3787500, .step = 12500, .n_bits = 8, +}; + +/* Buck1, 5 ~ 9 (uV) */ +static const struct voltage_map_desc buck_voltage_map_desc = { + .min = 750000, .max = 3900000, .step = 50000, .n_bits = 6, +}; + +static const struct voltage_map_desc *reg_voltage_map[] = { + [MAX77686_LDO1] = &ldo_low_voltage_map_desc, + [MAX77686_LDO2] = &ldo_low_voltage_map_desc, + [MAX77686_LDO3] = &ldo_voltage_map_desc, + [MAX77686_LDO4] = &ldo_voltage_map_desc, + [MAX77686_LDO5] = &ldo_voltage_map_desc, + [MAX77686_LDO6] = &ldo_low_voltage_map_desc, + [MAX77686_LDO7] = &ldo_low_voltage_map_desc, + [MAX77686_LDO8] = &ldo_low_voltage_map_desc, + [MAX77686_LDO9] = &ldo_voltage_map_desc, + [MAX77686_LDO10] = &ldo_voltage_map_desc, + [MAX77686_LDO11] = &ldo_voltage_map_desc, + [MAX77686_LDO12] = &ldo_voltage_map_desc, + [MAX77686_LDO13] = &ldo_voltage_map_desc, + [MAX77686_LDO14] = &ldo_voltage_map_desc, + [MAX77686_LDO15] = &ldo_low_voltage_map_desc, + [MAX77686_LDO16] = &ldo_voltage_map_desc, + [MAX77686_LDO17] = &ldo_voltage_map_desc, + [MAX77686_LDO18] = &ldo_voltage_map_desc, + [MAX77686_LDO19] = &ldo_voltage_map_desc, + [MAX77686_LDO20] = &ldo_voltage_map_desc, + [MAX77686_LDO21] = &ldo_voltage_map_desc, + [MAX77686_LDO22] = &ldo_voltage_map_desc, + [MAX77686_LDO23] = &ldo_voltage_map_desc, + [MAX77686_LDO24] = &ldo_voltage_map_desc, + [MAX77686_LDO25] = &ldo_voltage_map_desc, + [MAX77686_LDO26] = &ldo_voltage_map_desc, + [MAX77686_BUCK1] = &buck_voltage_map_desc, + [MAX77686_BUCK2] = &buck_dvs_voltage_map_desc, + [MAX77686_BUCK3] = &buck_dvs_voltage_map_desc, + [MAX77686_BUCK4] = &buck_dvs_voltage_map_desc, + [MAX77686_BUCK5] = &buck_voltage_map_desc, + [MAX77686_BUCK6] = &buck_voltage_map_desc, + [MAX77686_BUCK7] = &buck_voltage_map_desc, + [MAX77686_BUCK8] = &buck_voltage_map_desc, + [MAX77686_BUCK9] = &buck_voltage_map_desc, + [MAX77686_EN32KHZ_AP] = NULL, + [MAX77686_EN32KHZ_CP] = NULL, + [MAX77686_P32KH] = NULL, +}; + +static int max77686_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct voltage_map_desc *desc; + int rid = rdev_get_id(rdev); + int val; + + if (rid >= ARRAY_SIZE(reg_voltage_map) || + rid < 0) + return -EINVAL; + + desc = reg_voltage_map[rid]; + if (desc == NULL) + return -EINVAL; + + val = desc->min + desc->step * selector; + if (val > desc->max) + return -EINVAL; + + return val; +} + +/* + * TODO + * Reaction to the LP/Standby for each regulator should be defined by + * each consumer, not by the regulator device driver if it depends + * on which device is attached to which regulator. Here we are + * creating possible PM-wise regression with board changes.Also, + * we can do the same effect without creating issues with board + * changes by carefully defining .state_mem at bsp and suspend ops + * callbacks. + */ +unsigned int max77686_opmode_reg[][3] = { + /* LDO1 ... LDO26 */ + /* {NORMAL, LP, STANDBY} */ + {0x3, 0x2, 0x0}, /* LDO1 */ + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, /* LDO11 */ + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x0}, /* LDO21 */ + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x0}, + {0x3, 0x2, 0x0}, + /* BUCK1 ... BUCK9 */ + {0x3, 0x0, 0x1}, /* BUCK1 */ + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x0, 0x0}, + {0x3, 0x0, 0x0}, + {0x3, 0x0, 0x0}, + {0x3, 0x0, 0x0}, + {0x3, 0x0, 0x0}, + /* 32KHZ */ + {0x1, 0x0, 0x0}, + {0x1, 0x0, 0x0}, + {0x1, 0x0, 0x0}, +}; + +static int max77686_get_enable_register(struct regulator_dev *rdev, + int *reg, int *mask, int *pattern) +{ + unsigned int rid = rdev_get_id(rdev); + unsigned int mode; + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + + if (rid >= ARRAY_SIZE(max77686_opmode_reg)) + return -EINVAL; + + mode = max77686->opmode_data[rid].mode; + pr_debug("%s: rid=%d, mode=%d, size=%d\n", + __func__, rid, mode, ARRAY_SIZE(max77686_opmode_reg)); + + if (max77686_opmode_reg[rid][mode] == 0x0) + WARN(1, "Not supported opmode\n"); + + switch (rid) { + case MAX77686_LDO1 ... MAX77686_LDO26: + *reg = MAX77686_REG_LDO1CTRL1 + (rid - MAX77686_LDO1); + *mask = MAX77686_OPMODE_MASK << MAX77686_OPMODE_SHIFT; + *pattern = max77686_opmode_reg[rid][mode] << MAX77686_OPMODE_SHIFT; + break; + case MAX77686_BUCK1: + *reg = MAX77686_REG_BUCK1CTRL; + *mask = MAX77686_OPMODE_MASK; + *pattern = max77686_opmode_reg[rid][mode]; + break; + case MAX77686_BUCK2: + case MAX77686_BUCK3: + case MAX77686_BUCK4: + *reg = MAX77686_REG_BUCK2CTRL1 + (rid - MAX77686_BUCK2)*10; + *mask = MAX77686_OPMODE_MASK << MAX77686_OPMODE_BUCK234_SHIFT; + *pattern = max77686_opmode_reg[rid][mode] << MAX77686_OPMODE_BUCK234_SHIFT; + break; + case MAX77686_BUCK5 ... MAX77686_BUCK9: + *reg = MAX77686_REG_BUCK5CTRL + (rid - MAX77686_BUCK5) * 2; + *mask = MAX77686_OPMODE_MASK; + *pattern = max77686_opmode_reg[rid][mode]; + break; + case MAX77686_EN32KHZ_AP ... MAX77686_P32KH: + *reg = MAX77686_REG_32KHZ; + *mask = 0x01 << (rid - MAX77686_EN32KHZ_AP); + *pattern = 0x01 << (rid - MAX77686_EN32KHZ_AP); + break; + default: + /* Not controllable or not exists */ + return -EINVAL; + } + + return 0; +} + +static int max77686_reg_is_enabled(struct regulator_dev *rdev) +{ + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77686->iodev->i2c; + int ret, reg, mask, pattern; + u8 val; + + ret = max77686_get_enable_register(rdev, ®, &mask, &pattern); + if (ret == -EINVAL) + return 1; /* "not controllable" */ + else if (ret) + return ret; + + ret = max77686_read_reg(i2c, reg, &val); + if (ret) + return ret; + + pr_debug("%s: id=%d, ret=%d, val=%x, mask=%x, pattern=%x\n", + __func__, rdev_get_id(rdev), (val & mask) == pattern, + val, mask, pattern); + + return (val & mask) == pattern; +} + +static int max77686_reg_enable(struct regulator_dev *rdev) +{ + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77686->iodev->i2c; + int ret, reg, mask, pattern; + + ret = max77686_get_enable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + printk(PMIC_DEBUG "%s: id=%d, pattern=%x\n", + __func__, rdev_get_id(rdev), pattern); + + return max77686_update_reg(i2c, reg, pattern, mask); +} + +static int max77686_reg_disable(struct regulator_dev *rdev) +{ + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77686->iodev->i2c; + int ret, reg, mask, pattern; + + ret = max77686_get_enable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + printk(PMIC_DEBUG "%s: id=%d, pattern=%x\n", + __func__, rdev_get_id(rdev), pattern); + + return max77686_update_reg(i2c, reg, ~mask, mask); +} + +static int max77686_get_voltage_register(struct regulator_dev *rdev, + int *_reg, int *_shift, int *_mask) +{ + int rid = rdev_get_id(rdev); + int reg, shift = 0, mask = 0x3f; + + switch (rid) { + case MAX77686_LDO1 ... MAX77686_LDO26: + reg = MAX77686_REG_LDO1CTRL1 + (rid - MAX77686_LDO1); + break; + case MAX77686_BUCK1: + reg = MAX77686_REG_BUCK1OUT; + break; + case MAX77686_BUCK2: + reg = MAX77686_REG_BUCK2DVS2; + mask = 0xff; + break; + case MAX77686_BUCK3: + reg = MAX77686_REG_BUCK3DVS2; + mask = 0xff; + break; + case MAX77686_BUCK4: + reg = MAX77686_REG_BUCK4DVS2; + mask = 0xff; + break; + case MAX77686_BUCK5 ... MAX77686_BUCK9: + reg = MAX77686_REG_BUCK5OUT + (rid - MAX77686_BUCK5) * 2; + break; + default: + return -EINVAL; + } + + *_reg = reg; + *_shift = shift; + *_mask = mask; + + return 0; +} + +static int max77686_get_voltage(struct regulator_dev *rdev) +{ + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77686->iodev->i2c; + int reg, shift, mask, ret; + int rid = rdev_get_id(rdev); + u8 val; + + ret = max77686_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + if ((rid == MAX77686_BUCK2 && max77686->buck2_gpiodvs) || + (rid == MAX77686_BUCK3 && max77686->buck3_gpiodvs) || + (rid == MAX77686_BUCK4 && max77686->buck4_gpiodvs)) + reg += max77686->buck234_gpioindex; + + ret = max77686_read_reg(i2c, reg, &val); + if (ret) + return ret; + + val >>= shift; + val &= mask; + + printk(PMIC_REG_DEBUG "%s: id=%d, val=%x\n", + __func__, rid, val); + + return max77686_list_voltage(rdev, val); +} + +static inline int max77686_get_voltage_proper_val( + const struct voltage_map_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; + + while (desc->min + desc->step * i < min_vol && + desc->min + desc->step * i < desc->max) + i++; + + if (desc->min + desc->step * i > max_vol) + return -EINVAL; + + if (i >= (1 << desc->n_bits)) + return -EINVAL; + + return i; +} + +static int max77686_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77686->iodev->i2c; + const struct voltage_map_desc *desc; + int rid = rdev_get_id(rdev); + int reg, shift = 0, mask, ret; + int i; + u8 org; + + desc = reg_voltage_map[rid]; + + /* W/A code about voltage drop of PASS1 */ + if (max77686->device_id <= MAX77686_DEVICE_PASS1) { + if (rid >= MAX77686_BUCK1 && rid <= MAX77686_BUCK4) { + min_uV = min_uV + MAX77686_DVS_VOL_COMP; + max_uV = max_uV + MAX77686_DVS_VOL_COMP; + } + } + + i = max77686_get_voltage_proper_val(desc, min_uV, max_uV); + if (i < 0) + return i; + + ret = max77686_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + /* TODO: If GPIO-DVS is being used, this won't work. */ + + max77686_read_reg(i2c, reg, &org); + org = (org & mask) >> shift; + +#if defined(CONFIG_MACH_M0) || defined(CONFIG_MACH_C1) || \ + defined(CONFIG_MACH_C1VZW) || defined(CONFIG_MACH_P4NOTE) || \ + defined(CONFIG_MACH_GC1) + /* Test code for HDMI debug */ + if (!gpio_get_value(GPIO_HDMI_EN)) +#endif + printk(PMIC_REG_DEBUG "max77686: id=%d, org=%x, val=%x", + rdev_get_id(rdev), org, i); + + ret = max77686_update_reg(i2c, reg, i << shift, mask << shift); + *selector = i; + + switch (rid) { + case MAX77686_BUCK2 ... MAX77686_BUCK4: + if (org < i) + udelay(DIV_ROUND_UP(desc->step * (i - org), + max77686->ramp_delay * 1000)); + break; + case MAX77686_BUCK1: + case MAX77686_BUCK5 ... MAX77686_BUCK9: + /* Unconditionally 100 mV/us */ + if (org < i) + udelay(DIV_ROUND_UP(desc->step * (i - org), 100000)); + break; + default: + break; + } + + return ret; +} + +static int max77686_reg_do_nothing(struct regulator_dev *rdev) +{ + return 0; +} + +static struct regulator_ops max77686_ldo_ops = { + .list_voltage = max77686_list_voltage, + .is_enabled = max77686_reg_is_enabled, + .enable = max77686_reg_enable, + .disable = max77686_reg_disable, + .get_voltage = max77686_get_voltage, + .set_voltage = max77686_set_voltage, + /* TODO: set 0xC0 -> 0x40 with suspend_enable. for 0x0, ->0x0 */ + .set_suspend_enable = max77686_reg_do_nothing, + /* LDO's ON(0xC0) means "ON at normal, OFF at suspend" */ + .set_suspend_disable = max77686_reg_do_nothing, +}; + +static struct regulator_ops max77686_buck_ops = { + .list_voltage = max77686_list_voltage, + .is_enabled = max77686_reg_is_enabled, + .enable = max77686_reg_enable, + .disable = max77686_reg_disable, + .get_voltage = max77686_get_voltage, + .set_voltage = max77686_set_voltage, + /* Interpret suspend_enable as "keep on if it was enabled." */ + .set_suspend_enable = max77686_reg_do_nothing, + .set_suspend_disable = max77686_reg_disable, +}; + +static struct regulator_ops max77686_fixedvolt_ops = { + .list_voltage = max77686_list_voltage, + .is_enabled = max77686_reg_is_enabled, + .enable = max77686_reg_enable, + .disable = max77686_reg_disable, + /* Interpret suspend_enable as "keep on if it was enabled." */ + .set_suspend_enable = max77686_reg_do_nothing, + .set_suspend_disable = max77686_reg_disable, +}; + +#define regulator_desc_ldo(num) { \ + .name = "LDO"#num, \ + .id = MAX77686_LDO##num, \ + .ops = &max77686_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ +} +#define regulator_desc_buck(num) { \ + .name = "BUCK"#num, \ + .id = MAX77686_BUCK##num, \ + .ops = &max77686_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_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), + regulator_desc_buck(9), + { + .name = "EN32KHz AP", + .id = MAX77686_EN32KHZ_AP, + .ops = &max77686_fixedvolt_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz CP", + .id = MAX77686_EN32KHZ_CP, + .ops = &max77686_fixedvolt_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz PMIC", + .id = MAX77686_P32KH, + .ops = &max77686_fixedvolt_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, +}; + +static int max77686_set_ramp_rate(struct i2c_client *i2c, int rate) +{ + int ramp_delay = 0; + u8 data = 0; + + switch (rate) { + case MAX77686_RAMP_RATE_100MV: + ramp_delay = 100; + data = MAX77686_REG_RAMP_RATE_100MV; + break; + case MAX77686_RAMP_RATE_13MV: + ramp_delay = 14; + data = MAX77686_REG_RAMP_RATE_13MV; + break; + case MAX77686_RAMP_RATE_27MV: + ramp_delay = 28; + data = MAX77686_REG_RAMP_RATE_27MV; + break; + case MAX77686_RAMP_RATE_55MV: + ramp_delay = 55; + data = MAX77686_REG_RAMP_RATE_55MV; + break; + } + + printk(PMIC_DEBUG "%s: ramp_delay=%d, data=0x%x\n", __func__, ramp_delay, data); + + max77686_update_reg(i2c, MAX77686_REG_BUCK2CTRL1, data, 0xC0); + max77686_update_reg(i2c, MAX77686_REG_BUCK3CTRL1, data, 0xC0); + max77686_update_reg(i2c, MAX77686_REG_BUCK4CTRL1, data, 0xC0); + + return ramp_delay; +} + +static __devinit int max77686_pmic_probe(struct platform_device *pdev) +{ + struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_dev **rdev; + struct max77686_data *max77686; + struct i2c_client *i2c; + int i, ret, size; + u8 data = 0; + + printk(PMIC_DEBUG "%s\n", __func__); + + if (!pdata) { + dev_err(pdev->dev.parent, "No platform init data supplied.\n"); + return -ENODEV; + } + + max77686 = kzalloc(sizeof(struct max77686_data), GFP_KERNEL); + if (!max77686) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * pdata->num_regulators; + max77686->rdev = kzalloc(size, GFP_KERNEL); + if (!max77686->rdev) { + kfree(max77686); + return -ENOMEM; + } + + rdev = max77686->rdev; + max77686->dev = &pdev->dev; + max77686->iodev = iodev; + max77686->num_regulators = pdata->num_regulators; + platform_set_drvdata(pdev, max77686); + i2c = max77686->iodev->i2c; + + max77686->opmode_data = pdata->opmode_data; + max77686->ramp_delay = max77686_set_ramp_rate(i2c, pdata->ramp_rate); + + max77686_read_reg(i2c, MAX77686_REG_DEVICE_ID, &data); + max77686->device_id = (data & 0x7); + printk(PMIC_DEBUG "%s: DEVICE ID=0x%x\n", __func__, data); + + /* + * TODO + * This disables GPIO-DVS. Later we may need to implement GPIO-DVS.. + * or we do not? + */ + max77686->buck2_gpiodvs = false; + max77686->buck3_gpiodvs = false; + max77686->buck4_gpiodvs = false; + for (i = 0; i < 3; i++) { + char buf[80]; + + sprintf(buf, "MAX77686 DVS%d", i); + + if (gpio_is_valid(pdata->buck234_gpio_dvs[i].gpio)) { + max77686->buck234_gpios_dvs[i] = + pdata->buck234_gpio_dvs[i].gpio; + gpio_request(pdata->buck234_gpio_dvs[i].gpio, buf); + gpio_direction_output(pdata->buck234_gpio_dvs[i].gpio, + pdata->buck234_gpio_dvs[i].data); + } else { + dev_info(&pdev->dev, "GPIO %s ignored (%d)\n", + buf, pdata->buck234_gpio_dvs[i].gpio); + } + + sprintf(buf, "MAX77686 SELB%d", i); + + if (gpio_is_valid(pdata->buck234_gpio_selb[i])) { + int data = (max77686->device_id <= MAX77686_DEVICE_PASS1) ? 1 : 0; + max77686->buck234_gpios_selb[i] = + pdata->buck234_gpio_selb[i]; + gpio_request(pdata->buck234_gpio_selb[i], buf); + gpio_direction_output(pdata->buck234_gpio_selb[i], data); + } else { + dev_info(&pdev->dev, "GPIO %s ignored (%d)\n", + buf, pdata->buck234_gpio_selb[i]); + } + } + max77686->buck234_gpioindex = 0; + + for (i = 0; i < 8; i++) { + ret = max77686_get_voltage_proper_val( + &buck_dvs_voltage_map_desc, + pdata->buck2_voltage[i], + pdata->buck2_voltage[i] + + buck_dvs_voltage_map_desc.step); + /* 1.1V as default for safety */ + if (ret < 0) + max77686->buck2_vol[i] = 0x28; + else + max77686->buck2_vol[i] = ret; + max77686_write_reg(i2c, MAX77686_REG_BUCK2DVS1 + i, + max77686->buck2_vol[i]); + + ret = max77686_get_voltage_proper_val( + &buck_dvs_voltage_map_desc, + pdata->buck3_voltage[i], + pdata->buck3_voltage[i] + + buck_dvs_voltage_map_desc.step); + /* 1.1V as default for safety */ + if (ret < 0) + max77686->buck3_vol[i] = 0x28; + else + max77686->buck3_vol[i] = ret; + max77686_write_reg(i2c, MAX77686_REG_BUCK3DVS1 + i, + max77686->buck3_vol[i]); + + ret = max77686_get_voltage_proper_val( + &buck_dvs_voltage_map_desc, + pdata->buck4_voltage[i], + pdata->buck4_voltage[i] + + buck_dvs_voltage_map_desc.step); + /* 1.1V as default for safety */ + if (ret < 0) + max77686->buck4_vol[i] = 0x28; + else + max77686->buck4_vol[i] = ret; + max77686_write_reg(i2c, MAX77686_REG_BUCK4DVS1 + i, + max77686->buck4_vol[i]); + } + + if (pdata->has_full_constraints) + regulator_has_full_constraints(); + + for (i = 0; i < pdata->num_regulators; i++) { + const struct voltage_map_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; + + printk(PMIC_DEBUG "%s: desc=%p, id=%d, n_vol=%d, max=%d, min=%d, step=%d\n", + __func__, desc, id, regulators[id].n_voltages, + desc->max, desc->min, desc->step); + } + + rdev[i] = regulator_register(®ulators[id], max77686->dev, + pdata->regulators[i].initdata, max77686); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(max77686->dev, "regulator init failed for %d\n", + id); + rdev[i] = NULL; + goto err; + } + } + + return 0; +err: + for (i = 0; i < 3; i++) { + if (max77686->buck234_gpios_dvs[i]) + gpio_free(max77686->buck234_gpios_dvs[i]); + if (max77686->buck234_gpios_dvs[i]) + gpio_free(max77686->buck234_gpios_selb[i]); + } + + for (i = 0; i < max77686->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + kfree(max77686->rdev); + kfree(max77686); + + return ret; +} + +static int __devexit max77686_pmic_remove(struct platform_device *pdev) +{ + struct max77686_data *max77686 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max77686->rdev; + int i; + + for (i = 0; i < 3; i++) { + if (max77686->buck234_gpios_dvs[i]) + gpio_free(max77686->buck234_gpios_dvs[i]); + if (max77686->buck234_gpios_dvs[i]) + gpio_free(max77686->buck234_gpios_selb[i]); + } + + for (i = 0; i < max77686->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + kfree(max77686->rdev); + kfree(max77686); + + return 0; +} + +static const struct platform_device_id max77686_pmic_id[] = { + { "max77686-pmic", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, max77686_pmic_id); + +static struct platform_driver max77686_pmic_driver = { + .driver = { + .name = "max77686-pmic", + .owner = THIS_MODULE, + }, + .probe = max77686_pmic_probe, + .remove = __devexit_p(max77686_pmic_remove), + .id_table = max77686_pmic_id, +}; + +static int __init max77686_pmic_init(void) +{ + printk(PMIC_DEBUG "%s\n", __func__); + + return platform_driver_register(&max77686_pmic_driver); +} +subsys_initcall(max77686_pmic_init); + +static void __exit max77686_pmic_cleanup(void) +{ + platform_driver_unregister(&max77686_pmic_driver); +} +module_exit(max77686_pmic_cleanup); + +MODULE_DESCRIPTION("MAXIM 77686 Regulator Driver"); +MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c new file mode 100644 index 00000000000..bcbdd3ad42c --- /dev/null +++ b/drivers/regulator/max77693.c @@ -0,0 +1,574 @@ +/* + * max77693.c - Regulator driver for the Maxim 77693 + * + * Copyright (C) 2011 Samsung Electronics + * Sukdong Kim <sukdong.kim@smasung.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997.c + */ + +#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/max77693.h> +#include <linux/mfd/max77693-private.h> + +struct max77693_data { + struct device *dev; + struct max77693_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; + + u8 saved_states[MAX77693_REG_MAX]; +}; + +struct voltage_map_desc { + int min; + int max; + int step; + unsigned int n_bits; +}; + +/* current map in mA */ +static const struct voltage_map_desc charger_current_map_desc = { + .min = 60, .max = 2580, .step = 20, .n_bits = 7, +}; + +static const struct voltage_map_desc topoff_current_map_desc = { + .min = 50, .max = 200, .step = 10, .n_bits = 4, +}; + +static const struct voltage_map_desc *reg_voltage_map[] = { + [MAX77693_ESAFEOUT1] = NULL, + [MAX77693_ESAFEOUT2] = NULL, + [MAX77693_CHARGER] = &charger_current_map_desc, +}; + +static inline int max77693_get_rid(struct regulator_dev *rdev) +{ + dev_info(&rdev->dev, "func:%s\n", __func__); + return rdev_get_id(rdev); +} + +static int max77693_list_voltage_safeout(struct regulator_dev *rdev, + unsigned int selector) +{ + int rid = max77693_get_rid(rdev); + dev_info(&rdev->dev, "func:%s\n", __func__); + if (rid == MAX77693_ESAFEOUT1 || rid == MAX77693_ESAFEOUT2) { + switch (selector) { + case 0: + return 4850000; + case 1: + return 4900000; + case 2: + return 4950000; + case 3: + return 3300000; + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static int max77693_get_enable_register(struct regulator_dev *rdev, + int *reg, int *mask, int *pattern) +{ + int rid = max77693_get_rid(rdev); + dev_info(&rdev->dev, "func:%s\n", __func__); + switch (rid) { + case MAX77693_ESAFEOUT1...MAX77693_ESAFEOUT2: + *reg = MAX77693_CHG_REG_SAFEOUT_CTRL; + *mask = 0x40 << (rid - MAX77693_ESAFEOUT1); + *pattern = 0x40 << (rid - MAX77693_ESAFEOUT1); + break; + case MAX77693_CHARGER: + *reg = MAX77693_CHG_REG_CHG_CNFG_00; + *mask = 0xf; + *pattern = 0x5; + break; + default: + /* Not controllable or not exists */ + return -EINVAL; + } + + return 0; +} + +static int max77693_get_disable_register(struct regulator_dev *rdev, + int *reg, int *mask, int *pattern) +{ + int rid = max77693_get_rid(rdev); + dev_info(&rdev->dev, "func:%s\n", __func__); + switch (rid) { + case MAX77693_ESAFEOUT1...MAX77693_ESAFEOUT2: + *reg = MAX77693_CHG_REG_SAFEOUT_CTRL; + *mask = 0x40 << (rid - MAX77693_ESAFEOUT1); + *pattern = 0x00; + break; + case MAX77693_CHARGER: + *reg = MAX77693_CHG_REG_CHG_CNFG_00; + *mask = 0xf; + *pattern = 0x00; + break; + default: + /* Not controllable or not exists */ + return -EINVAL; + } + + return 0; +} + +static int max77693_reg_is_enabled(struct regulator_dev *rdev) +{ + struct max77693_data *max77693 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77693->iodev->i2c; + int ret, reg, mask, pattern; + u8 val; + dev_info(&rdev->dev, "func:%s\n", __func__); + ret = max77693_get_enable_register(rdev, ®, &mask, &pattern); + if (ret == -EINVAL) + return 1; /* "not controllable" */ + else if (ret) + return ret; + + ret = max77693_read_reg(i2c, reg, &val); + if (ret) + return ret; + + return (val & mask) == pattern; +} + +static int max77693_reg_enable(struct regulator_dev *rdev) +{ + struct max77693_data *max77693 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77693->iodev->i2c; + int ret, reg, mask, pattern; + dev_info(&rdev->dev, "func:%s\n", __func__); + ret = max77693_get_enable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + return max77693_update_reg(i2c, reg, pattern, mask); +} + +static int max77693_reg_disable(struct regulator_dev *rdev) +{ + struct max77693_data *max77693 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77693->iodev->i2c; + int ret, reg, mask, pattern; + dev_info(&rdev->dev, "func:%s\n", __func__); + ret = max77693_get_disable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + return max77693_update_reg(i2c, reg, pattern, mask); +} + +static int max77693_get_voltage_register(struct regulator_dev *rdev, + int *_reg, int *_shift, int *_mask) +{ + int rid = max77693_get_rid(rdev); + int reg, shift = 0, mask = 0x3f; + dev_info(&rdev->dev, "func:%s\n", __func__); + switch (rid) { + case MAX77693_ESAFEOUT1...MAX77693_ESAFEOUT2: + reg = MAX77693_CHG_REG_SAFEOUT_CTRL; + shift = (rid == MAX77693_ESAFEOUT2) ? 2 : 0; + mask = 0x3; + break; + case MAX77693_CHARGER: + reg = MAX77693_CHG_REG_CHG_CNFG_09; + shift = 0; + mask = 0x7f; + break; + default: + return -EINVAL; + } + + *_reg = reg; + *_shift = shift; + *_mask = mask; + + return 0; +} + +static int max77693_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct voltage_map_desc *desc; + int rid = max77693_get_rid(rdev); + int val; + dev_info(&rdev->dev, "func:%s\n", __func__); + if (rid >= ARRAY_SIZE(reg_voltage_map) || rid < 0) + return -EINVAL; + + desc = reg_voltage_map[rid]; + if (desc == NULL) + return -EINVAL; + + /* the first four codes for charger current are all 60mA */ + if (rid == MAX77693_CHARGER) { + if (selector <= 3) + selector = 0; + else + selector -= 3; + } + + val = desc->min + desc->step * selector; + if (val > desc->max) + return -EINVAL; + + return val * 1000; +} + +static int max77693_get_voltage(struct regulator_dev *rdev) +{ + struct max77693_data *max77693 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77693->iodev->i2c; + int reg, shift, mask, ret; + + u8 val; + dev_info(&rdev->dev, "func:%s\n", __func__); + ret = max77693_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + ret = max77693_read_reg(i2c, reg, &val); + if (ret) + return ret; + + val >>= shift; + val &= mask; + + if (rdev->desc && rdev->desc->ops && rdev->desc->ops->list_voltage) + return rdev->desc->ops->list_voltage(rdev, val); + + /* + * max77693_list_voltage returns value for any rdev with voltage_map, + * which works for "CHARGER" and "CHARGER TOPOFF" that do not have + * list_voltage ops (they are current regulators). + */ + return max77693_list_voltage(rdev, val); +} + +static inline int max77693_get_voltage_proper_val( + const struct voltage_map_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; + + while (desc->min + desc->step * i < min_vol && + desc->min + desc->step * i < desc->max) + i++; + + if (desc->min + desc->step * i > max_vol) + return -EINVAL; + + if (i >= (1 << desc->n_bits)) + return -EINVAL; + + return i; +} + +static int max77693_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct max77693_data *max77693 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77693->iodev->i2c; + int min_vol = min_uV / 1000, max_vol = max_uV / 1000; + const struct voltage_map_desc *desc; + int rid = max77693_get_rid(rdev); + int reg, shift = 0, mask, ret; + int i; + u8 org; + + switch (rid) { + case MAX77693_CHARGER: + break; + default: + return -EINVAL; + } + + desc = reg_voltage_map[rid]; + + i = max77693_get_voltage_proper_val(desc, min_vol, max_vol); + if (i < 0) + return i; + + ret = max77693_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + max77693_read_reg(i2c, reg, &org); + org = (org & mask) >> shift; + + /* the first four codes for charger current are all 60mA */ + if (rid == MAX77693_CHARGER) + i += 3; + + ret = max77693_update_reg(i2c, reg, i << shift, mask << shift); + + return ret; +} + +static const int safeoutvolt[] = { + 3300000, + 4850000, + 4900000, + 4950000, +}; + +/* For SAFEOUT1 and SAFEOUT2 */ +static int max77693_set_voltage_safeout(struct regulator_dev *rdev, + int min_uV, int max_uV, + unsigned *selector) +{ + struct max77693_data *max77693 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77693->iodev->i2c; + int rid = max77693_get_rid(rdev); + int reg, shift = 0, mask, ret; + int i = 0; + u8 val; + dev_info(&rdev->dev, "func:%s\n", __func__); + if (rid != MAX77693_ESAFEOUT1 && rid != MAX77693_ESAFEOUT2) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(safeoutvolt); i++) { + if (min_uV <= safeoutvolt[i] && max_uV >= safeoutvolt[i]) + break; + } + + if (i >= ARRAY_SIZE(safeoutvolt)) + return -EINVAL; + + if (i == 0) + val = 0x3; + else + val = i - 1; + + ret = max77693_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + ret = max77693_update_reg(i2c, reg, val << shift, mask << shift); + *selector = val; + + return ret; +} + +static int max77693_reg_enable_suspend(struct regulator_dev *rdev) +{ + dev_info(&rdev->dev, "func:%s\n", __func__); + return 0; +} + +static int max77693_reg_disable_suspend(struct regulator_dev *rdev) +{ + struct max77693_data *max77693 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max77693->iodev->i2c; + int ret, reg, mask, pattern; + int rid = max77693_get_rid(rdev); + dev_info(&rdev->dev, "func:%s\n", __func__); + ret = max77693_get_disable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + max77693_read_reg(i2c, reg, &max77693->saved_states[rid]); + + dev_dbg(&rdev->dev, "Full Power-Off for %s (%xh -> %xh)\n", + rdev->desc->name, max77693->saved_states[rid] & mask, + (~pattern) & mask); + return max77693_update_reg(i2c, reg, pattern, mask); +} + +static struct regulator_ops max77693_safeout_ops = { + .list_voltage = max77693_list_voltage_safeout, + .is_enabled = max77693_reg_is_enabled, + .enable = max77693_reg_enable, + .disable = max77693_reg_disable, + .get_voltage = max77693_get_voltage, + .set_voltage = max77693_set_voltage_safeout, + .set_suspend_enable = max77693_reg_enable_suspend, + .set_suspend_disable = max77693_reg_disable_suspend, +}; + +static struct regulator_ops max77693_charger_ops = { + .is_enabled = max77693_reg_is_enabled, + .enable = max77693_reg_enable, + .disable = max77693_reg_disable, + .get_current_limit = max77693_get_voltage, + .set_current_limit = max77693_set_voltage, +}; + +static struct regulator_desc regulators[] = { + { + .name = "ESAFEOUT1", + .id = MAX77693_ESAFEOUT1, + .ops = &max77693_safeout_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ESAFEOUT2", + .id = MAX77693_ESAFEOUT2, + .ops = &max77693_safeout_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "CHARGER", + .id = MAX77693_CHARGER, + .ops = &max77693_charger_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + } +}; + +static __devinit int max77693_pmic_probe(struct platform_device *pdev) +{ + struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max77693_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_dev **rdev; + struct max77693_data *max77693; + struct i2c_client *i2c; + int i, ret, size; + dev_info(&pdev->dev, "%s\n", __func__); + if (!pdata) { + pr_info("[%s:%d] !pdata\n", __FILE__, __LINE__); + dev_err(pdev->dev.parent, "No platform init data supplied.\n"); + return -ENODEV; + } + + max77693 = kzalloc(sizeof(struct max77693_data), GFP_KERNEL); + if (!max77693) { + pr_info("[%s:%d] if (!max77693)\n", __FILE__, __LINE__); + return -ENOMEM; + } + size = sizeof(struct regulator_dev *) * pdata->num_regulators; + max77693->rdev = kzalloc(size, GFP_KERNEL); + if (!max77693->rdev) { + pr_info("[%s:%d] if (!max77693->rdev)\n", __FILE__, __LINE__); + kfree(max77693); + return -ENOMEM; + } + + rdev = max77693->rdev; + max77693->dev = &pdev->dev; + max77693->iodev = iodev; + max77693->num_regulators = pdata->num_regulators; + platform_set_drvdata(pdev, max77693); + i2c = max77693->iodev->i2c; + pr_info("[%s:%d] pdata->num_regulators:%d\n", __FILE__, __LINE__, + pdata->num_regulators); + for (i = 0; i < pdata->num_regulators; i++) { + + const struct voltage_map_desc *desc; + int id = pdata->regulators[i].id; + pr_info("[%s:%d] for in pdata->num_regulators:%d\n", __FILE__, + __LINE__, pdata->num_regulators); + desc = reg_voltage_map[id]; + if (id == MAX77693_ESAFEOUT1 || id == MAX77693_ESAFEOUT2) + regulators[id].n_voltages = 4; + + rdev[i] = regulator_register(®ulators[id], max77693->dev, + pdata->regulators[i].initdata, + max77693); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(max77693->dev, "regulator init failed for %d\n", + id); + rdev[i] = NULL; + goto err; + } + } + + return 0; + err: + pr_info("[%s:%d] err:\n", __FILE__, __LINE__); + for (i = 0; i < max77693->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + err_alloc: + pr_info("[%s:%d] err_alloc\n", __FILE__, __LINE__); + kfree(max77693->rdev); + kfree(max77693); + + return ret; +} + +static int __devexit max77693_pmic_remove(struct platform_device *pdev) +{ + struct max77693_data *max77693 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max77693->rdev; + int i; + dev_info(&pdev->dev, "%s\n", __func__); + for (i = 0; i < max77693->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + kfree(max77693->rdev); + kfree(max77693); + + return 0; +} + +static const struct platform_device_id max77693_pmic_id[] = { + {"max77693-safeout", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(platform, max77693_pmic_id); + +static struct platform_driver max77693_pmic_driver = { + .driver = { + .name = "max77693-safeout", + .owner = THIS_MODULE, + }, + .probe = max77693_pmic_probe, + .remove = __devexit_p(max77693_pmic_remove), + .id_table = max77693_pmic_id, +}; + +static int __init max77693_pmic_init(void) +{ + return platform_driver_register(&max77693_pmic_driver); +} + +subsys_initcall(max77693_pmic_init); + +static void __exit max77693_pmic_cleanup(void) +{ + platform_driver_unregister(&max77693_pmic_driver); +} + +module_exit(max77693_pmic_cleanup); + +MODULE_DESCRIPTION("MAXIM 77693 Regulator Driver"); +MODULE_AUTHOR("Sukdong Kim <Sukdong.Kim@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index 30eb9e54f7e..c6d0f7920bb 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -22,6 +22,9 @@ #define MAX8649_DCDC_STEP 10000 /* uV */ #define MAX8649_VOL_MASK 0x3f +/* difference between voltages of max8649 and max8952 */ +#define DIFF_MAX8952_DCDC_VOL 20000 /* uV */ + /* Registers */ #define MAX8649_MODE0 0x00 #define MAX8649_MODE1 0x01 @@ -47,6 +50,11 @@ #define MAX8649_RAMP_MASK (7 << 5) #define MAX8649_RAMP_DOWN (1 << 1) +enum chips { + MAX8649 = 0x200a, + MAX8952 = 0x201a, +}; + struct max8649_regulator_info { struct regulator_dev *regulator; struct i2c_client *i2c; @@ -54,6 +62,7 @@ struct max8649_regulator_info { struct mutex io_lock; int vol_reg; + int type; unsigned mode:2; /* bit[1:0] = VID1, VID0 */ unsigned extclk_freq:2; unsigned extclk:1; @@ -138,7 +147,12 @@ static inline int check_range(int min_uV, int max_uV) static int max8649_list_voltage(struct regulator_dev *rdev, unsigned index) { - return (MAX8649_DCDC_VMIN + index * MAX8649_DCDC_STEP); + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + int ret = MAX8649_DCDC_VMIN + index * MAX8649_DCDC_STEP; + + if (info->type == MAX8952) + ret += DIFF_MAX8952_DCDC_VOL; + return ret; } static int max8649_get_voltage(struct regulator_dev *rdev) @@ -160,6 +174,11 @@ static int max8649_set_voltage(struct regulator_dev *rdev, struct max8649_regulator_info *info = rdev_get_drvdata(rdev); unsigned char data, mask; + if (info->type == MAX8952) { + min_uV -= DIFF_MAX8952_DCDC_VOL; + max_uV -= DIFF_MAX8952_DCDC_VOL; + } + if (check_range(min_uV, max_uV)) { dev_err(info->dev, "invalid voltage range (%d, %d) uV\n", min_uV, max_uV); @@ -282,6 +301,7 @@ static int __devinit max8649_regulator_probe(struct i2c_client *client, struct max8649_regulator_info *info = NULL; unsigned char data; int ret; + int chip_id; info = kzalloc(sizeof(struct max8649_regulator_info), GFP_KERNEL); if (!info) { @@ -314,11 +334,32 @@ static int __devinit max8649_regulator_probe(struct i2c_client *client, ret = max8649_reg_read(info->i2c, MAX8649_CHIP_ID1); if (ret < 0) { - dev_err(info->dev, "Failed to detect ID of MAX8649:%d\n", - ret); + dev_err(info->dev, "Failed to detect ID1 of %s:%d\n", + id->name, ret); + goto out; + } + chip_id = ret; + + ret = max8649_reg_read(info->i2c, MAX8649_CHIP_ID2); + if (ret < 0) { + dev_err(info->dev, "Failed to detect ID2 of %s:%d\n", + id->name, ret); goto out; } - dev_info(info->dev, "Detected MAX8649 (ID:%x)\n", ret); + + chip_id = (chip_id << 8) | ret; + + if ((id->driver_data & 0xFFF0) != (chip_id & 0xFFF0)) { + dev_err(info->dev, "Failed to detect the device\n" + "requested : 0x%x, detected 0x%x\n", + (u32)id->driver_data, chip_id); + ret = -ENODEV; + goto out; + } + + dev_info(info->dev, "Detected %s (ID: 0x%x)\n", id->name, chip_id); + + info->type = id->driver_data; /* enable VID0 & VID1 */ max8649_set_bits(info->i2c, MAX8649_CONTROL, MAX8649_VID_MASK, 0); @@ -355,7 +396,7 @@ static int __devinit max8649_regulator_probe(struct i2c_client *client, goto out; } - dev_info(info->dev, "Max8649 regulator device is detected.\n"); + dev_info(info->dev, "%s regulator device is detected.\n", id->name); return 0; out: kfree(info); @@ -376,7 +417,8 @@ static int __devexit max8649_regulator_remove(struct i2c_client *client) } static const struct i2c_device_id max8649_id[] = { - { "max8649", 0 }, + { "max8649", MAX8649 }, + { "max8952", MAX8952 }, { } }; MODULE_DEVICE_TABLE(i2c, max8649_id); diff --git a/drivers/regulator/max8698.c b/drivers/regulator/max8698.c new file mode 100644 index 00000000000..c9ff8134d86 --- /dev/null +++ b/drivers/regulator/max8698.c @@ -0,0 +1,569 @@ +/* + * max8698.c - Voltage regulator driver for the Maxim 8698 + * + * Copyright (C) 2009-2010 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * Marek Szyprowski <m.szyprowski@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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/mfd/max8698.h> +#include <linux/mfd/max8698-private.h> + +struct max8698_data { + struct device *dev; + struct max8698_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; +}; + +struct voltage_map_desc { + int min; + int max; + int step; +}; + +#define RAMP_DELAY_VAL 10 + +/* Voltage maps */ +static const struct voltage_map_desc ldo23_voltage_map_desc = { + .min = 800, + .step = 50, + .max = 1300, +}; +static const struct voltage_map_desc ldo45679_voltage_map_desc = { + .min = 1600, + .step = 100, + .max = 3600, +}; +static const struct voltage_map_desc ldo8_voltage_map_desc = { + .min = 3000, + .step = 100, + .max = 3600, +}; +static const struct voltage_map_desc buck12_voltage_map_desc = { + .min = 750, + .step = 50, + .max = 1500, +}; + +static const struct voltage_map_desc *ldo_voltage_map[] = { + NULL, + &ldo23_voltage_map_desc, /* LDO1 */ + &ldo23_voltage_map_desc, /* LDO2 */ + &ldo23_voltage_map_desc, /* LDO3 */ + &ldo45679_voltage_map_desc, /* LDO4 */ + &ldo45679_voltage_map_desc, /* LDO5 */ + &ldo45679_voltage_map_desc, /* LDO6 */ + &ldo45679_voltage_map_desc, /* LDO7 */ + &ldo8_voltage_map_desc, /* LDO8 */ + &ldo45679_voltage_map_desc, /* LDO9 */ + &buck12_voltage_map_desc, /* BUCK1 */ + &buck12_voltage_map_desc, /* BUCK2 */ + &ldo45679_voltage_map_desc, /* BUCK3 */ +}; + +static inline int max8698_get_ldo(struct regulator_dev *rdev) +{ + return rdev_get_id(rdev); +} + +static int max8698_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct voltage_map_desc *desc; + int ldo = max8698_get_ldo(rdev); + int val; + + if (ldo >= ARRAY_SIZE(ldo_voltage_map)) + return -EINVAL; + + desc = ldo_voltage_map[ldo]; + if (desc == NULL) + return -EINVAL; + + val = desc->min + desc->step * selector; + if (val > desc->max) + return -EINVAL; + + return val * 1000; +} + +static int max8698_get_enable_register(struct regulator_dev *rdev, + int *reg, int *shift) +{ + int ldo = max8698_get_ldo(rdev); + + switch (ldo) { + case MAX8698_LDO2 ... MAX8698_LDO5: + *reg = MAX8698_REG_ONOFF1; + *shift = 4 - (ldo - MAX8698_LDO2); + break; + case MAX8698_LDO6 ... MAX8698_LDO9: + *reg = MAX8698_REG_ONOFF2; + *shift = 7 - (ldo - MAX8698_LDO6); + break; + case MAX8698_BUCK1 ... MAX8698_BUCK3: + *reg = MAX8698_REG_ONOFF1; + *shift = 7 - (ldo - MAX8698_BUCK1); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int max8698_ldo_is_enabled(struct regulator_dev *rdev) +{ + struct max8698_data *max8698 = rdev_get_drvdata(rdev); + int ret, reg, shift = 8; + u8 val; + + ret = max8698_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + ret = max8698_read_reg(max8698->iodev, reg, &val); + if (ret) + return ret; + + return val & (1 << shift); +} + +static int max8698_ldo_enable(struct regulator_dev *rdev) +{ + struct max8698_data *max8698 = rdev_get_drvdata(rdev); + int reg, shift = 8, ret; + + ret = max8698_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + return max8698_update_reg(max8698->iodev, reg, 1<<shift, 1<<shift); +} + +static int max8698_ldo_disable(struct regulator_dev *rdev) +{ + struct max8698_data *max8698 = rdev_get_drvdata(rdev); + int reg, shift = 8, ret; + + ret = max8698_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + return max8698_update_reg(max8698->iodev, reg, 0, 1<<shift); +} + +static int max8698_get_voltage_register(struct regulator_dev *rdev, + int *_reg, int *_shift, int *_mask) +{ + struct max8698_data *max8698 = rdev_get_drvdata(rdev); + struct max8698_platform_data *pdata = dev_get_platdata(max8698->iodev->dev); + int ldo = max8698_get_ldo(rdev); + int reg, shift = 0, mask = 0xff; + + switch (ldo) { + case MAX8698_LDO2 ... MAX8698_LDO3: + reg = MAX8698_REG_LDO2_LDO3; + mask = 0xf; + if (ldo == MAX8698_LDO2) + shift = 4; + else + shift = 0; + break; + case MAX8698_LDO4 ... MAX8698_LDO7: + reg = MAX8698_REG_LDO4 + (ldo - MAX8698_LDO4); + break; + case MAX8698_LDO8: + reg = MAX8698_REG_LDO8_BKCHAR; + mask = 0xf; + shift = 4; + break; + case MAX8698_LDO9: + reg = MAX8698_REG_LDO9; + break; + case MAX8698_BUCK1: + mask = 0xf; + if (gpio_get_value(pdata->set2) == 0) + reg = MAX8698_REG_DVSARM12; + else + reg = MAX8698_REG_DVSARM34; + + if (gpio_get_value(pdata->set1) == 0) + shift = 0; + else + shift = 4; + break; + case MAX8698_BUCK2: + if (gpio_get_value(pdata->set3) == 0) + shift = 0; + else + shift = 4; + reg = MAX8698_REG_DVSINT12; + mask = 0xf; + break; + case MAX8698_BUCK3: + reg = MAX8698_REG_BUCK3; + break; + default: + return -EINVAL; + } + + *_reg = reg; + *_shift = shift; + *_mask = mask; + + return 0; +} + +static int max8698_get_voltage(struct regulator_dev *rdev) +{ + struct max8698_data *max8698 = rdev_get_drvdata(rdev); + int reg, shift = 0, mask, ret; + u8 val; + + ret = max8698_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + ret = max8698_read_reg(max8698->iodev, reg, &val); + if (ret) + return ret; + + val >>= shift; + val &= mask; + + return max8698_list_voltage(rdev, val); +} + +static int max8698_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max8698_data *max8698 = rdev_get_drvdata(rdev); + struct max8698_platform_data *pdata = dev_get_platdata(max8698->iodev->dev); + int min_vol = min_uV / 1000, max_vol = max_uV / 1000; + const struct voltage_map_desc *desc; + int ldo = max8698_get_ldo(rdev); + int reg, shift = 0, mask, ret = 0; + int i = 0; + int previous_vol = 0; + + if (ldo >= ARRAY_SIZE(ldo_voltage_map)) + return -EINVAL; + + desc = ldo_voltage_map[ldo]; + if (desc == NULL) + return -EINVAL; + + /* For Buck1/2 */ + if (ldo == MAX8698_BUCK1) { + if (min_vol == (desc->min + desc->step * pdata->dvsarm2)) { + gpio_set_value(pdata->set2, 0); + gpio_set_value(pdata->set1, 1); + } else if (min_vol == (desc->min + desc->step * pdata->dvsarm3)) { + gpio_set_value(pdata->set2, 1); + gpio_set_value(pdata->set1, 0); + } else if (min_vol == (desc->min + desc->step * pdata->dvsarm4)) { + gpio_set_value(pdata->set2, 1); + gpio_set_value(pdata->set1, 1); + } else { + gpio_set_value(pdata->set2, 0); + gpio_set_value(pdata->set1, 0); + } + + goto ramp_delay; + } + + if (ldo == MAX8698_BUCK2) { + if (min_vol == (desc->min + desc->step * pdata->dvsint2)) + gpio_set_value(pdata->set3, 1); + else + gpio_set_value(pdata->set3, 0); + + goto ramp_delay; + } + + if (max_vol < desc->min || min_vol > desc->max) + return -EINVAL; + + while (desc->min + desc->step*i < min_vol && + desc->min + desc->step*i < desc->max) + i++; + + if (desc->min + desc->step*i > max_vol) + return -EINVAL; + + *selector = i; + + ret = max8698_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + ret = max8698_update_reg(max8698->iodev, reg, i<<shift, mask<<shift); + +ramp_delay: + /* wait for RAMP_UP_DELAY if rdev is BUCK1/2 */ + if (ldo == MAX8698_BUCK1 || MAX8698_BUCK2) { + int difference = desc->min + desc->step*i - previous_vol/1000; + if (difference > 0) + udelay(difference / RAMP_DELAY_VAL); + } + + return ret; +} + +static struct regulator_ops max8698_ldo_ops = { + .list_voltage = max8698_list_voltage, + .is_enabled = max8698_ldo_is_enabled, + .enable = max8698_ldo_enable, + .disable = max8698_ldo_disable, + .get_voltage = max8698_get_voltage, + .set_voltage = max8698_set_voltage, + .set_suspend_enable = max8698_ldo_enable, + .set_suspend_disable = max8698_ldo_disable, +}; + +static struct regulator_ops max8698_buck_ops = { + .list_voltage = max8698_list_voltage, + .is_enabled = max8698_ldo_is_enabled, + .enable = max8698_ldo_enable, + .disable = max8698_ldo_disable, + .get_voltage = max8698_get_voltage, + .set_voltage = max8698_set_voltage, + .set_suspend_enable = max8698_ldo_enable, + .set_suspend_disable = max8698_ldo_disable, +}; + +static struct regulator_desc regulators[] = { + { + .name = "LDO1", + .id = MAX8698_LDO1, + .ops = &max8698_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO2", + .id = MAX8698_LDO2, + .ops = &max8698_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO3", + .id = MAX8698_LDO3, + .ops = &max8698_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO4", + .id = MAX8698_LDO4, + .ops = &max8698_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO5", + .id = MAX8698_LDO5, + .ops = &max8698_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO6", + .id = MAX8698_LDO6, + .ops = &max8698_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO7", + .id = MAX8698_LDO7, + .ops = &max8698_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO8", + .id = MAX8698_LDO8, + .ops = &max8698_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO9", + .id = MAX8698_LDO9, + .ops = &max8698_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK1", + .id = MAX8698_BUCK1, + .ops = &max8698_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK2", + .id = MAX8698_BUCK2, + .ops = &max8698_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK3", + .id = MAX8698_BUCK3, + .ops = &max8698_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + } +}; + +static __devinit int max8698_pmic_probe(struct platform_device *pdev) +{ + struct max8698_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max8698_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_dev **rdev; + struct max8698_data *max8698; + int i, ret, size; + + if (!pdata) { + dev_err(pdev->dev.parent, "No platform init data supplied\n"); + return -ENODEV; + } + + max8698 = kzalloc(sizeof(struct max8698_data), GFP_KERNEL); + if (!max8698) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * (pdata->num_regulators + 1); + max8698->rdev = kzalloc(size, GFP_KERNEL); + if (!max8698->rdev) { + kfree(max8698); + return -ENOMEM; + } + + rdev = max8698->rdev; + max8698->iodev = iodev; + platform_set_drvdata(pdev, max8698); + + for (i = 0; i < pdata->num_regulators; i++) { + const struct voltage_map_desc *desc; + int count; + int id = pdata->regulators[i].id; + int index = id - MAX8698_LDO1; + + desc = ldo_voltage_map[id]; + count = (desc->max - desc->min) / desc->step + 1; + regulators[index].n_voltages = count; + + rdev[i] = regulator_register(®ulators[index], max8698->dev, + pdata->regulators[i].initdata, max8698); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(max8698->dev, "regulator init failed\n"); + rdev[i] = NULL; + goto err; + } + } + + if (pdata->dvsarm1 && pdata->dvsarm2 && pdata->dvsarm3 && + pdata->dvsarm4 && gpio_is_valid(pdata->set1) && + gpio_is_valid(pdata->set2)) { + max8698_write_reg(iodev, MAX8698_REG_DVSARM12, + pdata->dvsarm1 | (pdata->dvsarm2 << 4)); + max8698_write_reg(iodev, MAX8698_REG_DVSARM34, + pdata->dvsarm3 | (pdata->dvsarm4 << 4)); + + ret = gpio_request(pdata->set1, "MAX8698 SET1"); + if (ret) + goto err; + gpio_direction_output(pdata->set1, 0); + ret = gpio_request(pdata->set2, "MAX8698 SET2"); + if (ret) + goto out_set2; + gpio_direction_output(pdata->set2, 0); + } + + if (pdata->dvsint1 && pdata->dvsint2 && gpio_is_valid(pdata->set3)) { + max8698_write_reg(iodev, MAX8698_REG_DVSINT12, + pdata->dvsint1 | (pdata->dvsint2 << 4)); + + ret = gpio_request(pdata->set3, "MAX8698 SET3"); + if (ret) + goto out_set3; + gpio_direction_output(pdata->set3, 0); + } + + return 0; + +out_set3: + gpio_free(S5PV210_GPH1(7)); + +out_set2: + gpio_free(S5PV210_GPH1(6)); +err: + for (i = 0; i <= max8698->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + kfree(max8698->rdev); + kfree(max8698); + + return ret; +} + +static int __devexit max8698_pmic_remove(struct platform_device *pdev) +{ + struct max8698_data *max8698 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max8698->rdev; + int i; + + for (i = 0; i <= max8698->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + kfree(max8698->rdev); + kfree(max8698); + + return 0; +} + +static struct platform_driver max8698_pmic_driver = { + .driver = { + .name = "max8698-pmic", + .owner = THIS_MODULE, + }, + .probe = max8698_pmic_probe, + .remove = __devexit_p(max8698_pmic_remove), +}; + +static int __init max8698_pmic_init(void) +{ + return platform_driver_register(&max8698_pmic_driver); +} +subsys_initcall(max8698_pmic_init); + +static void __exit max8698_pmic_cleanup(void) +{ + platform_driver_unregister(&max8698_pmic_driver); +} +module_exit(max8698_pmic_cleanup); + +MODULE_DESCRIPTION("MAXIM 8698 voltage regulator driver"); diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index ad6628ca94f..62065332296 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -1,8 +1,9 @@ /* - * max8997.c - Regulator driver for the Maxim 8997/8966 + * max8997.c - Voltage regulator driver for the Maxim 8997 * - * Copyright (C) 2011 Samsung Electronics - * MyungJoo Ham <myungjoo.ham@smasung.com> + * Copyright (C) 2009-2010 Samsung Electronics + * + * based on max8998.c * * 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 @@ -17,180 +18,149 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * This driver is based on max8998.c */ -#include <linux/bug.h> -#include <linux/delay.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> #include <linux/err.h> #include <linux/gpio.h> #include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/delay.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> -#include <linux/regulator/machine.h> #include <linux/mfd/max8997.h> #include <linux/mfd/max8997-private.h> +#include <mach/sec_debug.h> + struct max8997_data { - struct device *dev; - struct max8997_dev *iodev; - int num_regulators; - struct regulator_dev **rdev; - int ramp_delay; /* in mV/us */ - - bool buck1_gpiodvs; - bool buck2_gpiodvs; - bool buck5_gpiodvs; - u8 buck1_vol[8]; - u8 buck2_vol[8]; - u8 buck5_vol[8]; - int buck125_gpios[3]; - int buck125_gpioindex; - bool ignore_gpiodvs_side_effect; - - u8 saved_states[MAX8997_REG_MAX]; + struct device *dev; + struct max8997_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; + bool buck1_gpiodvs; + int buck_set1; + int buck_set2; + int buck_set3; + u8 buck1_vol[8]; /* voltages for selection */ + unsigned int buck1_idx; /* index to last changed voltage */ + /* value in a set */ + bool buck_ramp_en; + int buck_ramp_delay; + struct max8997_buck1_dvs_funcs funcs; + struct mutex dvs_lock; }; -static inline void max8997_set_gpio(struct max8997_data *max8997) -{ - int set3 = (max8997->buck125_gpioindex) & 0x1; - int set2 = ((max8997->buck125_gpioindex) >> 1) & 0x1; - int set1 = ((max8997->buck125_gpioindex) >> 2) & 0x1; - - gpio_set_value(max8997->buck125_gpios[0], set1); - gpio_set_value(max8997->buck125_gpios[1], set2); - gpio_set_value(max8997->buck125_gpios[2], set3); -} - -struct voltage_map_desc { +struct vol_cur_map_desc { int min; int max; int step; - unsigned int n_bits; }; -/* Voltage maps in mV */ -static const struct voltage_map_desc ldo_voltage_map_desc = { - .min = 800, .max = 3950, .step = 50, .n_bits = 6, -}; /* LDO1 ~ 18, 21 all */ - -static const struct voltage_map_desc buck1245_voltage_map_desc = { - .min = 650, .max = 2225, .step = 25, .n_bits = 6, -}; /* Buck1, 2, 4, 5 */ - -static const struct voltage_map_desc buck37_voltage_map_desc = { - .min = 750, .max = 3900, .step = 50, .n_bits = 6, -}; /* Buck3, 7 */ - -/* current map in mA */ -static const struct voltage_map_desc charger_current_map_desc = { - .min = 200, .max = 950, .step = 50, .n_bits = 4, +/* Voltage maps */ +static const struct vol_cur_map_desc ldos_vol_cur_map_desc = { + .min = 800, .step = 50, .max = 3950, }; - -static const struct voltage_map_desc topoff_current_map_desc = { - .min = 50, .max = 200, .step = 10, .n_bits = 4, +static const struct vol_cur_map_desc buck1245_vol_cur_map_desc = { + .min = 650, .step = 25, .max = 2225, +}; +static const struct vol_cur_map_desc buck37_vol_cur_map_desc = { + .min = 750, .step = 50, .max = 3900, }; -static const struct voltage_map_desc *reg_voltage_map[] = { - [MAX8997_LDO1] = &ldo_voltage_map_desc, - [MAX8997_LDO2] = &ldo_voltage_map_desc, - [MAX8997_LDO3] = &ldo_voltage_map_desc, - [MAX8997_LDO4] = &ldo_voltage_map_desc, - [MAX8997_LDO5] = &ldo_voltage_map_desc, - [MAX8997_LDO6] = &ldo_voltage_map_desc, - [MAX8997_LDO7] = &ldo_voltage_map_desc, - [MAX8997_LDO8] = &ldo_voltage_map_desc, - [MAX8997_LDO9] = &ldo_voltage_map_desc, - [MAX8997_LDO10] = &ldo_voltage_map_desc, - [MAX8997_LDO11] = &ldo_voltage_map_desc, - [MAX8997_LDO12] = &ldo_voltage_map_desc, - [MAX8997_LDO13] = &ldo_voltage_map_desc, - [MAX8997_LDO14] = &ldo_voltage_map_desc, - [MAX8997_LDO15] = &ldo_voltage_map_desc, - [MAX8997_LDO16] = &ldo_voltage_map_desc, - [MAX8997_LDO17] = &ldo_voltage_map_desc, - [MAX8997_LDO18] = &ldo_voltage_map_desc, - [MAX8997_LDO21] = &ldo_voltage_map_desc, - [MAX8997_BUCK1] = &buck1245_voltage_map_desc, - [MAX8997_BUCK2] = &buck1245_voltage_map_desc, - [MAX8997_BUCK3] = &buck37_voltage_map_desc, - [MAX8997_BUCK4] = &buck1245_voltage_map_desc, - [MAX8997_BUCK5] = &buck1245_voltage_map_desc, - [MAX8997_BUCK6] = NULL, - [MAX8997_BUCK7] = &buck37_voltage_map_desc, - [MAX8997_EN32KHZ_AP] = NULL, - [MAX8997_EN32KHZ_CP] = NULL, - [MAX8997_ENVICHG] = NULL, - [MAX8997_ESAFEOUT1] = NULL, - [MAX8997_ESAFEOUT2] = NULL, - [MAX8997_CHARGER_CV] = NULL, - [MAX8997_CHARGER] = &charger_current_map_desc, - [MAX8997_CHARGER_TOPOFF] = &topoff_current_map_desc, +/* flash currents just aren't matching up right! */ +static const struct vol_cur_map_desc flash_vol_cur_map_desc = { + .min = 23440, .step = 23440, .max = 750080, +}; +static const struct vol_cur_map_desc movie_vol_cur_map_desc = { + .min = 15625, .step = 15625, .max = 250000, +}; +#ifdef MAX8997_SUPPORT_TORCH +static const struct vol_cur_map_desc torch_vol_cur_map_desc = { + .min = 15625, .step = 15625, .max = 250000, +}; +#endif /* MAX8997_SUPPORT_TORCH */ + +static const struct vol_cur_map_desc *ldo_vol_cur_map[] = { + NULL, + &ldos_vol_cur_map_desc, /* LDO1 */ + &ldos_vol_cur_map_desc, /* LDO2 */ + &ldos_vol_cur_map_desc, /* LDO3 */ + &ldos_vol_cur_map_desc, /* LDO4 */ + &ldos_vol_cur_map_desc, /* LDO5 */ + &ldos_vol_cur_map_desc, /* LDO6 */ + &ldos_vol_cur_map_desc, /* LDO7 */ + &ldos_vol_cur_map_desc, /* LDO8 */ + &ldos_vol_cur_map_desc, /* LDO9 */ + &ldos_vol_cur_map_desc, /* LDO10 */ + &ldos_vol_cur_map_desc, /* LDO11 */ + &ldos_vol_cur_map_desc, /* LDO12 */ + &ldos_vol_cur_map_desc, /* LDO13 */ + &ldos_vol_cur_map_desc, /* LDO14 */ + &ldos_vol_cur_map_desc, /* LDO15 */ + &ldos_vol_cur_map_desc, /* LDO16 */ + &ldos_vol_cur_map_desc, /* LDO17 */ + &ldos_vol_cur_map_desc, /* LDO18 */ + &ldos_vol_cur_map_desc, /* LDO21 */ + &buck1245_vol_cur_map_desc, /* BUCK1 */ + &buck1245_vol_cur_map_desc, /* BUCK2 */ + &buck37_vol_cur_map_desc, /* BUCK3 */ + &buck1245_vol_cur_map_desc, /* BUCK4 */ + &buck1245_vol_cur_map_desc, /* BUCK5 */ + NULL, /* BUCK6 */ + &buck37_vol_cur_map_desc, /* BUCK7 */ + NULL, /* EN32KH_AP */ + NULL, /* EN32KH_CP */ + NULL, /* ENVICHG */ + NULL, /* ESAFEOUT1 */ + NULL, /* ESAFEOUT2 */ + &flash_vol_cur_map_desc, /* FLASH_EN */ + &movie_vol_cur_map_desc, /* MOVIE_EN */ +#ifdef MAX8997_SUPPORT_TORCH + &torch_vol_cur_map_desc, /* TORCH */ +#endif /* MAX8997_SUPPORT_TORCH */ }; -static inline int max8997_get_rid(struct regulator_dev *rdev) +static inline int max8997_get_ldo(struct regulator_dev *rdev) { return rdev_get_id(rdev); } -static int max8997_list_voltage_safeout(struct regulator_dev *rdev, - unsigned int selector) +static int max8997_list_voltage(struct regulator_dev *rdev, + unsigned int selector) { - int rid = max8997_get_rid(rdev); - - if (rid == MAX8997_ESAFEOUT1 || rid == MAX8997_ESAFEOUT2) { - switch (selector) { - case 0: - return 4850000; - case 1: - return 4900000; - case 2: - return 4950000; - case 3: - return 3300000; - default: - return -EINVAL; - } - } + const struct vol_cur_map_desc *desc; + int ldo = max8997_get_ldo(rdev); + int val; - return -EINVAL; -} + if (ldo >= ARRAY_SIZE(ldo_vol_cur_map)) + return -EINVAL; -static int max8997_list_voltage_charger_cv(struct regulator_dev *rdev, - unsigned int selector) -{ - int rid = max8997_get_rid(rdev); - - if (rid != MAX8997_CHARGER_CV) - goto err; - - switch (selector) { - case 0x00: - return 4200000; - case 0x01 ... 0x0E: - return 4000000 + 20000 * (selector - 0x01); - case 0x0F: - return 4350000; - default: + desc = ldo_vol_cur_map[ldo]; + if (desc == NULL) return -EINVAL; - } -err: - return -EINVAL; + + val = desc->min + desc->step * selector; + if (val > desc->max) + return -EINVAL; + + return val * 1000; } -static int max8997_list_voltage(struct regulator_dev *rdev, - unsigned int selector) +static int max8997_list_current(struct regulator_dev *rdev, + unsigned int selector) { - const struct voltage_map_desc *desc; - int rid = max8997_get_rid(rdev); + const struct vol_cur_map_desc *desc; + int co = max8997_get_ldo(rdev); int val; - if (rid >= ARRAY_SIZE(reg_voltage_map) || - rid < 0) + if (co >= ARRAY_SIZE(ldo_vol_cur_map)) return -EINVAL; - desc = reg_voltage_map[rid]; + desc = ldo_vol_cur_map[co]; if (desc == NULL) return -EINVAL; @@ -198,176 +168,194 @@ static int max8997_list_voltage(struct regulator_dev *rdev, if (val > desc->max) return -EINVAL; - return val * 1000; + return val; } static int max8997_get_enable_register(struct regulator_dev *rdev, - int *reg, int *mask, int *pattern) + int *reg, int *shift) { - int rid = max8997_get_rid(rdev); + int ldo = max8997_get_ldo(rdev); - switch (rid) { + switch (ldo) { case MAX8997_LDO1 ... MAX8997_LDO21: - *reg = MAX8997_REG_LDO1CTRL + (rid - MAX8997_LDO1); - *mask = 0xC0; - *pattern = 0xC0; + *reg = MAX8997_REG_LDO1CTRL + (ldo - MAX8997_LDO1); + *shift = 6; break; case MAX8997_BUCK1: *reg = MAX8997_REG_BUCK1CTRL; - *mask = 0x01; - *pattern = 0x01; + *shift = 0; break; case MAX8997_BUCK2: *reg = MAX8997_REG_BUCK2CTRL; - *mask = 0x01; - *pattern = 0x01; + *shift = 0; break; case MAX8997_BUCK3: *reg = MAX8997_REG_BUCK3CTRL; - *mask = 0x01; - *pattern = 0x01; + *shift = 0; break; case MAX8997_BUCK4: *reg = MAX8997_REG_BUCK4CTRL; - *mask = 0x01; - *pattern = 0x01; + *shift = 0; break; case MAX8997_BUCK5: *reg = MAX8997_REG_BUCK5CTRL; - *mask = 0x01; - *pattern = 0x01; + *shift = 0; break; case MAX8997_BUCK6: - *reg = MAX8997_REG_BUCK6CTRL; - *mask = 0x01; - *pattern = 0x01; + *reg = MAX8997_REG_BUCK6CTRL1; + *shift = 0; break; case MAX8997_BUCK7: *reg = MAX8997_REG_BUCK7CTRL; - *mask = 0x01; - *pattern = 0x01; + *shift = 0; break; case MAX8997_EN32KHZ_AP ... MAX8997_EN32KHZ_CP: - *reg = MAX8997_REG_MAINCON1; - *mask = 0x01 << (rid - MAX8997_EN32KHZ_AP); - *pattern = 0x01 << (rid - MAX8997_EN32KHZ_AP); + *reg = MAX8997_REG_CONTROL1; + *shift = 0 + (ldo - MAX8997_EN32KHZ_AP); break; case MAX8997_ENVICHG: *reg = MAX8997_REG_MBCCTRL1; - *mask = 0x80; - *pattern = 0x80; + *shift = 7; break; case MAX8997_ESAFEOUT1 ... MAX8997_ESAFEOUT2: *reg = MAX8997_REG_SAFEOUTCTRL; - *mask = 0x40 << (rid - MAX8997_ESAFEOUT1); - *pattern = 0x40 << (rid - MAX8997_ESAFEOUT1); - break; - case MAX8997_CHARGER: - *reg = MAX8997_REG_MBCCTRL2; - *mask = 0x40; - *pattern = 0x40; + *shift = 6 + (ldo - MAX8997_ESAFEOUT1); break; default: - /* Not controllable or not exists */ return -EINVAL; } return 0; } -static int max8997_reg_is_enabled(struct regulator_dev *rdev) +static int max8997_get_enable_mask(struct regulator_dev *rdev) +{ + int ret = 0; + int ldo = max8997_get_ldo(rdev); + + switch (ldo) { + case MAX8997_LDO1 ... MAX8997_LDO21: + ret = 3; + break; + case MAX8997_BUCK1 ... MAX8997_ESAFEOUT2: + ret = 1; + break; + default: + break; + } + + return ret; +} + +static int max8997_get_disable_val(struct regulator_dev *rdev) +{ + int ret = 0; + int ldo = max8997_get_ldo(rdev); + + switch (ldo) { + case MAX8997_LDO1: + case MAX8997_LDO10: + case MAX8997_LDO21: + ret = 1; + break; + default: + ret = 0; + break; + } + + return ret; +} + +static int max8997_ldo_is_enabled(struct regulator_dev *rdev) { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; - int ret, reg, mask, pattern; - u8 val; + int ret, reg, shift = 8; + u8 val, mask; - ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); - if (ret == -EINVAL) - return 1; /* "not controllable" */ - else if (ret) + ret = max8997_get_enable_register(rdev, ®, &shift); + if (ret < 0) return ret; ret = max8997_read_reg(i2c, reg, &val); - if (ret) + if (ret < 0) return ret; - return (val & mask) == pattern; + mask = max8997_get_enable_mask(rdev); + + return val & (mask << shift); } -static int max8997_reg_enable(struct regulator_dev *rdev) +static int max8997_ldo_enable(struct regulator_dev *rdev) { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; - int ret, reg, mask, pattern; + int reg, shift = 8, ret; + u8 mask; - ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); - if (ret) + ret = max8997_get_enable_register(rdev, ®, &shift); + if (ret < 0) return ret; - return max8997_update_reg(i2c, reg, pattern, mask); + mask = max8997_get_enable_mask(rdev); + + return max8997_update_reg(i2c, reg, mask<<shift, mask<<shift); } -static int max8997_reg_disable(struct regulator_dev *rdev) +static int max8997_ldo_disable(struct regulator_dev *rdev) { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; - int ret, reg, mask, pattern; + int reg, shift = 8, ret, val; + u8 mask; - ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); - if (ret) + ret = max8997_get_enable_register(rdev, ®, &shift); + if (ret < 0) return ret; - return max8997_update_reg(i2c, reg, ~pattern, mask); + mask = max8997_get_enable_mask(rdev); + val = max8997_get_disable_val(rdev); + + return max8997_update_reg(i2c, reg, val<<shift, mask<<shift); +} + +static int max8997_ldo_suspend_enable(struct regulator_dev *rdev) +{ + if (rdev->use_count > 0) + return max8997_ldo_enable(rdev); + else + return max8997_ldo_disable(rdev); } static int max8997_get_voltage_register(struct regulator_dev *rdev, - int *_reg, int *_shift, int *_mask) + int *_reg, int *_shift, int *_mask) { - int rid = max8997_get_rid(rdev); - int reg, shift = 0, mask = 0x3f; + int ldo = max8997_get_ldo(rdev); + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + int reg, shift = 0, mask = 0xff; - switch (rid) { + switch (ldo) { case MAX8997_LDO1 ... MAX8997_LDO21: - reg = MAX8997_REG_LDO1CTRL + (rid - MAX8997_LDO1); + reg = MAX8997_REG_LDO1CTRL + (ldo - MAX8997_LDO1); + mask = 0x3f; break; case MAX8997_BUCK1: - reg = MAX8997_REG_BUCK1DVS1; + reg = MAX8997_REG_BUCK1DVSTV1 + max8997->buck1_idx; break; case MAX8997_BUCK2: - reg = MAX8997_REG_BUCK2DVS1; + reg = MAX8997_REG_BUCK2DVSTV1 + 1; break; case MAX8997_BUCK3: - reg = MAX8997_REG_BUCK3DVS; + reg = MAX8997_REG_BUCK3DVSTV; break; case MAX8997_BUCK4: - reg = MAX8997_REG_BUCK4DVS; + reg = MAX8997_REG_BUCK4DVSTV; break; case MAX8997_BUCK5: - reg = MAX8997_REG_BUCK5DVS1; + reg = MAX8997_REG_BUCK5DVSTV1 + 1; break; case MAX8997_BUCK7: - reg = MAX8997_REG_BUCK7DVS; - break; - case MAX8997_ESAFEOUT1 ... MAX8997_ESAFEOUT2: - reg = MAX8997_REG_SAFEOUTCTRL; - shift = (rid == MAX8997_ESAFEOUT2) ? 2 : 0; - mask = 0x3; - break; - case MAX8997_CHARGER_CV: - reg = MAX8997_REG_MBCCTRL3; - shift = 0; - mask = 0xf; - break; - case MAX8997_CHARGER: - reg = MAX8997_REG_MBCCTRL4; - shift = 0; - mask = 0xf; - break; - case MAX8997_CHARGER_TOPOFF: - reg = MAX8997_REG_MBCCTRL5; - shift = 0; - mask = 0xf; + reg = MAX8997_REG_BUCK7DVSTV; break; default: return -EINVAL; @@ -384,578 +372,841 @@ static int max8997_get_voltage(struct regulator_dev *rdev) { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; - int reg, shift, mask, ret; - int rid = max8997_get_rid(rdev); + int reg, shift = 0, mask, ret; u8 val; ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); - if (ret) + if (ret < 0) return ret; - if ((rid == MAX8997_BUCK1 && max8997->buck1_gpiodvs) || - (rid == MAX8997_BUCK2 && max8997->buck2_gpiodvs) || - (rid == MAX8997_BUCK5 && max8997->buck5_gpiodvs)) - reg += max8997->buck125_gpioindex; - ret = max8997_read_reg(i2c, reg, &val); - if (ret) + if (ret < 0) return ret; val >>= shift; val &= mask; - if (rdev->desc && rdev->desc->ops && rdev->desc->ops->list_voltage) - return rdev->desc->ops->list_voltage(rdev, val); - - /* - * max8997_list_voltage returns value for any rdev with voltage_map, - * which works for "CHARGER" and "CHARGER TOPOFF" that do not have - * list_voltage ops (they are current regulators). - */ return max8997_list_voltage(rdev, val); } -static inline int max8997_get_voltage_proper_val( - const struct voltage_map_desc *desc, - int min_vol, int max_vol) +static int max8997_set_voltage_ldo(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) { + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int min_vol = min_uV / 1000, max_vol = max_uV / 1000; + const struct vol_cur_map_desc *desc; + int ldo = max8997_get_ldo(rdev); + int reg, shift = 0, mask, ret; int i = 0; + if (ldo >= ARRAY_SIZE(ldo_vol_cur_map)) + return -EINVAL; + + desc = ldo_vol_cur_map[ldo]; if (desc == NULL) return -EINVAL; if (max_vol < desc->min || min_vol > desc->max) return -EINVAL; - while (desc->min + desc->step * i < min_vol && - desc->min + desc->step * i < desc->max) + while (desc->min + desc->step*i < min_vol && + desc->min + desc->step*i < desc->max) i++; - if (desc->min + desc->step * i > max_vol) - return -EINVAL; - - if (i >= (1 << desc->n_bits)) - return -EINVAL; - - return i; -} - -static int max8997_set_voltage_charger_cv(struct regulator_dev *rdev, - int min_uV, int max_uV, unsigned *selector) -{ - struct max8997_data *max8997 = rdev_get_drvdata(rdev); - struct i2c_client *i2c = max8997->iodev->i2c; - int rid = max8997_get_rid(rdev); - int lb, ub; - int reg, shift = 0, mask, ret = 0; - u8 val = 0x0; - - if (rid != MAX8997_CHARGER_CV) + if (desc->min + desc->step*i > max_vol) return -EINVAL; ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); - if (ret) + if (ret < 0) return ret; - if (max_uV < 4000000 || min_uV > 4350000) - return -EINVAL; + ret = max8997_update_reg(i2c, reg, i<<shift, mask<<shift); + *selector = i; - if (min_uV <= 4000000) { - if (max_uV >= 4000000) - return -EINVAL; - else - val = 0x1; - } else if (min_uV <= 4200000 && max_uV >= 4200000) - val = 0x0; - else { - lb = (min_uV - 4000001) / 20000 + 2; - ub = (max_uV - 4000000) / 20000 + 1; - - if (lb > ub) - return -EINVAL; + return ret; +} - if (lb < 0xf) - val = lb; - else { - if (ub >= 0xf) - val = 0xf; - else - return -EINVAL; - } +static inline void buck1_gpio_set(struct max8997_data *max8997, int v) +{ + static int prev_v = 0x1; + + int gpio1 = max8997->buck_set1; + int gpio2 = max8997->buck_set2; + int gpio3 = max8997->buck_set3; + + if (prev_v > v) { + /* raise the volage */ + gpio_set_value(gpio3, (v >> 2) & 0x1); + udelay(40); + gpio_set_value(gpio2, (v >> 1) & 0x1); + udelay(40); + gpio_set_value(gpio1, v & 0x1); + } else { + gpio_set_value(gpio1, v & 0x1); + udelay(40); + gpio_set_value(gpio2, (v >> 1) & 0x1); + udelay(40); + gpio_set_value(gpio3, (v >> 2) & 0x1); } - *selector = val; - - ret = max8997_update_reg(i2c, reg, val << shift, mask); - - return ret; + prev_v = v; } -/* - * For LDO1 ~ LDO21, BUCK1~5, BUCK7, CHARGER, CHARGER_TOPOFF - * BUCK1, 2, and 5 are available if they are not controlled by gpio - */ -static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev, - int min_uV, int max_uV, unsigned *selector) +static int max8997_set_voltage_buck(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; + int i = 0, j, k; int min_vol = min_uV / 1000, max_vol = max_uV / 1000; - const struct voltage_map_desc *desc; - int rid = max8997_get_rid(rdev); + const struct vol_cur_map_desc *desc; + int buck = max8997_get_ldo(rdev); int reg, shift = 0, mask, ret; - int i; - u8 org; + int difference = 0, previous_vol = 0, current_vol = 0; + u8 data[7]; - switch (rid) { - case MAX8997_LDO1 ... MAX8997_LDO21: - break; - case MAX8997_BUCK1 ... MAX8997_BUCK5: - break; - case MAX8997_BUCK6: - return -EINVAL; - case MAX8997_BUCK7: - break; - case MAX8997_CHARGER: - break; - case MAX8997_CHARGER_TOPOFF: - break; - default: - return -EINVAL; + mutex_lock(&max8997->dvs_lock); + + if (buck >= ARRAY_SIZE(ldo_vol_cur_map)) { + ret = -EINVAL; + goto out; } - desc = reg_voltage_map[rid]; + desc = ldo_vol_cur_map[buck]; - i = max8997_get_voltage_proper_val(desc, min_vol, max_vol); - if (i < 0) - return i; + if (desc == NULL) { + ret = -EINVAL; + goto out; + } - ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); - if (ret) - return ret; + if (max_vol < desc->min || min_vol > desc->max) { + ret = -EINVAL; + goto out; + } - max8997_read_reg(i2c, reg, &org); - org = (org & mask) >> shift; + while (desc->min + desc->step*i < min_vol && + desc->min + desc->step*i < desc->max) + i++; - ret = max8997_update_reg(i2c, reg, i << shift, mask << shift); *selector = i; - if (rid == MAX8997_BUCK1 || rid == MAX8997_BUCK2 || - rid == MAX8997_BUCK4 || rid == MAX8997_BUCK5) { - /* If the voltage is increasing */ - if (org < i) - udelay(DIV_ROUND_UP(desc->step * (i - org), - max8997->ramp_delay)); + if (desc->min + desc->step*i > max_vol) { + ret = -EINVAL; + goto out; } - return ret; -} + ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); + if (ret < 0) + goto out; -/* - * Assess the damage on the voltage setting of BUCK1,2,5 by the change. - * - * When GPIO-DVS mode is used for multiple bucks, changing the voltage value - * of one of the bucks may affect that of another buck, which is the side - * effect of the change (set_voltage). This function examines the GPIO-DVS - * configurations and checks whether such side-effect exists. - */ -static int max8997_assess_side_effect(struct regulator_dev *rdev, - u8 new_val, int *best) -{ - struct max8997_data *max8997 = rdev_get_drvdata(rdev); - int rid = max8997_get_rid(rdev); - u8 *buckx_val[3]; - bool buckx_gpiodvs[3]; - int side_effect[8]; - int min_side_effect = INT_MAX; - int i; + previous_vol = max8997_get_voltage(rdev); - *best = -1; + /* Check if voltage needs to be changed */ + /* if previous_voltage equal new voltage, return */ + if (previous_vol == max8997_list_voltage(rdev, i)) { + dev_dbg(max8997->dev, "No voltage change, old:%d, new:%d\n", + previous_vol, max8997_list_voltage(rdev, i)); + ret = 0; + goto out; + } - switch (rid) { + switch (buck) { case MAX8997_BUCK1: - rid = 0; + sec_debug_aux_log(SEC_DEBUG_AUXLOG_CPU_BUS_CLOCK_CHANGE, + "%s: BUCK1: min_vol=%d, max_vol=%d(%ps)", + __func__, min_vol, max_vol, + __builtin_return_address(0)); + + if (!max8997->buck1_gpiodvs) { + ret = max8997_write_reg(i2c, reg, i); + break; + } + + if (gpio_is_valid(max8997->buck_set1) && + gpio_is_valid(max8997->buck_set2) && + gpio_is_valid(max8997->buck_set3)) { + /* check if requested voltage */ + /* value is already defined */ + for (j = 1; j < ARRAY_SIZE(max8997->buck1_vol); j++) { + if (max8997->buck1_vol[j] == i) { + max8997->buck1_idx = j; + buck1_gpio_set(max8997, j); + goto buck1_exit; + } + } + + /* no predefine regulator found */ + dev_warn(max8997->dev, "BUCK1 no predefined:%d,%d\n", + min_vol, max_vol); + max8997->buck1_idx = 7; + max8997->buck1_vol[max8997->buck1_idx] = i; + ret = max8997_get_voltage_register(rdev, ®, &shift, + &mask); + if (ret < 0) + break; + + ret = max8997_write_reg(i2c, reg, i); + if (ret < 0) + break; + + buck1_gpio_set(max8997, max8997->buck1_idx); +buck1_exit: + dev_dbg(max8997->dev, "%s: SET3:%d,SET2:%d,SET1:%d\n", + __func__, gpio_get_value(max8997->buck_set3), + gpio_get_value(max8997->buck_set2), + gpio_get_value(max8997->buck_set1)); + break; + } else + ret = max8997_write_reg(i2c, reg, i); break; case MAX8997_BUCK2: - rid = 1; - break; + sec_debug_aux_log(SEC_DEBUG_AUXLOG_CPU_BUS_CLOCK_CHANGE, + "%s: BUCK2: min_vol=%d, max_vol=%d(%ps)", + __func__, min_vol, max_vol, + __builtin_return_address(0)); case MAX8997_BUCK5: - rid = 2; + for (k = 0; k < 7; k++) + data[k] = i; + ret = max8997_bulk_write(i2c, reg, 7, data); + break; + case MAX8997_BUCK3: + case MAX8997_BUCK4: + case MAX8997_BUCK7: + ret = max8997_write_reg(i2c, reg, i); break; default: - return -EINVAL; + ret = -EINVAL; + break; } - buckx_val[0] = max8997->buck1_vol; - buckx_val[1] = max8997->buck2_vol; - buckx_val[2] = max8997->buck5_vol; - buckx_gpiodvs[0] = max8997->buck1_gpiodvs; - buckx_gpiodvs[1] = max8997->buck2_gpiodvs; - buckx_gpiodvs[2] = max8997->buck5_gpiodvs; + if (ret < 0) { + dev_err(max8997->dev, "Failed to write buck%d voltage" + "register: %d\n", buck - MAX8997_BUCK1 + 1, ret); + goto out; + } - for (i = 0; i < 8; i++) { - int others; + if (!max8997->buck_ramp_en) + goto out; - if (new_val != (buckx_val[rid])[i]) { - side_effect[i] = -1; - continue; - } + current_vol = desc->min + desc->step*i; + if (previous_vol/1000 < current_vol) + difference = current_vol - previous_vol/1000; + else + difference = previous_vol/1000 - current_vol; - side_effect[i] = 0; - for (others = 0; others < 3; others++) { - int diff; + udelay(difference / max8997->buck_ramp_delay); +out: + mutex_unlock(&max8997->dvs_lock); + return ret; +} - if (others == rid) - continue; - if (buckx_gpiodvs[others] == false) - continue; /* Not affected */ - diff = (buckx_val[others])[i] - - (buckx_val[others])[max8997->buck125_gpioindex]; - if (diff > 0) - side_effect[i] += diff; - else if (diff < 0) - side_effect[i] -= diff; - } - if (side_effect[i] == 0) { - *best = i; - return 0; /* NO SIDE EFFECT! Use This! */ - } - if (side_effect[i] < min_side_effect) { - min_side_effect = side_effect[i]; - *best = i; - } - } +static int max8997_flash_is_enabled(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int ret; + u8 val, mask; - if (*best == -1) + switch (max8997_get_ldo(rdev)) { + case MAX8997_FLASH_CUR: + mask = MAX8997_FLASH_EN_MASK; + break; +#ifdef MAX8997_SUPPORT_TORCH + case MAX8997_FLASH_TORCH: +#endif /* MAX8997_FLASH_TORCH */ + case MAX8997_MOVIE_CUR: + mask = MAX8997_MOVIE_EN_MASK; + break; + default: return -EINVAL; + } - return side_effect[*best]; + ret = max8997_read_reg(i2c, MAX8997_REG_LED_CNTL, &val); + if (ret < 0) + return ret; + + return val & mask; } -/* - * For Buck 1 ~ 5 and 7. If it is not controlled by GPIO, this calls - * max8997_set_voltage_ldobuck to do the job. - */ -static int max8997_set_voltage_buck(struct regulator_dev *rdev, - int min_uV, int max_uV, unsigned *selector) +static int max8997_flash_enable(struct regulator_dev *rdev) { struct max8997_data *max8997 = rdev_get_drvdata(rdev); - int rid = max8997_get_rid(rdev); - const struct voltage_map_desc *desc; - int new_val, new_idx, damage, tmp_val, tmp_idx, tmp_dmg; - bool gpio_dvs_mode = false; - int min_vol = min_uV / 1000, max_vol = max_uV / 1000; + struct i2c_client *i2c = max8997->iodev->i2c; + int ret; - if (rid < MAX8997_BUCK1 || rid > MAX8997_BUCK7) - return -EINVAL; + ret = max8997_update_reg(i2c, MAX8997_REG_BOOST_CNTL, + 1 << MAX8997_BOOST_EN_SHIFT, MAX8997_BOOST_EN_MASK); + if (ret < 0) + return ret; - switch (rid) { - case MAX8997_BUCK1: - if (max8997->buck1_gpiodvs) - gpio_dvs_mode = true; + switch (max8997_get_ldo(rdev)) { + case MAX8997_FLASH_CUR: + ret = max8997_update_reg(i2c, MAX8997_REG_LED_CNTL, + 7 << MAX8997_FLASH_EN_SHIFT, MAX8997_FLASH_EN_MASK); break; - case MAX8997_BUCK2: - if (max8997->buck2_gpiodvs) - gpio_dvs_mode = true; + case MAX8997_MOVIE_CUR: + ret = max8997_update_reg(i2c, MAX8997_REG_LED_CNTL, + 7 << MAX8997_MOVIE_EN_SHIFT, MAX8997_MOVIE_EN_MASK); break; - case MAX8997_BUCK5: - if (max8997->buck5_gpiodvs) - gpio_dvs_mode = true; +#ifdef MAX8997_SUPPORT_TORCH + case MAX8997_FLASH_TORCH: + ret = max8997_update_reg(i2c, MAX8997_REG_LED_CNTL, + 3 << MAX8997_MOVIE_EN_SHIFT, MAX8997_MOVIE_EN_MASK); break; +#endif /* MAX8997_SUPPORT_TORCH */ + default: + return -EINVAL; } - if (!gpio_dvs_mode) - return max8997_set_voltage_ldobuck(rdev, min_uV, max_uV, - selector); - - desc = reg_voltage_map[rid]; - new_val = max8997_get_voltage_proper_val(desc, min_vol, max_vol); - if (new_val < 0) - return new_val; - - tmp_dmg = INT_MAX; - tmp_idx = -1; - tmp_val = -1; - do { - damage = max8997_assess_side_effect(rdev, new_val, &new_idx); - if (damage == 0) - goto out; - - if (tmp_dmg > damage) { - tmp_idx = new_idx; - tmp_val = new_val; - tmp_dmg = damage; - } - - new_val++; - } while (desc->min + desc->step + new_val <= desc->max); - - new_idx = tmp_idx; - new_val = tmp_val; + return ret; +} - if (max8997->ignore_gpiodvs_side_effect == false) - return -EINVAL; +static int max8997_flash_disable(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int ret; - dev_warn(&rdev->dev, "MAX8997 GPIO-DVS Side Effect Warning: GPIO SET:" - " %d -> %d\n", max8997->buck125_gpioindex, tmp_idx); + ret = max8997_update_reg(i2c, MAX8997_REG_LED_CNTL, + 0 << MAX8997_FLASH_EN_SHIFT, MAX8997_FLASH_EN_MASK); + if (ret < 0) + return ret; -out: - if (new_idx < 0 || new_val < 0) + switch (max8997_get_ldo(rdev)) { + case MAX8997_FLASH_CUR: + ret = max8997_update_reg(i2c, MAX8997_REG_LED_CNTL, + 0 << MAX8997_FLASH_EN_SHIFT, MAX8997_FLASH_EN_MASK); + break; +#ifdef MAX8997_SUPPORT_TORCH + case MAX8997_FLASH_TORCH: +#endif /* MAX8997_SUPPORT_TORCH */ + case MAX8997_MOVIE_CUR: + ret = max8997_update_reg(i2c, MAX8997_REG_LED_CNTL, + 0 << MAX8997_MOVIE_EN_SHIFT, MAX8997_MOVIE_EN_MASK); + break; + default: return -EINVAL; + } - max8997->buck125_gpioindex = new_idx; - max8997_set_gpio(max8997); - *selector = new_val; + if (ret < 0) + return ret; - return 0; + return max8997_update_reg(i2c, MAX8997_REG_BOOST_CNTL, + 0 << MAX8997_BOOST_EN_SHIFT, MAX8997_BOOST_EN_MASK); } -static const int safeoutvolt[] = { - 3300000, - 4850000, - 4900000, - 4950000, -}; - -/* For SAFEOUT1 and SAFEOUT2 */ -static int max8997_set_voltage_safeout(struct regulator_dev *rdev, - int min_uV, int max_uV, unsigned *selector) +static int max8997_flash_get_current(struct regulator_dev *rdev) { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; - int rid = max8997_get_rid(rdev); - int reg, shift = 0, mask, ret; - int i = 0; - u8 val; - - if (rid != MAX8997_ESAFEOUT1 && rid != MAX8997_ESAFEOUT2) + int ret; + u8 val, shift = 0; + + switch (max8997_get_ldo(rdev)) { + case MAX8997_FLASH_CUR: + /* Assume that FLED1 and FLED2 have same current setting */ + ret = max8997_read_reg(i2c, MAX8997_REG_FLASH1_CUR, &val); + shift = 3; + break; +#ifdef MAX8997_SUPPORT_TORCH + case MAX8997_FLASH_TORCH: +#endif /* MAX8997_SUPPORT_TORCH */ + case MAX8997_MOVIE_CUR: + ret = max8997_read_reg(i2c, MAX8997_REG_MOVIE_CUR, &val); + shift = 4; + break; + default: return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(safeoutvolt); i++) { - if (min_uV <= safeoutvolt[i] && - max_uV >= safeoutvolt[i]) - break; } - if (i >= ARRAY_SIZE(safeoutvolt)) - return -EINVAL; - - if (i == 0) - val = 0x3; - else - val = i - 1; - - ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); - if (ret) + if (ret < 0) return ret; - ret = max8997_update_reg(i2c, reg, val << shift, mask << shift); - *selector = val; - - return ret; -} + val >>= shift; -static int max8997_reg_enable_suspend(struct regulator_dev *rdev) -{ - return 0; + return max8997_list_current(rdev, val); } -static int max8997_reg_disable_suspend(struct regulator_dev *rdev) +static int max8997_flash_set_current(struct regulator_dev *rdev, + int min_uA, int max_uA) { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; - int ret, reg, mask, pattern; - int rid = max8997_get_rid(rdev); + int min_amp = min_uA, max_amp = max_uA; + const struct vol_cur_map_desc *desc; + int co = max8997_get_ldo(rdev); + int ret; + int i = 0; - ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); - if (ret) - return ret; + if (co >= ARRAY_SIZE(ldo_vol_cur_map)) + return -EINVAL; - max8997_read_reg(i2c, reg, &max8997->saved_states[rid]); + desc = ldo_vol_cur_map[co]; + if (desc == NULL) + return -EINVAL; + + if (max_amp < desc->min || min_amp > desc->max) + return -EINVAL; + + while (desc->min + desc->step*i < min_amp && + desc->min + desc->step*i < desc->max) + i++; - if (rid == MAX8997_LDO1 || - rid == MAX8997_LDO10 || - rid == MAX8997_LDO21) { - dev_dbg(&rdev->dev, "Conditional Power-Off for %s\n", - rdev->desc->name); - return max8997_update_reg(i2c, reg, 0x40, mask); + if (desc->min + desc->step*i > max_amp) + return -EINVAL; + + switch (co) { + case MAX8997_FLASH_CUR: + ret = max8997_write_reg(i2c, MAX8997_REG_FLASH1_CUR, i << 3); + if (ret < 0) + return ret; + ret = max8997_write_reg(i2c, MAX8997_REG_FLASH2_CUR, i << 3); + break; + case MAX8997_MOVIE_CUR: +#if 0 + ret = max8997_write_reg(i2c, MAX8997_REG_MOVIE_CUR, i << 4); +#else + /* Workaround: MOVIE_CUR setting was ignored because + * GSMB is always high. + */ + ret = max8997_update_reg(i2c, MAX8997_REG_GSMB_CUR, i << 2, + 0xf << 2); +#endif + break; +#ifdef MAX8997_SUPPORT_TORCH + case MAX8997_FLASH_TORCH: + ret = max8997_write_reg(i2c, MAX8997_REG_MOVIE_CUR, i << 4); + break; +#endif /* MAX8997_SUPPORT_TORCH */ + default: + return -EINVAL; } - dev_dbg(&rdev->dev, "Full Power-Off for %s (%xh -> %xh)\n", - rdev->desc->name, max8997->saved_states[rid] & mask, - (~pattern) & mask); - return max8997_update_reg(i2c, reg, ~pattern, mask); + return ret; } static struct regulator_ops max8997_ldo_ops = { .list_voltage = max8997_list_voltage, - .is_enabled = max8997_reg_is_enabled, - .enable = max8997_reg_enable, - .disable = max8997_reg_disable, + .is_enabled = max8997_ldo_is_enabled, + .enable = max8997_ldo_enable, + .disable = max8997_ldo_disable, .get_voltage = max8997_get_voltage, - .set_voltage = max8997_set_voltage_ldobuck, - .set_suspend_enable = max8997_reg_enable_suspend, - .set_suspend_disable = max8997_reg_disable_suspend, + .set_voltage = max8997_set_voltage_ldo, + .set_suspend_enable = max8997_ldo_suspend_enable, + .set_suspend_disable = max8997_ldo_disable, }; static struct regulator_ops max8997_buck_ops = { .list_voltage = max8997_list_voltage, - .is_enabled = max8997_reg_is_enabled, - .enable = max8997_reg_enable, - .disable = max8997_reg_disable, + .is_enabled = max8997_ldo_is_enabled, + .enable = max8997_ldo_enable, + .disable = max8997_ldo_disable, .get_voltage = max8997_get_voltage, .set_voltage = max8997_set_voltage_buck, - .set_suspend_enable = max8997_reg_enable_suspend, - .set_suspend_disable = max8997_reg_disable_suspend, + .set_suspend_enable = max8997_ldo_enable, + .set_suspend_disable = max8997_ldo_disable, }; -static struct regulator_ops max8997_fixedvolt_ops = { - .list_voltage = max8997_list_voltage, - .is_enabled = max8997_reg_is_enabled, - .enable = max8997_reg_enable, - .disable = max8997_reg_disable, - .set_suspend_enable = max8997_reg_enable_suspend, - .set_suspend_disable = max8997_reg_disable_suspend, +static struct regulator_ops max8997_flash_ops = { + .is_enabled = max8997_flash_is_enabled, + .enable = max8997_flash_enable, + .disable = max8997_flash_disable, + .set_current_limit = max8997_flash_set_current, + .get_current_limit = max8997_flash_get_current, + .set_suspend_enable = max8997_flash_enable, + .set_suspend_disable = max8997_flash_disable, }; -static struct regulator_ops max8997_safeout_ops = { - .list_voltage = max8997_list_voltage_safeout, - .is_enabled = max8997_reg_is_enabled, - .enable = max8997_reg_enable, - .disable = max8997_reg_disable, - .get_voltage = max8997_get_voltage, - .set_voltage = max8997_set_voltage_safeout, - .set_suspend_enable = max8997_reg_enable_suspend, - .set_suspend_disable = max8997_reg_disable_suspend, +static struct regulator_ops max8997_others_ops = { + .is_enabled = max8997_ldo_is_enabled, + .enable = max8997_ldo_enable, + .disable = max8997_ldo_disable, + .set_suspend_enable = max8997_ldo_enable, + .set_suspend_disable = max8997_ldo_disable, }; -static struct regulator_ops max8997_fixedstate_ops = { - .list_voltage = max8997_list_voltage_charger_cv, - .get_voltage = max8997_get_voltage, - .set_voltage = max8997_set_voltage_charger_cv, +static struct regulator_desc regulators[] = { + { + .name = "LDO1", + .id = MAX8997_LDO1, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO2", + .id = MAX8997_LDO2, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO3", + .id = MAX8997_LDO3, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO4", + .id = MAX8997_LDO4, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO5", + .id = MAX8997_LDO5, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO6", + .id = MAX8997_LDO6, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO7", + .id = MAX8997_LDO7, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO8", + .id = MAX8997_LDO8, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO9", + .id = MAX8997_LDO9, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO10", + .id = MAX8997_LDO10, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO11", + .id = MAX8997_LDO11, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO12", + .id = MAX8997_LDO12, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO13", + .id = MAX8997_LDO13, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO14", + .id = MAX8997_LDO14, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO15", + .id = MAX8997_LDO15, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO16", + .id = MAX8997_LDO16, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO17", + .id = MAX8997_LDO17, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO18", + .id = MAX8997_LDO18, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO21", + .id = MAX8997_LDO21, + .ops = &max8997_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK1", + .id = MAX8997_BUCK1, + .ops = &max8997_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK2", + .id = MAX8997_BUCK2, + .ops = &max8997_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK3", + .id = MAX8997_BUCK3, + .ops = &max8997_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK4", + .id = MAX8997_BUCK4, + .ops = &max8997_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK5", + .id = MAX8997_BUCK5, + .ops = &max8997_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK6", + .id = MAX8997_BUCK6, + .ops = &max8997_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK7", + .id = MAX8997_BUCK7, + .ops = &max8997_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz AP", + .id = MAX8997_EN32KHZ_AP, + .ops = &max8997_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz CP", + .id = MAX8997_EN32KHZ_CP, + .ops = &max8997_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ENVICHG", + .id = MAX8997_ENVICHG, + .ops = &max8997_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ESAFEOUT1", + .id = MAX8997_ESAFEOUT1, + .ops = &max8997_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ESAFEOUT2", + .id = MAX8997_ESAFEOUT2, + .ops = &max8997_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "FLASH_CUR", + .id = MAX8997_FLASH_CUR, + .ops = &max8997_flash_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, { + .name = "MOVIE_CUR", + .id = MAX8997_MOVIE_CUR, + .ops = &max8997_flash_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, +#ifdef MAX8997_SUPPORT_TORCH + }, { + .name = "FLASH_TORCH", + .id = MAX8997_FLASH_TORCH, + .ops = &max8997_flash_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, +#endif /* MAX8997_SUPPORT_TORCH */ + } }; -static int max8997_set_voltage_ldobuck_wrap(struct regulator_dev *rdev, - int min_uV, int max_uV) +static int max8997_set_buck_max_voltage(struct max8997_data *max8997, + int buck_no, unsigned int buck_max_vol) { - unsigned dummy; + struct i2c_client *i2c; + int i = 0, j; + u8 reg, data[8]; - return max8997_set_voltage_ldobuck(rdev, min_uV, max_uV, &dummy); + i2c = max8997->iodev->i2c; + + while (buck1245_vol_cur_map_desc.min + buck1245_vol_cur_map_desc.step*i + != (buck_max_vol / 1000)) { + i++; + if (i > 0x3f) { + dev_err(max8997->dev, "%s: invalid voltage(%d,%d)\n", + __func__, buck_no, buck_max_vol); + return -EINVAL; + } + } + + dev_info(max8997->dev, "%s: buck%d: %d, i:%d\n", __func__, buck_no, + buck_max_vol, i); + + switch (buck_no) { + case 1: + max8997->buck1_vol[0] = i; + reg = MAX8997_REG_BUCK1DVSTV1; + break; + case 2: + reg = MAX8997_REG_BUCK2DVSTV1; + break; + case 5: + reg = MAX8997_REG_BUCK5DVSTV1; + break; + default: + pr_err("%s: wrong BUCK number(%d)!\n", __func__, buck_no); + return -EINVAL; + } + + for (j = 0; j < 8; j++) + data[j] = i; + + return max8997_bulk_write(i2c, reg, 8, data); } +/* Set predefined value for BUCK1 registers */ +static int max8997_set_buck1_voltages(struct max8997_data *max8997, + unsigned int *buck1_voltages, int arr_size) +{ + struct i2c_client *i2c; + int i, j, idx = 1, ret = 0; + u8 reg; -static struct regulator_ops max8997_charger_ops = { - .is_enabled = max8997_reg_is_enabled, - .enable = max8997_reg_enable, - .disable = max8997_reg_disable, - .get_current_limit = max8997_get_voltage, - .set_current_limit = max8997_set_voltage_ldobuck_wrap, -}; + mutex_lock(&max8997->dvs_lock); -static struct regulator_ops max8997_charger_fixedstate_ops = { - .is_enabled = max8997_reg_is_enabled, - .get_current_limit = max8997_get_voltage, - .set_current_limit = max8997_set_voltage_ldobuck_wrap, -}; + if (arr_size > BUCK1_TABLE_SIZE) { + dev_err(max8997->dev, "%s: too big voltage array!(%d)\n", + __func__, arr_size); + ret = -EINVAL; + goto out; + } -#define regulator_desc_ldo(num) { \ - .name = "LDO"#num, \ - .id = MAX8997_LDO##num, \ - .ops = &max8997_ldo_ops, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ + i2c = max8997->iodev->i2c; + + for (i = 0; i < arr_size; i++) { + if (buck1_voltages[i] == 0) + continue; + + j = 0; + while (buck1245_vol_cur_map_desc.min + + buck1245_vol_cur_map_desc.step*j + != (buck1_voltages[i] / 1000)) + j++; + + dev_info(max8997->dev, "%s: buck1_voltages[%d]=%d, " + "buck1_vol[%d]=%d\n", __func__, + i, buck1_voltages[i], idx, j); + + max8997->buck1_vol[idx] = j; + reg = MAX8997_REG_BUCK1DVSTV1 + idx; + ret = max8997_write_reg(i2c, reg, j); + if (ret < 0) { + dev_err(max8997->dev, "%s: fail to write reg(%d)!\n", + __func__, ret); + break; + } + idx++; + } +out: + mutex_unlock(&max8997->dvs_lock); + return ret; } -#define regulator_desc_buck(num) { \ - .name = "BUCK"#num, \ - .id = MAX8997_BUCK##num, \ - .ops = &max8997_buck_ops, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ + +static void max8997_set_buckramp(struct max8997_data *max8997, + struct max8997_platform_data *pdata) +{ + struct i2c_client *i2c = max8997->iodev->i2c; + int ret; + u8 val = 0; + + max8997->buck_ramp_en = pdata->buck_ramp_en; + if (max8997->buck_ramp_en) + val = (MAX8997_ENRAMPBUCK1 | MAX8997_ENRAMPBUCK2 + | MAX8997_ENRAMPBUCK4 | MAX8997_ENRAMPBUCK5); + + max8997->buck_ramp_delay = pdata->buck_ramp_delay; + switch (max8997->buck_ramp_delay) { + case 1 ... 12: + val |= max8997->buck_ramp_delay - 1; + break; + case 16: + val |= 0xc; /* 16.67mV/us */ + break; + case 25: + val |= 0xd; + break; + case 50: + val |= 0xe; + break; + case 100: + val |= 0xf; + break; + default: + dev_warn(max8997->dev, "%s: invalid ramp delay, " + "set ramp delay as 10mV/us\n", __func__); + max8997->buck_ramp_delay = 10; + val |= 0x9; + break; + } + + dev_info(max8997->dev, "RAMP REG(0x%x)\n", val); + ret = max8997_write_reg(i2c, MAX8997_REG_BUCKRAMP, val); + if (ret < 0) + dev_err(max8997->dev, "failed to write RAMP REG(%d)\n", ret); } -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(21), - regulator_desc_buck(1), - regulator_desc_buck(2), - regulator_desc_buck(3), - regulator_desc_buck(4), - regulator_desc_buck(5), - { - .name = "BUCK6", - .id = MAX8997_BUCK6, - .ops = &max8997_fixedvolt_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - }, - regulator_desc_buck(7), - { - .name = "EN32KHz AP", - .id = MAX8997_EN32KHZ_AP, - .ops = &max8997_fixedvolt_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - }, { - .name = "EN32KHz CP", - .id = MAX8997_EN32KHZ_CP, - .ops = &max8997_fixedvolt_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - }, { - .name = "ENVICHG", - .id = MAX8997_ENVICHG, - .ops = &max8997_fixedvolt_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - }, { - .name = "ESAFEOUT1", - .id = MAX8997_ESAFEOUT1, - .ops = &max8997_safeout_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - }, { - .name = "ESAFEOUT2", - .id = MAX8997_ESAFEOUT2, - .ops = &max8997_safeout_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - }, { - .name = "CHARGER CV", - .id = MAX8997_CHARGER_CV, - .ops = &max8997_fixedstate_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - }, { - .name = "CHARGER", - .id = MAX8997_CHARGER, - .ops = &max8997_charger_ops, - .type = REGULATOR_CURRENT, - .owner = THIS_MODULE, - }, { - .name = "CHARGER TOPOFF", - .id = MAX8997_CHARGER_TOPOFF, - .ops = &max8997_charger_fixedstate_ops, - .type = REGULATOR_CURRENT, - .owner = THIS_MODULE, - }, -}; +static int max8997_set_buck1_dvs_table(struct max8997_buck1_dvs_funcs *ptr, + unsigned int *voltage_table, int arr_size) +{ + struct max8997_data *max8997 + = container_of(ptr, struct max8997_data, funcs); + int ret; + + ret = max8997_set_buck1_voltages(max8997, voltage_table, arr_size); + + if (ret >= 0) { + max8997->buck1_gpiodvs = true; + dev_info(max8997->dev, "%s: enable BUCK1 GPIO DVS\n", + __func__); + + } + return ret; +} + +static void max8997_set_mr_debouce_time(struct max8997_data *max8997, + struct max8997_platform_data *pdata) +{ + struct i2c_client *i2c = max8997->iodev->i2c; + int ret; + u8 val = pdata->mr_debounce_time; + + if (val > 8) { + dev_err(max8997->dev, "Invalid MR debounce time(%d)\n", val); + return; + } + + dev_info(max8997->dev, "manual reset debouce time: %d sec.\n", val); + + ret = max8997_write_reg(i2c, MAX8997_REG_CONTROL2, --val); + if (ret < 0) + dev_err(max8997->dev, "failed to write CONTROL2(%d)\n", ret); +} static __devinit int max8997_pmic_probe(struct platform_device *pdev) { @@ -964,11 +1215,10 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) struct regulator_dev **rdev; struct max8997_data *max8997; struct i2c_client *i2c; - int i, ret, size; - u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0; + int i, size, ret = 0; if (!pdata) { - dev_err(pdev->dev.parent, "No platform init data supplied.\n"); + dev_err(pdev->dev.parent, "No platform init data supplied\n"); return -ENODEV; } @@ -979,189 +1229,157 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) size = sizeof(struct regulator_dev *) * pdata->num_regulators; max8997->rdev = kzalloc(size, GFP_KERNEL); if (!max8997->rdev) { - kfree(max8997); - return -ENOMEM; + ret = -ENOMEM; + goto err3; } + mutex_init(&max8997->dvs_lock); + rdev = max8997->rdev; max8997->dev = &pdev->dev; max8997->iodev = iodev; max8997->num_regulators = pdata->num_regulators; platform_set_drvdata(pdev, max8997); i2c = max8997->iodev->i2c; - - max8997->buck125_gpioindex = pdata->buck125_default_idx; max8997->buck1_gpiodvs = pdata->buck1_gpiodvs; - max8997->buck2_gpiodvs = pdata->buck2_gpiodvs; - max8997->buck5_gpiodvs = pdata->buck5_gpiodvs; - memcpy(max8997->buck125_gpios, pdata->buck125_gpios, sizeof(int) * 3); - max8997->ignore_gpiodvs_side_effect = pdata->ignore_gpiodvs_side_effect; - - for (i = 0; i < 8; i++) { - max8997->buck1_vol[i] = ret = - max8997_get_voltage_proper_val( - &buck1245_voltage_map_desc, - pdata->buck1_voltage[i] / 1000, - pdata->buck1_voltage[i] / 1000 + - buck1245_voltage_map_desc.step); - if (ret < 0) - goto err_alloc; - - max8997->buck2_vol[i] = ret = - max8997_get_voltage_proper_val( - &buck1245_voltage_map_desc, - pdata->buck2_voltage[i] / 1000, - pdata->buck2_voltage[i] / 1000 + - buck1245_voltage_map_desc.step); - if (ret < 0) - goto err_alloc; - - max8997->buck5_vol[i] = ret = - max8997_get_voltage_proper_val( - &buck1245_voltage_map_desc, - pdata->buck5_voltage[i] / 1000, - pdata->buck5_voltage[i] / 1000 + - buck1245_voltage_map_desc.step); - if (ret < 0) - goto err_alloc; - - if (max_buck1 < max8997->buck1_vol[i]) - max_buck1 = max8997->buck1_vol[i]; - if (max_buck2 < max8997->buck2_vol[i]) - max_buck2 = max8997->buck2_vol[i]; - if (max_buck5 < max8997->buck5_vol[i]) - max_buck5 = max8997->buck5_vol[i]; + max8997->buck_set1 = pdata->buck_set1; + max8997->buck_set2 = pdata->buck_set2; + max8997->buck_set3 = pdata->buck_set3; + + /* NOTE: + * This MAX8997 PMIC driver support only BUCK1 GPIO DVS + * because BUCK1(ARM clock voltage) is most frequently changed + */ + /* For the safety, set max voltage before DVS configuration */ + if (!pdata->buck1_max_vol || !pdata->buck2_max_vol + || !pdata->buck5_max_vol) { + pr_err("MAX8997: must set buck max voltage!\n"); + goto err2; } - /* For the safety, set max voltage before setting up */ - for (i = 0; i < 8; i++) { - max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i, - max_buck1, 0x3f); - max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i, - max_buck2, 0x3f); - max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i, - max_buck5, 0x3f); + ret = max8997_set_buck_max_voltage(max8997, 1, pdata->buck1_max_vol); + if (ret < 0) { + pr_err("MAX8997: fail to set buck1 max voltage!\n"); + goto err2; } - /* - * If buck 1, 2, and 5 do not care DVS GPIO settings, ignore them. - * If at least one of them cares, set gpios. - */ - if (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs || - pdata->buck5_gpiodvs) { - bool gpio1set = false, gpio2set = false; - - if (!gpio_is_valid(pdata->buck125_gpios[0]) || - !gpio_is_valid(pdata->buck125_gpios[1]) || - !gpio_is_valid(pdata->buck125_gpios[2])) { - dev_err(&pdev->dev, "GPIO NOT VALID\n"); - ret = -EINVAL; - goto err_alloc; - } + ret = max8997_set_buck_max_voltage(max8997, 2, pdata->buck2_max_vol); + if (ret < 0) { + pr_err("MAX8997: fail to set buck2 max voltage!\n"); + goto err2; + } - ret = gpio_request(pdata->buck125_gpios[0], - "MAX8997 SET1"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request" - " on SET1\n"); - else if (ret) - goto err_alloc; - else - gpio1set = true; - - ret = gpio_request(pdata->buck125_gpios[1], - "MAX8997 SET2"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request" - " on SET2\n"); - else if (ret) { - if (gpio1set) - gpio_free(pdata->buck125_gpios[0]); - goto err_alloc; - } else - gpio2set = true; - - ret = gpio_request(pdata->buck125_gpios[2], - "MAX8997 SET3"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request" - " on SET3\n"); - else if (ret) { - if (gpio1set) - gpio_free(pdata->buck125_gpios[0]); - if (gpio2set) - gpio_free(pdata->buck125_gpios[1]); - goto err_alloc; + ret = max8997_set_buck_max_voltage(max8997, 5, pdata->buck5_max_vol); + if (ret < 0) { + pr_err("MAX8997: fail to set buck5 max voltage!\n"); + goto err2; + } + + /* NOTE: */ + /* For unused GPIO NOT marked as -1 (thereof equal to 0) WARN_ON */ + /* will be displayed */ + /* Check if MAX8997 voltage selection GPIOs are defined */ + if (gpio_is_valid(max8997->buck_set1) && + gpio_is_valid(max8997->buck_set2) && + gpio_is_valid(max8997->buck_set3)) { + /* Check if SET1 is not equal to 0 */ + if (!max8997->buck_set1) { + pr_err("MAX8997 SET1 GPIO defined as 0 !\n"); + WARN_ON(!pdata->buck_set1); + ret = -EIO; + goto err2; + } + /* Check if SET2 is not equal to 0 */ + if (!max8997->buck_set2) { + pr_err("MAX8998 SET2 GPIO defined as 0 !\n"); + WARN_ON(!pdata->buck_set2); + ret = -EIO; + goto err2; + } + /* Check if SET3 is not equal to 0 */ + if (!max8997->buck_set3) { + pr_err("MAX8997 SET3 GPIO defined as 0 !\n"); + WARN_ON(!max8997->buck_set3); + ret = -EIO; + goto err2; } - gpio_direction_output(pdata->buck125_gpios[0], - (max8997->buck125_gpioindex >> 2) - & 0x1); /* SET1 */ - gpio_direction_output(pdata->buck125_gpios[1], - (max8997->buck125_gpioindex >> 1) - & 0x1); /* SET2 */ - gpio_direction_output(pdata->buck125_gpios[2], - (max8997->buck125_gpioindex >> 0) - & 0x1); /* SET3 */ - ret = 0; + /* To prepare watchdog reset, index 0 of voltage table is + * always highest voltage. + * Default voltage of BUCK1,2,5 was configured by bootloader. + */ + max8997->buck1_idx = 1; + gpio_request(max8997->buck_set1, "MAX8997 BUCK_SET1"); + gpio_direction_output(max8997->buck_set1, 1); + gpio_request(max8997->buck_set2, "MAX8997 BUCK_SET2"); + gpio_direction_output(max8997->buck_set2, 0); + gpio_request(max8997->buck_set3, "MAX8997 BUCK_SET3"); + gpio_direction_output(max8997->buck_set3, 0); + + if (max8997->buck1_gpiodvs) { + /* Set predefined value for BUCK1 register 2 ~ 8 */ + ret = max8997_set_buck1_voltages(max8997, + pdata->buck1_voltages, BUCK1_TABLE_SIZE); + if (ret < 0) + goto err2; + } + } else { + pr_err("MAX8997 SETx GPIO is invalid!\n"); + goto err2; } - /* DVS-GPIO disabled */ - max8997_update_reg(i2c, MAX8997_REG_BUCK1CTRL, (pdata->buck1_gpiodvs) ? - (1 << 1) : (0 << 1), 1 << 1); - max8997_update_reg(i2c, MAX8997_REG_BUCK2CTRL, (pdata->buck2_gpiodvs) ? - (1 << 1) : (0 << 1), 1 << 1); - max8997_update_reg(i2c, MAX8997_REG_BUCK5CTRL, (pdata->buck5_gpiodvs) ? - (1 << 1) : (0 << 1), 1 << 1); - - /* Initialize all the DVS related BUCK registers */ - for (i = 0; i < 8; i++) { - max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i, - max8997->buck1_vol[i], - 0x3f); - max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i, - max8997->buck2_vol[i], - 0x3f); - max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i, - max8997->buck5_vol[i], - 0x3f); + max8997->funcs.set_buck1_dvs_table = max8997_set_buck1_dvs_table; + if (pdata->register_buck1_dvs_funcs) + pdata->register_buck1_dvs_funcs(&max8997->funcs); + + max8997_set_buckramp(max8997, pdata); + + if (pdata->flash_cntl_val) { + ret = max8997_write_reg(i2c, MAX8997_REG_FLASH_CNTL, + pdata->flash_cntl_val); + if (ret < 0) { + dev_err(max8997->dev, "flash init failed: %d\n", ret); + goto err2; + } } - /* Misc Settings */ - max8997->ramp_delay = 10; /* set 10mV/us, which is the default */ - max8997_write_reg(i2c, MAX8997_REG_BUCKRAMP, (0xf << 4) | 0x9); + if (pdata->mr_debounce_time) + max8997_set_mr_debouce_time(max8997, pdata); for (i = 0; i < pdata->num_regulators; i++) { - const struct voltage_map_desc *desc; + const struct vol_cur_map_desc *desc; int id = pdata->regulators[i].id; + int index = id - MAX8997_LDO1; - desc = reg_voltage_map[id]; - if (desc) - regulators[id].n_voltages = - (desc->max - desc->min) / desc->step + 1; - else if (id == MAX8997_ESAFEOUT1 || id == MAX8997_ESAFEOUT2) - regulators[id].n_voltages = 4; - else if (id == MAX8997_CHARGER_CV) - regulators[id].n_voltages = 16; + if (pdata->regulators[i].is_valid_regulator) { + if (!pdata->regulators[i].is_valid_regulator(id, + pdata->regulators[i].initdata)) + continue; + } - rdev[i] = regulator_register(®ulators[id], max8997->dev, + desc = ldo_vol_cur_map[id]; + if (desc && regulators[index].ops != &max8997_others_ops) { + int count = (desc->max - desc->min) / desc->step + 1; + regulators[index].n_voltages = count; + } + rdev[i] = regulator_register(®ulators[index], max8997->dev, pdata->regulators[i].initdata, max8997); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); - dev_err(max8997->dev, "regulator init failed for %d\n", - id); + dev_err(max8997->dev, "regulator init failed\n"); rdev[i] = NULL; - goto err; + goto err1; } } return 0; -err: +err1: for (i = 0; i < max8997->num_regulators; i++) if (rdev[i]) regulator_unregister(rdev[i]); -err_alloc: +err2: kfree(max8997->rdev); +err3: kfree(max8997); return ret; @@ -1183,12 +1401,6 @@ static int __devexit max8997_pmic_remove(struct platform_device *pdev) return 0; } -static const struct platform_device_id max8997_pmic_id[] = { - { "max8997-pmic", 0}, - { }, -}; -MODULE_DEVICE_TABLE(platform, max8997_pmic_id); - static struct platform_driver max8997_pmic_driver = { .driver = { .name = "max8997-pmic", @@ -1196,7 +1408,6 @@ static struct platform_driver max8997_pmic_driver = { }, .probe = max8997_pmic_probe, .remove = __devexit_p(max8997_pmic_remove), - .id_table = max8997_pmic_id, }; static int __init max8997_pmic_init(void) @@ -1211,6 +1422,6 @@ static void __exit max8997_pmic_cleanup(void) } module_exit(max8997_pmic_cleanup); -MODULE_DESCRIPTION("MAXIM 8997/8966 Regulator Driver"); -MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_DESCRIPTION("MAXIM 8997 voltage regulator driver"); +MODULE_AUTHOR("<ms925.kim@samsung.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c new file mode 100644 index 00000000000..2690b4c29d5 --- /dev/null +++ b/drivers/regulator/s5m8767.c @@ -0,0 +1,1031 @@ +/* + * 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-core.h> +#include <linux/mfd/s5m87xx/s5m-pmic.h> + +struct s5m8767_info { + struct device *dev; + struct s5m87xx_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; + struct s5m_opmode_data *opmode_data; + + 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_ds[3]; + + int buck_gpioindex; +}; + +struct s5m_voltage_desc { + int max; + int min; + int step; +}; + +static const struct s5m_voltage_desc buck_voltage_val1 = { + .max = 2225000, + .min = 650000, + .step = 6250, +}; + +static const struct s5m_voltage_desc buck_voltage_val2 = { + .max = 1600000, + .min = 600000, + .step = 6250, +}; + +static const struct s5m_voltage_desc buck_voltage_val3 = { + .max = 3000000, + .min = 750000, + .step = 12500, +}; + +static const struct s5m_voltage_desc ldo_voltage_val1 = { + .max = 3950000, + .min = 800000, + .step = 50000, +}; + +static const struct s5m_voltage_desc ldo_voltage_val2 = { + .max = 2375000, + .min = 800000, + .step = 25000, +}; + +static const struct s5m_voltage_desc *reg_voltage_map[] = { + [S5M8767_LDO1] = &ldo_voltage_val2, + [S5M8767_LDO2] = &ldo_voltage_val2, + [S5M8767_LDO3] = &ldo_voltage_val1, + [S5M8767_LDO4] = &ldo_voltage_val1, + [S5M8767_LDO5] = &ldo_voltage_val1, + [S5M8767_LDO6] = &ldo_voltage_val2, + [S5M8767_LDO7] = &ldo_voltage_val2, + [S5M8767_LDO8] = &ldo_voltage_val2, + [S5M8767_LDO9] = &ldo_voltage_val1, + [S5M8767_LDO10] = &ldo_voltage_val1, + [S5M8767_LDO11] = &ldo_voltage_val1, + [S5M8767_LDO12] = &ldo_voltage_val1, + [S5M8767_LDO13] = &ldo_voltage_val1, + [S5M8767_LDO14] = &ldo_voltage_val1, + [S5M8767_LDO15] = &ldo_voltage_val2, + [S5M8767_LDO16] = &ldo_voltage_val1, + [S5M8767_LDO17] = &ldo_voltage_val1, + [S5M8767_LDO18] = &ldo_voltage_val1, + [S5M8767_LDO19] = &ldo_voltage_val1, + [S5M8767_LDO20] = &ldo_voltage_val1, + [S5M8767_LDO21] = &ldo_voltage_val1, + [S5M8767_LDO22] = &ldo_voltage_val1, + [S5M8767_LDO23] = &ldo_voltage_val1, + [S5M8767_LDO24] = &ldo_voltage_val1, + [S5M8767_LDO25] = &ldo_voltage_val1, + [S5M8767_LDO26] = &ldo_voltage_val1, + [S5M8767_LDO27] = &ldo_voltage_val1, + [S5M8767_LDO28] = &ldo_voltage_val1, + [S5M8767_BUCK1] = &buck_voltage_val1, + [S5M8767_BUCK2] = &buck_voltage_val2, + [S5M8767_BUCK3] = &buck_voltage_val2, + [S5M8767_BUCK4] = &buck_voltage_val2, + [S5M8767_BUCK5] = &buck_voltage_val1, + [S5M8767_BUCK6] = &buck_voltage_val1, + [S5M8767_BUCK7] = NULL, + [S5M8767_BUCK8] = NULL, + [S5M8767_BUCK9] = &buck_voltage_val3, +}; + +static inline int s5m8767_get_reg_id(struct regulator_dev *rdev) +{ + return rdev_get_id(rdev); +} + +static int s5m8767_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct s5m_voltage_desc *desc; + int reg_id = s5m8767_get_reg_id(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; +} + +unsigned int s5m8767_opmode_reg[][3] = { + /* LDO1 ... LDO28 */ + /* {NORMAL, LP, STANDBY} */ + {0x3, 0x2, 0x1}, /* LDO1 */ + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, /* LDO11 */ + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, /* LDO21 */ + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, + {0x3, 0x2, 0x1}, /* LDO28 */ + /* BUCK1 ... BUCK9 */ + {0x3, 0x1, 0x1}, /* BUCK1 */ + {0x3, 0x1, 0x1}, + {0x3, 0x1, 0x1}, + {0x3, 0x1, 0x1}, + {0x3, 0x2, 0x1}, /* BUCK5 */ + {0x3, 0x1, 0x1}, + {0x3, 0x1, 0x1}, + {0x3, 0x1, 0x1}, + {0x3, 0x1, 0x1}, /* BUCK9 */ + /* 32KHZ */ + {0x1, 0x1, 0x1}, + {0x2, 0x2, 0x2}, + {0x4, 0x4, 0x4}, +}; + +static int s5m8767_get_register(struct regulator_dev *rdev, int *reg, int *pmic_en) +{ + int reg_id = s5m8767_get_reg_id(rdev); + unsigned int mode; + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_LDO2: + *reg = S5M8767_REG_LDO1CTRL + (reg_id - S5M8767_LDO1); + break; + case S5M8767_LDO3 ... S5M8767_LDO28: + *reg = S5M8767_REG_LDO3CTRL + (reg_id - S5M8767_LDO3); + 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: + *reg = S5M8767_REG_BUCK5CTRL1; + break; + case S5M8767_BUCK6 ... S5M8767_BUCK9: + *reg = S5M8767_REG_BUCK6CTRL1 + (reg_id - S5M8767_BUCK6) * 2; + break; + case S5M8767_AP_EN32KHZ ... S5M8767_BT_EN32KHZ: + *reg = S5M8767_REG_CTRL1; + break; + default: + return -EINVAL; + } + + mode = s5m8767->opmode_data[reg_id].mode; + *pmic_en = s5m8767_opmode_reg[reg_id][mode] << S5M8767_PMIC_EN_SHIFT; + + return 0; +} + +static int s5m8767_reg_is_enabled(struct regulator_dev *rdev) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = s5m8767->iodev->i2c; + int reg_id = s5m8767_get_reg_id(rdev); + int ret, reg; + int mask = 0xc0, pmic_en; + u8 val; + + ret = s5m8767_get_register(rdev, ®, &pmic_en); + if (ret == -EINVAL) + return 1; + else if (ret) + return ret; + + ret = s5m_reg_read(i2c, reg, &val); + if (ret) + return ret; + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_BUCK9: + mask = 0xc0; + break; + case S5M8767_AP_EN32KHZ: + mask = 0x01; + break; + case S5M8767_CP_EN32KHZ: + mask = 0x02; + break; + case S5M8767_BT_EN32KHZ: + mask = 0x04; + break; + default: + return -EINVAL; + } + + return (val & mask) == pmic_en; +} + +static int s5m8767_reg_enable(struct regulator_dev *rdev) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = s5m8767->iodev->i2c; + int reg_id = s5m8767_get_reg_id(rdev); + int ret, reg; + int mask, pmic_en; + + ret = s5m8767_get_register(rdev, ®, &pmic_en); + if (ret) + return ret; + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_BUCK9: + mask = 0xc0; + break; + case S5M8767_AP_EN32KHZ: + mask = 0x01; + break; + case S5M8767_CP_EN32KHZ: + mask = 0x02; + break; + case S5M8767_BT_EN32KHZ: + mask = 0x04; + break; + default: + return -EINVAL; + } + + return s5m_reg_update(i2c, reg, pmic_en, mask); +} + +static int s5m8767_reg_disable(struct regulator_dev *rdev) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = s5m8767->iodev->i2c; + int reg_id = s5m8767_get_reg_id(rdev); + int ret, reg; + int mask, pmic_en; + + ret = s5m8767_get_register(rdev, ®, &pmic_en); + if (ret) + return ret; + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_BUCK9: + mask = 0xc0; + break; + case S5M8767_AP_EN32KHZ: + mask = 0x01; + break; + case S5M8767_CP_EN32KHZ: + mask = 0x02; + break; + case S5M8767_BT_EN32KHZ: + mask = 0x04; + break; + default: + return -EINVAL; + } + + return s5m_reg_update(i2c, reg, ~mask, mask); +} + +static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg) +{ + int reg_id = s5m8767_get_reg_id(rdev); + int reg; + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_LDO2: + reg = S5M8767_REG_LDO1CTRL + (reg_id - S5M8767_LDO1); + break; + case S5M8767_LDO3 ... S5M8767_LDO28: + reg = S5M8767_REG_LDO3CTRL + (reg_id - S5M8767_LDO3); + break; + case S5M8767_BUCK1: + reg = S5M8767_REG_BUCK1CTRL2; + break; + case S5M8767_BUCK2: + reg = S5M8767_REG_BUCK2DVS2; + break; + case S5M8767_BUCK3: + reg = S5M8767_REG_BUCK3DVS2; + break; + case S5M8767_BUCK4: + reg = S5M8767_REG_BUCK4DVS2; + break; + case S5M8767_BUCK5: + reg = S5M8767_REG_BUCK5CTRL2; + break; + case S5M8767_BUCK6 ... S5M8767_BUCK9: + reg = S5M8767_REG_BUCK6CTRL2 + (reg_id - S5M8767_BUCK6) * 2; + break; + default: + return -EINVAL; + } + + *_reg = reg; + + return 0; +} + +static int s5m8767_get_voltage_sel(struct regulator_dev *rdev) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = s5m8767->iodev->i2c; + int reg, mask = 0xff, ret; + int reg_id = s5m8767_get_reg_id(rdev); + u8 val; + + ret = s5m8767_get_voltage_register(rdev, ®); + if (ret) + return ret; + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_LDO28: + 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(i2c, reg, &val); + if (ret) + return ret; + + val &= mask; + + return val; +} + +static inline int s5m8767_convert_voltage( + const struct s5m_voltage_desc *desc, + int min_vol, int max_vol) +{ + int i = 0, j = 0; + + if (desc == NULL) + return -EINVAL; + + if (max_vol < desc->min || min_vol > desc->max) + return -EINVAL; + + j = (min_vol - desc->min) / desc->step; + + if (desc->min + desc->step * i > max_vol) + return -EINVAL; + + return j; +} + +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); + struct i2c_client *i2c = s5m8767->iodev->i2c; + int min_vol = min_uV, max_vol = max_uV; + const struct s5m_voltage_desc *desc; + int reg_id = s5m8767_get_reg_id(rdev); + int reg, mask, ret; + int i; + u8 val; + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_LDO28: + mask = 0x3f; + break; + case S5M8767_BUCK1 ... S5M8767_BUCK6: + mask = 0xff; + break; + case S5M8767_BUCK7 ... S5M8767_BUCK8: + return -EINVAL; + case S5M8767_BUCK9: + mask = 0xff; + break; + 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, ®); + if (ret) + return ret; + + s5m_reg_read(i2c, reg, &val); + val = val & mask; + + ret = s5m_reg_update(i2c, reg, i, mask); + *selector = i; + + if (val < i) { + udelay(DIV_ROUND_UP(desc->step * (i - val), + s5m8767->ramp_delay * 1000)); + } + 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_reg_id(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_BUCK9) + 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); + case S5M8767_BUCK9: + 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) +{ + return 0; +} + +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_sel = s5m8767_get_voltage_sel, + .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_sel = s5m8767_get_voltage_sel, + .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_ldo(28), + 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), + regulator_desc_buck(9), + { + .name = "EN32KHz AP", + .id = S5M8767_AP_EN32KHZ, + .ops = &s5m8767_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz CP", + .id = S5M8767_CP_EN32KHZ, + .ops = &s5m8767_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz BT", + .id = S5M8767_BT_EN32KHZ, + .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 s5m_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_dev **rdev; + struct s5m8767_info *s5m8767; + struct i2c_client *i2c; + int i, ret, size, buck_init; + + 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->buck_ds[0] = pdata->buck_ds[0]; + s5m8767->buck_ds[1] = pdata->buck_ds[1]; + s5m8767->buck_ds[2] = pdata->buck_ds[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; + s5m8767->opmode_data = pdata->opmode_data; + + buck_init = s5m8767_convert_voltage(&buck_voltage_val2, + pdata->buck2_init, + pdata->buck2_init + + buck_voltage_val2.step); + + s5m_reg_write(i2c, S5M8767_REG_BUCK2DVS2, buck_init); + + buck_init = s5m8767_convert_voltage(&buck_voltage_val2, + pdata->buck3_init, + pdata->buck3_init + + buck_voltage_val2.step); + + s5m_reg_write(i2c, S5M8767_REG_BUCK3DVS2, buck_init); + + buck_init = s5m8767_convert_voltage(&buck_voltage_val2, + pdata->buck4_init, + pdata->buck4_init + + buck_voltage_val2.step); + + s5m_reg_write(i2c, S5M8767_REG_BUCK4DVS2, buck_init); + + buck_init = s5m8767_convert_voltage(&buck_voltage_val2, + pdata->buck2_init, + pdata->buck2_init + + buck_voltage_val2.step); + + s5m_reg_write(i2c, S5M8767_REG_BUCK2DVS2, buck_init); + + buck_init = s5m8767_convert_voltage(&buck_voltage_val2, + pdata->buck3_init, + pdata->buck3_init + + buck_voltage_val2.step); + + s5m_reg_write(i2c, S5M8767_REG_BUCK3DVS2, buck_init); + + buck_init = s5m8767_convert_voltage(&buck_voltage_val2, + pdata->buck4_init, + pdata->buck4_init + + buck_voltage_val2.step); + + s5m_reg_write(i2c, S5M8767_REG_BUCK4DVS2, buck_init); + + for (i = 0; i < 8; i++) { + if (s5m8767->buck2_gpiodvs) { + s5m8767->buck2_vol[i] = + s5m8767_convert_voltage( + &buck_voltage_val2, + pdata->buck2_voltage[i], + pdata->buck2_voltage[i] + + buck_voltage_val2.step); + } + + if (s5m8767->buck3_gpiodvs) { + s5m8767->buck3_vol[i] = + s5m8767_convert_voltage( + &buck_voltage_val2, + pdata->buck3_voltage[i], + pdata->buck3_voltage[i] + + buck_voltage_val2.step); + } + + if (s5m8767->buck4_gpiodvs) { + s5m8767->buck4_vol[i] = + s5m8767_convert_voltage( + &buck_voltage_val2, + pdata->buck4_voltage[i], + pdata->buck4_voltage[i] + + buck_voltage_val2.step); + } + } + + 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 NOT VALID\n"); + ret = -EINVAL; + goto err_alloc; + } + + ret = gpio_request(pdata->buck_ds[0], "S5M8767 DS2"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS2\n"); + + ret = gpio_request(pdata->buck_ds[1], "S5M8767 DS3"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS3\n"); + + ret = gpio_request(pdata->buck_ds[2], "S5M8767 DS4"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS4\n"); + + /* DS2 GPIO */ + gpio_direction_output(pdata->buck_ds[0], 0x0); + /* DS3 GPIO */ + gpio_direction_output(pdata->buck_ds[1], 0x0); + /* DS4 GPIO */ + gpio_direction_output(pdata->buck_ds[2], 0x0); + + if (pdata->buck2_gpiodvs) { + if (pdata->buck3_gpiodvs || pdata->buck4_gpiodvs) { + dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n"); + ret = -EINVAL; + goto err_alloc; + } + } + + if (pdata->buck3_gpiodvs) { + if (pdata->buck2_gpiodvs || pdata->buck4_gpiodvs) { + dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n"); + ret = -EINVAL; + goto err_alloc; + } + } + + if (pdata->buck4_gpiodvs) { + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs) { + dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n"); + ret = -EINVAL; + goto err_alloc; + } + } + + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || + pdata->buck4_gpiodvs) { + s5m_reg_update(i2c, S5M8767_REG_BUCK2CTRL, + (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + s5m_reg_update(i2c, S5M8767_REG_BUCK3CTRL, + (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + s5m_reg_update(i2c, 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(i2c, S5M8767_REG_BUCK2DVS1 + i, + s5m8767->buck2_vol[i]); + } + + if (s5m8767->buck3_gpiodvs) { + s5m_reg_write(i2c, S5M8767_REG_BUCK3DVS1 + i, + s5m8767->buck3_vol[i]); + } + + if (s5m8767->buck4_gpiodvs) { + s5m_reg_write(i2c, S5M8767_REG_BUCK4DVS1 + i, + s5m8767->buck4_vol[i]); + } + } + + if (s5m8767->buck2_ramp) + s5m_reg_update(i2c, S5M8767_REG_DVSRAMP, 0x08, 0x08); + + if (s5m8767->buck3_ramp) + s5m_reg_update(i2c, S5M8767_REG_DVSRAMP, 0x04, 0x04); + + if (s5m8767->buck4_ramp) + s5m_reg_update(i2c, S5M8767_REG_DVSRAMP, 0x02, 0x02); + + if (s5m8767->buck2_ramp || s5m8767->buck3_ramp + || s5m8767->buck4_ramp) { + switch (s5m8767->ramp_delay) { + case 5: + s5m_reg_update(i2c, S5M8767_REG_DVSRAMP, + 0x40, 0xf0); + break; + case 10: + s5m_reg_update(i2c, S5M8767_REG_DVSRAMP, + 0x90, 0xf0); + break; + case 25: + s5m_reg_update(i2c, S5M8767_REG_DVSRAMP, + 0xd0, 0xf0); + break; + case 50: + s5m_reg_update(i2c, S5M8767_REG_DVSRAMP, + 0xe0, 0xf0); + break; + case 100: + s5m_reg_update(i2c, S5M8767_REG_DVSRAMP, + 0xf0, 0xf0); + break; + default: + s5m_reg_update(i2c, 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(®ulators[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"); diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 35b2958d510..e5c396bbdac 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -72,6 +72,12 @@ static int wm8994_ldo_is_enabled(struct regulator_dev *rdev) static int wm8994_ldo_enable_time(struct regulator_dev *rdev) { + struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); + struct wm8994_pdata *pdata = ldo->wm8994->dev->platform_data; + + if (pdata->ldo_ena_delay) + return pdata->ldo_ena_delay; + /* 3ms is fairly conservative but this shouldn't be too performance * critical; can be tweaked per-system if required. */ return 3000; @@ -91,11 +97,14 @@ static int wm8994_ldo1_get_voltage_sel(struct regulator_dev *rdev) struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); int val; - val = wm8994_reg_read(ldo->wm8994, WM8994_LDO_1); - if (val < 0) - return val; - - return (val & WM8994_LDO1_VSEL_MASK) >> WM8994_LDO1_VSEL_SHIFT; + switch (ldo->wm8994->type) { + case WM8994: + case WM8958: + case WM1811: + return 6; + default: + return -EINVAL; + } } static int wm8994_ldo1_set_voltage(struct regulator_dev *rdev, @@ -140,6 +149,14 @@ static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev, return (selector * 100000) + 900000; case WM8958: return (selector * 100000) + 1000000; + case WM1811: + switch (selector) { + case 0: + return -EINVAL; + default: + return (selector * 100000) + 950000; + } + break; default: return -EINVAL; } @@ -150,11 +167,15 @@ static int wm8994_ldo2_get_voltage_sel(struct regulator_dev *rdev) struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); int val; - val = wm8994_reg_read(ldo->wm8994, WM8994_LDO_2); - if (val < 0) - return val; - - return (val & WM8994_LDO2_VSEL_MASK) >> WM8994_LDO2_VSEL_SHIFT; + switch (ldo->wm8994->type) { + case WM8994: + return 2; + case WM8958: + case WM1811: + return 1; + default: + return -EINVAL; + } } static int wm8994_ldo2_set_voltage(struct regulator_dev *rdev, @@ -170,6 +191,11 @@ static int wm8994_ldo2_set_voltage(struct regulator_dev *rdev, case WM8958: selector = (min_uV - 1000000) / 100000; break; + case WM1811: + selector = (min_uV - 950000) / 100000; + if (selector == 0) + selector = 1; + break; default: return -EINVAL; } |