From 350d1f5a66df8d571f439bb004adca722266f793 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Thu, 7 Feb 2013 15:04:33 +0900 Subject: [PATCH] regulator: max77693: Add regulator driver for max77693 Signed-off-by: Jonghwa Lee --- drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/max77693.c | 792 +++++++++++++++++++++++++++++++++++ include/linux/mfd/max77693-private.h | 14 + include/linux/mfd/max77693.h | 27 ++ 5 files changed, 842 insertions(+) create mode 100644 drivers/regulator/max77693.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 8bb2644..c50864c 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -250,6 +250,14 @@ config REGULATOR_MAX77686 via I2C bus. The provided regulator is suitable for Exynos-4 chips to control VARM and VINT voltages. +config REGULATOR_MAX77693 + tristate "Maxim 77693 regulator" + depends on MFD_MAX77693 + help + This driver controls a Maxim 77693 regulator + via I2C bus. The provided regulator supports Safeout LDOs + and can be used to supply low voltage related to USB systems. + config REGULATOR_PCAP tristate "Motorola PCAP2 regulator driver" depends on EZX_PCAP diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 47a34ff..a4094cd 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_REGULATOR_MAX8973) += max8973-regulator.o obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o +obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c new file mode 100644 index 0000000..85776b2 --- /dev/null +++ b/drivers/regulator/max77693.c @@ -0,0 +1,792 @@ +/* + * max77693.c - Regulator driver for the Maxim 77693 + * + * Copyright (C) 2011 Samsung Electronics + * Sukdong Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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) +{ + 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(rdev->regmap, reg, &val); + if (ret) + return ret; + + return (val & mask) == pattern; +} + +static int max77693_reg_enable(struct regulator_dev *rdev) +{ + 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(rdev->regmap, reg, pattern, mask); +} + +static int max77693_reg_disable(struct regulator_dev *rdev) +{ + 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(rdev->regmap, 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) +{ + 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(rdev->regmap, 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) +{ + 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(rdev->regmap, 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(rdev->regmap, 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) +{ + 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(rdev->regmap, 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); + 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(rdev->regmap, 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(rdev->regmap, reg, pattern, mask); +} + +/* + * max77693_muic_is_enable_otg - Check the power state of otg + * @rdev: the instance for voltage/current regulator class device + */ +static bool max77693_muic_is_enable_otg(struct regulator_dev *rdev) +{ + u8 chg_cnfg_00; + u8 mask; + u8 state; + + /* OTG on, boost on, DIS_MUIC_CTRL=1 */ + max77693_read_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_00, &chg_cnfg_00); + + mask = (CHG_CNFG_00_OTG_MASK + | CHG_CNFG_00_BOOST_MASK + | CHG_CNFG_00_DIS_MUIC_CTRL_MASK); + + state = chg_cnfg_00 & mask; + + if (state == mask) + return true; + else + return false; +} + +/* + * max77693_muic_is_enable_usb - Check the power state of usb + * @rdev: the instance for voltage/current regulator class device + */ +static bool max77693_muic_is_enable_usb(struct regulator_dev *rdev) +{ + u8 chg_cnfg_00; + bool state = false; + + max77693_read_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_00, &chg_cnfg_00); + + if (chg_cnfg_00 == 0x05) + state = true; + else if (chg_cnfg_00 == 0x04) + state = false; + + return state; +} + +/* + * max77693_muic_enable_otg - Enable/disable otg feature on max77693 + * @rdev: the instance for voltage/current regulator class device + * @enable: the state of otg feature (true: ON, false: OFF) + */ +static void max77693_muic_enable_otg(struct regulator_dev *rdev, int enable) +{ + struct max77693_data *max77693 = rdev_get_drvdata(rdev); + struct regmap *regmap_muic = max77693->iodev->regmap_muic; + static u8 chg_int_state; /* Restore charger interrupt states */ + u8 int_mask; + u8 cdetctrl1; + u8 chg_cnfg_00; + + dev_info(&rdev->dev, "enable(%d)\n", enable); + + /* + * FIXME: This function write/read directly the register of max77693 + * charger device. It has dependency between muic and charger + * device. The dependency between two device have to be removed. + * - MAX77693_CHG_REG_CHG_CNFG_00 : control charger/OTG/buck/boost + * - MAX77693_CHG_REG_CHG_INT_MASK + */ + if (enable) { + /* disable charger interrupt */ + max77693_read_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_INT_MASK, &int_mask); + chg_int_state = int_mask; + int_mask |= (1 << 4); /* disable chgin intr */ + int_mask |= (1 << 6); /* disable chg */ + int_mask &= ~(1 << 0); /* enable byp intr */ + max77693_write_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_INT_MASK, int_mask); + + /* disable charger detection */ + max77693_read_reg(regmap_muic, + MAX77693_MUIC_REG_CDETCTRL1, &cdetctrl1); + cdetctrl1 &= ~(1 << 0); + max77693_write_reg(regmap_muic, + MAX77693_MUIC_REG_CDETCTRL1, cdetctrl1); + + /* OTG on, boost on, DIS_MUIC_CTRL=1 */ + max77693_read_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_00, &chg_cnfg_00); + chg_cnfg_00 &= ~(CHG_CNFG_00_CHG_MASK + | CHG_CNFG_00_OTG_MASK + | CHG_CNFG_00_BUCK_MASK + | CHG_CNFG_00_BOOST_MASK + | CHG_CNFG_00_DIS_MUIC_CTRL_MASK); + chg_cnfg_00 |= (CHG_CNFG_00_OTG_MASK + | CHG_CNFG_00_BOOST_MASK + | CHG_CNFG_00_DIS_MUIC_CTRL_MASK); + max77693_write_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_00, chg_cnfg_00); + } else { + /* OTG off, boost off, (buck on), + DIS_MUIC_CTRL = 0 unless CHG_ENA = 1 */ + max77693_read_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_00, &chg_cnfg_00); + chg_cnfg_00 &= ~(CHG_CNFG_00_OTG_MASK + | CHG_CNFG_00_BOOST_MASK + | CHG_CNFG_00_DIS_MUIC_CTRL_MASK); + chg_cnfg_00 |= CHG_CNFG_00_BUCK_MASK; + max77693_write_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_00, chg_cnfg_00); + + mdelay(50); + + /* enable charger detection */ + max77693_read_reg(regmap_muic, + MAX77693_MUIC_REG_CDETCTRL1, &cdetctrl1); + cdetctrl1 |= (1 << 0); + max77693_write_reg(regmap_muic, + MAX77693_MUIC_REG_CDETCTRL1, cdetctrl1); + + /* enable charger interrupt */ + max77693_write_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_INT_MASK, chg_int_state); + max77693_read_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_INT_MASK, &int_mask); + } + + dev_info(&rdev->dev, + "INT_MASK(0x%x), CDETCTRL1(0x%x), CHG_CNFG_00(0x%x)\n", + int_mask, cdetctrl1, chg_cnfg_00); +} + +/* + * max77693_muic_enable_powered_usb - Enable/disable usb feature on max77693 + * @rdev: the instance for voltage/current regulator class device + * @enable: the state of USB feature (true: ON, false: OFF) + */ +static void max77693_muic_enable_usb(struct regulator_dev *rdev, int enable) +{ + dev_info(&rdev->dev, "enable(%d)\n", enable); + + if (enable) { + /* OTG on, boost on */ + max77693_write_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_00, 0x05); + + max77693_write_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_02, 0x0E); + } else { + /* OTG off, boost off, (buck on) */ + max77693_write_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_00, 0x04); + } +} + +static int max77693_usb_is_enabled(struct regulator_dev *rdev) +{ + struct regulator_desc *desc = rdev->desc; + int ret; + + switch (desc->id) { + case MAX77693_USBHOST: + ret = max77693_muic_is_enable_otg(rdev); + break; + case MAX77693_USB: + ret = max77693_muic_is_enable_usb(rdev); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int max77693_usb_enable(struct regulator_dev *rdev) +{ + struct regulator_desc *desc = rdev->desc; + + switch (desc->id) { + case MAX77693_USBHOST: + max77693_muic_enable_otg(rdev, true); + break; + case MAX77693_USB: + max77693_muic_enable_usb(rdev, true); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int max77693_usb_disable(struct regulator_dev *rdev) +{ + struct regulator_desc *desc = rdev->desc; + + switch (desc->id) { + case MAX77693_USBHOST: + max77693_muic_enable_otg(rdev, false); + break; + case MAX77693_USB: + max77693_muic_enable_usb(rdev, false); + break; + default: + return -EINVAL; + } + + return 0; +} + +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_ops max77693_usb_ops = { + .is_enabled = max77693_usb_is_enabled, + .enable = max77693_usb_enable, + .disable = max77693_usb_disable, +}; + +static struct regulator_desc regulators[] = { + { + .name = "ESAFEOUT1", + .id = MAX77693_ESAFEOUT1, + .ops = &max77693_safeout_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = 4, + }, { + .name = "ESAFEOUT2", + .id = MAX77693_ESAFEOUT2, + .ops = &max77693_safeout_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = 4, + }, { + .name = "CHARGER", + .id = MAX77693_CHARGER, + .ops = &max77693_charger_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, { + .name = "USBHOST", + .id = MAX77693_USBHOST, + .ops = &max77693_usb_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, { + .name = "USB", + .id = MAX77693_USB, + .ops = &max77693_usb_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 max77693_data *max77693; + int i, ret, size; + struct regulator_config config = {}; + + 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 = devm_kzalloc(&pdev->dev, 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 =devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!max77693->rdev) { + pr_info("[%s:%d] if (!max77693->rdev)\n", __FILE__, __LINE__); + return -ENOMEM; + } + + max77693->dev = &pdev->dev; + max77693->iodev = iodev; + max77693->num_regulators = pdata->num_regulators; + + config.dev = &pdev->dev; + config.regmap = iodev->regmap; + config.driver_data = max77693; + platform_set_drvdata(pdev, max77693); + + 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; + + config.init_data = pdata->regulators[i].initdata; + + 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; + + max77693->rdev[i] = regulator_register(®ulators[id], &config); + if (IS_ERR(max77693->rdev[i])) { + ret = PTR_ERR(max77693->rdev[i]); + dev_err(max77693->dev, "regulator init failed for %d\n", + id); + max77693->rdev[i] = NULL; + goto err; + } + } + + return 0; + err: + pr_info("[%s:%d] err:\n", __FILE__, __LINE__); + while (--i >= 0) + regulator_unregister(max77693->rdev[i]); + + 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]); + + 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); +} +#ifdef CONFIG_FAST_RESUME +beforeresume_initcall(max77693_pmic_init); +#else +subsys_initcall(max77693_pmic_init); +#endif + +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 "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 1aa4f13..e005fb2 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -238,6 +238,20 @@ enum max77693_haptic_reg { MAX77693_HAPTIC_REG_END, }; +/* MAX77693 CHG_CNFG_00 register */ +#define CHG_CNFG_00_MODE_SHIFT 0 +#define CHG_CNFG_00_CHG_SHIFT 0 +#define CHG_CNFG_00_OTG_SHIFT 1 +#define CHG_CNFG_00_BUCK_SHIFT 2 +#define CHG_CNFG_00_BOOST_SHIFT 3 +#define CHG_CNFG_00_DIS_MUIC_CTRL_SHIFT 5 +#define CHG_CNFG_00_MODE_MASK (0xf << CHG_CNFG_00_MODE_SHIFT) +#define CHG_CNFG_00_CHG_MASK (1 << CHG_CNFG_00_CHG_SHIFT) +#define CHG_CNFG_00_OTG_MASK (1 << CHG_CNFG_00_OTG_SHIFT) +#define CHG_CNFG_00_BUCK_MASK (1 << CHG_CNFG_00_BUCK_SHIFT) +#define CHG_CNFG_00_BOOST_MASK (1 << CHG_CNFG_00_BOOST_SHIFT) +#define CHG_CNFG_00_DIS_MUIC_CTRL_MASK (1 << CHG_CNFG_00_DIS_MUIC_CTRL_SHIFT) + enum max77693_irq_source { LED_INT = 0, TOPSYS_INT, diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h index 3109a6c..77d8207 100644 --- a/include/linux/mfd/max77693.h +++ b/include/linux/mfd/max77693.h @@ -30,6 +30,25 @@ #ifndef __LINUX_MFD_MAX77693_H #define __LINUX_MFD_MAX77693_H +/* MAX77686 regulator IDs */ +enum max77693_regulators { + MAX77693_ESAFEOUT1 = 0, + MAX77693_ESAFEOUT2, + + MAX77693_CHARGER, + + MAX77693_USBHOST, + MAX77693_USB, + + MAX77693_REG_MAX, +}; + +struct max77693_regulator_data { + int id; + struct regulator_init_data *initdata; + struct device_node *of_node; +}; + struct max77693_reg_data { u8 addr; u8 data; @@ -50,9 +69,17 @@ struct max77693_muic_platform_data { }; struct max77693_platform_data { + /* IRQ */ + int irq_base; + int irq_gpio; int wakeup; + struct max77693_muic_data *muic; + bool (*is_default_uart_path_cp) (void); + struct max77693_regulator_data *regulators; + int num_regulators; /* muic data */ struct max77693_muic_platform_data *muic_data; }; + #endif /* __LINUX_MFD_MAX77693_H */ -- 2.7.4