diff options
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 18 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 2 | ||||
-rw-r--r-- | drivers/regulator/core.c | 2 | ||||
-rw-r--r-- | drivers/regulator/fixed.c | 4 | ||||
-rw-r--r-- | drivers/regulator/lp8720.c | 471 | ||||
-rw-r--r-- | drivers/regulator/max77686.c | 47 | ||||
-rw-r--r-- | drivers/regulator/max77693.c | 5 | ||||
-rw-r--r-- | drivers/regulator/max8952_grande.c | 258 | ||||
-rw-r--r-- | drivers/regulator/s5m8767.c | 58 |
9 files changed, 839 insertions, 26 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 02868c0..e17e47b 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -108,6 +108,15 @@ config REGULATOR_MAX8952 via I2C bus. Maxim 8952 has one voltage output and supports 4 DVS modes ranging from 0.77V to 1.40V by 0.01V steps. +config REGULATOR_MAX8952_GRANDE + tristate "Maxim MAX8952 Power Management IC" + depends on I2C + help + This driver controls a Maxim 8952 voltage output regulator + via I2C bus. Maxim 8952 has one voltage output and supports 4 DVS + modes ranging from 0.77V to 1.40V by 0.01V steps. + This driver uses MODE3 for Grande Project + config REGULATOR_MAX8997 tristate "Maxim 8997/8966 regulator" depends on MFD_MAX8997 @@ -218,6 +227,15 @@ config REGULATOR_LP3972 Say Y here to support the voltage regulators and convertors on National Semiconductors LP3972 PMIC +config REGULATOR_LP8720 + tristate "National Semiconductors LP8720 PMIC regulator driver" + depends on I2C + help + This driver controls a National Semiconductors LP8720 voltage + output regulator(the BUCKs and LDOs) via I2C bus. Say Y here + to support the voltage regulators and convertors on National + Semiconductors LP8720 PMIC + config REGULATOR_PCAP tristate "PCAP2 regulator driver" depends on EZX_PCAP diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 7f7be11..d71b793 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -12,12 +12,14 @@ obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o +obj-$(CONFIG_REGULATOR_LP8720) += lp8720.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o +obj-$(CONFIG_REGULATOR_MAX8952_GRANDE) += max8952_grande.o obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_MAX8698) += max8698.o diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d3e3879..5f1a08b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2704,8 +2704,8 @@ void regulator_unregister(struct regulator_dev *rdev) list_del(&rdev->list); if (rdev->supply) sysfs_remove_link(&rdev->dev.kobj, "supply"); - device_unregister(&rdev->dev); kfree(rdev->constraints); + device_unregister(&rdev->dev); mutex_unlock(®ulator_list_mutex); } EXPORT_SYMBOL_GPL(regulator_unregister); diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 2fe9d99c..153d35b 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -229,7 +229,11 @@ static int __init regulator_fixed_voltage_init(void) { return platform_driver_register(®ulator_fixed_voltage_driver); } +#ifdef CONFIG_FAST_RESUME +beforeresume_initcall(regulator_fixed_voltage_init); +#else subsys_initcall(regulator_fixed_voltage_init); +#endif static void __exit regulator_fixed_voltage_exit(void) { diff --git a/drivers/regulator/lp8720.c b/drivers/regulator/lp8720.c new file mode 100644 index 0000000..cb66718 --- /dev/null +++ b/drivers/regulator/lp8720.c @@ -0,0 +1,471 @@ +/* + * Regulator driver for National Semiconductors LP8720 PMIC chip + * + * Based on lp3972.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/bug.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/lp8720.h> +#include <mach/gpio.h> +#include <linux/slab.h> +#include <plat/gpio-cfg.h> + +static const int ldo_output_enable_mask[] = { + LP8720_LDO1_EN, + LP8720_LDO2_EN, + LP8720_LDO3_EN, + LP8720_LDO4_EN, + LP8720_LDO5_EN, + LP8720_BUCK_V1_EN, + LP8720_BUCK_V2_EN, +}; + +static const int ldo_output_enable_addr[] = { + LP8720_LDO1_REG, + LP8720_LDO2_REG, + LP8720_LDO3_REG, + LP8720_LDO4_REG, + LP8720_LDO5_REG, + LP8720_BUCK_V1_REG, + LP8720_BUCK_V2_REG, +}; + +#define LP8720_LDO_OUTPUT_ENABLE_MASK(x) (ldo_output_enable_mask[x]) +#define LP8720_LDO_OUTPUT_ENABLE_REG(x) (ldo_output_enable_addr[x]) + + +static const int ldo1235_voltage_map[] = { + 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, + 1600, 1650, 1700, 1750, 1800, 1850, 1900, 2000, + 2100, 2200, 2300, 2400, 2500, 2600, 2650, 2700, + 2750, 2800, 2850, 2900, 2950, 3000, 3100, 3300, +}; + +static const int ldo4_voltage_map[] = { + 800, 850, 900, 1000, 1100, 1200, 1250, 1300, + 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, + 1750, 1800, 1850, 1900, 2000, 2100, 2200, 2300, + 2400, 2500, 2600, 2650, 2700, 2750, 2800, 2850, +}; + +static const int buck12_voltage_map[] = { + 0, 800, 850, 900, 950, 1000, 1050, 1100, + 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, + 1550, 1600, 1650, 1700, 1750, 1800, 1850, 1900, + 1950, 2000, 2050, 2100, 2150, 2200, 2250, 2300, +}; + +static const int *ldo_voltage_map[] = { + ldo1235_voltage_map, /* LDO1 */ + ldo1235_voltage_map, /* LDO2 */ + ldo1235_voltage_map, /* LDO3 */ + ldo4_voltage_map, /* LDO4 */ + ldo1235_voltage_map, /* LDO5 */ + buck12_voltage_map, /* BUCK_V1 */ + buck12_voltage_map, /* BUCK_V2 */ +}; + +#define LDO_VOL_VALUE_MAP(x) (ldo_voltage_map[(x - LP8720_LDO1)]) +#define LDO_VOL_MIN_IDX (0) +#define LDO_VOL_MAX_IDX (31) + +static int lp8720_i2c_read(struct i2c_client *i2c, char reg, int count, + u8 *dest) +{ + u8 ret; + + if (count != 1) + return -EIO; + + ret = i2c_smbus_read_byte_data(i2c, reg); + + if (ret < 0) + return ret; + + *dest = ret; + return 0; +} + +static int lp8720_i2c_write(struct i2c_client *i2c, u8 reg, int count, + const u8 value) +{ + u8 ret; + + if (count != 1) + return -EIO; + + ret = i2c_smbus_write_byte_data(i2c, reg, value); + + return ret; +} + +static u8 lp8720_reg_read(struct lp8720 *lp8720, u8 reg) +{ + u8 val = 0; + + mutex_lock(&lp8720->io_lock); + lp8720_i2c_read(lp8720->i2c, reg, 1, &val); + dev_dbg(lp8720->dev, "reg read 0x%02x -> 0x%02x\n", (int)reg, + (unsigned)val & 0xff); + mutex_unlock(&lp8720->io_lock); + + return val & 0xff; +} + + +static int lp8720_set_bits(struct lp8720 *lp8720, u8 reg, u16 mask, u16 val) +{ + u8 tmp; + int ret; + + mutex_lock(&lp8720->io_lock); + ret = lp8720_i2c_read(lp8720->i2c, reg, 1, &tmp); + tmp = (tmp & ~mask) | val; + if (ret == 0) { + ret = lp8720_i2c_write(lp8720->i2c, reg, 1, tmp); + dev_dbg(lp8720->dev, "reg write 0x%02x -> 0x%02x\n", (int)reg, + (unsigned)val & 0xff); + } + mutex_unlock(&lp8720->io_lock); + + return ret; +} + +static int lp8720_ldo_is_enabled(struct regulator_dev *dev) +{ + struct lp8720 *lp8720 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP8720_LDO1; + u16 mask = LP8720_LDO_OUTPUT_ENABLE_MASK(ldo); + u16 val; + + val = lp8720_reg_read(lp8720, LP8720_ENABLE_REG); + dev_dbg(lp8720->dev, "%s, is_enabled ldo:%d, mask:0x%4x - %s\n", + __func__, ldo, mask, + !!(val & mask) ? "enabled" : "disabled"); + + return !!(val & mask); +} + +static int lp8720_ldo_enable(struct regulator_dev *dev) +{ + struct lp8720 *lp8720 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP8720_LDO1; + u16 mask = LP8720_LDO_OUTPUT_ENABLE_MASK(ldo); + + dev_dbg(lp8720->dev, "%s, enable ldo:%d, mask:0x%4x\n", + __func__, ldo, mask); + return lp8720_set_bits(lp8720, LP8720_ENABLE_REG, + mask, mask); +} + +static int lp8720_ldo_disable(struct regulator_dev *dev) +{ + struct lp8720 *lp8720 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP8720_LDO1; + u16 mask = LP8720_LDO_OUTPUT_ENABLE_MASK(ldo); + + dev_dbg(lp8720->dev, "%s, disable ldo:%d, mask:0x%4x\n", + __func__, ldo, mask); + return lp8720_set_bits(lp8720, LP8720_ENABLE_REG, + mask, 0); +} + +static int lp8720_ldo_list_voltage(struct regulator_dev *dev, unsigned index) +{ + struct lp8720 *lp8720 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP8720_LDO1; + + if (ldo < 0 || ldo > ARRAY_SIZE(ldo_voltage_map)) { + dev_err(lp8720->dev, "[ldo = %d] is out of range\n", ldo); + return -EINVAL; + } + if (index < 0 || index > LDO_VOL_MAX_IDX) { + dev_err(lp8720->dev, "[index = %d] is out of range\n", index); + return -EINVAL; + } + return 1000 * LDO_VOL_VALUE_MAP(ldo)[index]; +} + +static int lp8720_ldo_get_voltage(struct regulator_dev *dev) +{ + struct lp8720 *lp8720 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP8720_LDO1; + u16 val, reg; + + reg = lp8720_reg_read(lp8720, LP8720_LDO_VOL_CONTR_REG(ldo)); + val = reg & LP8720_LDOV_MASK; + + return 1000 * LDO_VOL_VALUE_MAP(ldo)[val]; +} + +static int lp8720_ldo_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, + unsigned int *selector) +{ + struct lp8720 *lp8720 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP8720_LDO1; + int min_vol = min_uV / 1000, max_vol = max_uV / 1000; + const int *vol_map = LDO_VOL_VALUE_MAP(ldo); + u16 val; + + if (min_vol < vol_map[LDO_VOL_MIN_IDX] || + min_vol > vol_map[LDO_VOL_MAX_IDX]) { + dev_err(lp8720->dev, "[min_vol = %d] is out of range\n", + min_vol); + dev_err(lp8720->dev, "vol_map[%d] : %d, vol_map[%d] : %d\n", + LDO_VOL_MIN_IDX, vol_map[LDO_VOL_MIN_IDX], + LDO_VOL_MAX_IDX, vol_map[LDO_VOL_MAX_IDX]); + return -EINVAL; + } + + for (val = LDO_VOL_MIN_IDX; val <= LDO_VOL_MAX_IDX; val++) + if (vol_map[val] >= min_vol) + break; + + if (val > LDO_VOL_MAX_IDX || vol_map[val] > max_vol) { + dev_err(lp8720->dev, "[val = %d] is out of range\n", val); + return -EINVAL; + } + + *selector = val; + dev_dbg(lp8720->dev, "%s, disable ldo:%d, val:0x%4x\n", + __func__, ldo, val); + + return lp8720_set_bits(lp8720, LP8720_LDO_VOL_CONTR_REG(ldo), + LP8720_LDOV_MASK, val); +} + +static struct regulator_ops lp8720_ldo_ops = { + .list_voltage = lp8720_ldo_list_voltage, + .is_enabled = lp8720_ldo_is_enabled, + .enable = lp8720_ldo_enable, + .disable = lp8720_ldo_disable, + .get_voltage = lp8720_ldo_get_voltage, + .set_voltage = lp8720_ldo_set_voltage, +}; + +static struct regulator_desc regulators[] = { + { + .name = "LDO1", + .id = LP8720_LDO1, + .ops = &lp8720_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo1235_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = LP8720_LDO2, + .ops = &lp8720_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo1235_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO3", + .id = LP8720_LDO3, + .ops = &lp8720_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo1235_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO4", + .id = LP8720_LDO4, + .ops = &lp8720_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo4_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO5", + .id = LP8720_LDO5, + .ops = &lp8720_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo1235_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "BUCK_V1", + .id = LP8720_BUCK_V1, + .ops = &lp8720_ldo_ops, + .n_voltages = ARRAY_SIZE(buck12_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "BUCK_V2", + .id = LP8720_BUCK_V2, + .ops = &lp8720_ldo_ops, + .n_voltages = ARRAY_SIZE(buck12_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, +}; + +static int __devinit setup_regulators(struct lp8720 *lp8720, + struct lp8720_platform_data *pdata) +{ + int i, err; + + lp8720->num_regulators = pdata->num_regulators; + lp8720->rdev = kcalloc(pdata->num_regulators, + sizeof(struct regulator_dev *), GFP_KERNEL); + if (!lp8720->rdev) { + err = -ENOMEM; + goto err_nomem; + } + + /* Instantiate the regulators */ + for (i = 0; i < pdata->num_regulators; i++) { + struct lp8720_regulator_subdev *reg = &pdata->regulators[i]; + lp8720->rdev[i] = regulator_register(®ulators[reg->id], + lp8720->dev, reg->initdata, lp8720); + if (IS_ERR(lp8720->rdev[i])) { + err = PTR_ERR(lp8720->rdev[i]); + dev_err(lp8720->dev, "regulator init failed: %d\n", + err); + goto error; + } + } + return 0; +error: + while (--i >= 0) + regulator_unregister(lp8720->rdev[i]); + kfree(lp8720->rdev); + lp8720->rdev = NULL; +err_nomem: + return err; +} + +static int __devinit lp8720_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct lp8720 *lp8720; + struct lp8720_platform_data *pdata = i2c->dev.platform_data; + int ret; + u8 readbyte = 0; + u8 enable = 0; + + if (!pdata) { + dev_dbg(&i2c->dev, "No platform init data supplied\n"); + return -ENODEV; + } + + lp8720 = kzalloc(sizeof(struct lp8720), GFP_KERNEL); + if (!lp8720) + return -ENOMEM; + + lp8720->i2c = i2c; + lp8720->dev = &i2c->dev; + + mutex_init(&lp8720->io_lock); + ret = lp8720_i2c_read(i2c, 0x00, 1, &readbyte); + if (ret == 0 && + readbyte != 0x05) { + ret = -ENODEV; + dev_err(&i2c->dev, "chip reported: [00h]= 0x%x\n", readbyte); + } + if (ret < 0) { + dev_err(&i2c->dev, "failed to detect device. ret = %d\n", ret); + goto err_detect; + } + + ret = setup_regulators(lp8720, pdata); + if (ret < 0) + goto err_detect; + + i2c_set_clientdata(i2c, lp8720); + ret = gpio_request(pdata->en_pin, pdata->name); + if (ret) { + printk(KERN_ERR "%s, ERROR [%s] - gpio_request(%d) failed\n", + __func__, + pdata->name, + pdata->en_pin); + } + s3c_gpio_cfgpin(pdata->en_pin, S3C_GPIO_OUTPUT); + s3c_gpio_setpull(pdata->en_pin, S3C_GPIO_PULL_NONE); + gpio_set_value(pdata->en_pin, 1); + if (!gpio_get_value(pdata->en_pin)) { + printk(KERN_ERR "%s, ERROR [%s] - gpio_get_value(%d) is %s\n", + __func__, + pdata->name, + pdata->en_pin, + gpio_get_value(pdata->en_pin) ? "HIGH" : "LOW"); + } + if (!strncmp(pdata->name, + "lp8720_folder_pmic", + strlen("lp8720_folder_pmic"))) { + printk(KERN_DEBUG "%s, folder_pmic\n", __func__); + } else if (!strncmp(pdata->name, + "lp8720_sub_pmic", + strlen("lp8720_sub_pmic"))) { + lp8720_i2c_read(i2c, LP8720_ENABLE_REG, 1, &readbyte); + enable = readbyte & 0xC0; + lp8720_i2c_write(i2c, LP8720_ENABLE_REG, 1, enable); + lp8720_i2c_read(i2c, LP8720_ENABLE_REG, 1, &readbyte); + printk(KERN_DEBUG "%s, [%s] - ENABLE_REG : 0x%2x\n", + __func__, pdata->name, readbyte); + } + + return 0; + +err_detect: + kfree(lp8720); + return ret; +} + +static int __devexit lp8720_i2c_remove(struct i2c_client *i2c) +{ + struct lp8720 *lp8720 = i2c_get_clientdata(i2c); + struct lp8720_platform_data *pdata = i2c->dev.platform_data; + int i; + + for (i = 0; i < lp8720->num_regulators; i++) + regulator_unregister(lp8720->rdev[i]); + kfree(lp8720->rdev); + kfree(lp8720); + gpio_free(pdata->en_pin); + return 0; +} + +static const struct i2c_device_id lp8720_i2c_id[] = { + { "lp8720", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp8720_i2c_id); + +static struct i2c_driver lp8720_i2c_driver = { + .driver = { + .name = "lp8720", + .owner = THIS_MODULE, + }, + .probe = lp8720_i2c_probe, + .remove = __devexit_p(lp8720_i2c_remove), + .id_table = lp8720_i2c_id, +}; + +static int __init lp8720_module_init(void) +{ + return i2c_add_driver(&lp8720_i2c_driver); +} +subsys_initcall(lp8720_module_init); + +static void __exit lp8720_module_exit(void) +{ + i2c_del_driver(&lp8720_i2c_driver); +} +module_exit(lp8720_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Axel Lin <axel.lin@gmail.com>"); +MODULE_DESCRIPTION("LP8720 PMIC driver"); diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index ebb2954..936afb1 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -316,7 +316,7 @@ static int max77686_reg_enable(struct regulator_dev *rdev) if (ret) return ret; - pr_info("%s: id=%d, pattern=%x\n", + printk(PMIC_DEBUG "%s: id=%d, pattern=%x\n", __func__, rdev_get_id(rdev), pattern); return max77686_update_reg(i2c, reg, pattern, mask); @@ -332,7 +332,7 @@ static int max77686_reg_disable(struct regulator_dev *rdev) if (ret) return ret; - pr_info("%s: id=%d, pattern=%x\n", + printk(PMIC_DEBUG "%s: id=%d, pattern=%x\n", __func__, rdev_get_id(rdev), pattern); return max77686_update_reg(i2c, reg, ~mask, mask); @@ -401,7 +401,7 @@ static int max77686_get_voltage(struct regulator_dev *rdev) val >>= shift; val &= mask; - pr_debug("%s: id=%d, val=%x\n", + printk(PMIC_REG_DEBUG "%s: id=%d, val=%x\n", __func__, rid, val); return max77686_list_voltage(rdev, val); @@ -467,12 +467,19 @@ static int max77686_set_voltage(struct regulator_dev *rdev, 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) + defined(CONFIG_MACH_M3) || \ + defined(CONFIG_MACH_P4NOTE) || \ + defined(CONFIG_MACH_GC1) || defined(CONFIG_MACH_T0) || \ + defined(CONFIG_MACH_GRANDE) || defined(CONFIG_MACH_IRON) +#if !defined(CONFIG_MACH_T0_CHN_CU_DUOS) || \ + !defined(CONFIG_MACH_T0_CHN_CMCC) || \ + !defined(CONFIG_MACH_T0_CHN_OPEN_DUOS) || \ + !defined(CONFIG_MACH_T0_CHN_CTC) /* Test code for HDMI debug */ if (!gpio_get_value(GPIO_HDMI_EN)) #endif - pr_debug("max77686: id=%d, org=%x, val=%x", +#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); @@ -633,7 +640,7 @@ static int max77686_set_ramp_rate(struct i2c_client *i2c, int rate) break; } - pr_debug("%s: ramp_delay=%d, data=0x%x\n", __func__, ramp_delay, data); + 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); @@ -649,10 +656,10 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) struct regulator_dev **rdev; struct max77686_data *max77686; struct i2c_client *i2c; - int i, ret, size; + int i, ret, size, err; u8 data = 0; - pr_info("%s\n", __func__); + printk(PMIC_DEBUG "%s\n", __func__); if (!pdata) { dev_err(pdev->dev.parent, "No platform init data supplied.\n"); @@ -682,7 +689,7 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) max77686_read_reg(i2c, MAX77686_REG_DEVICE_ID, &data); max77686->device_id = (data & 0x7); - pr_info("%s: DEVICE ID=0x%x\n", __func__, data); + printk(PMIC_DEBUG "%s: DEVICE ID=0x%x\n", __func__, data); /* * TODO @@ -700,7 +707,11 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) 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); + err = gpio_request( + pdata->buck234_gpio_dvs[i].gpio, buf); + if (err) + pr_warn( + "failed to request MAX77686 DVS%d\n", i); gpio_direction_output(pdata->buck234_gpio_dvs[i].gpio, pdata->buck234_gpio_dvs[i].data); } else { @@ -714,7 +725,11 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) 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); + err = gpio_request( + pdata->buck234_gpio_selb[i], buf); + if (err) + pr_warn( + "failed to request MAX77686 SELB%d\n", i); gpio_direction_output(pdata->buck234_gpio_selb[i], data); } else { dev_info(&pdev->dev, "GPIO %s ignored (%d)\n", @@ -776,7 +791,7 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) regulators[id].n_voltages = (desc->max - desc->min) / desc->step + 1; - pr_info("%s: desc=%p, id=%d, n_vol=%d, max=%d, min=%d, step=%d\n", + 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); } @@ -852,11 +867,15 @@ static struct platform_driver max77686_pmic_driver = { static int __init max77686_pmic_init(void) { - pr_info("%s\n", __func__); + printk(PMIC_DEBUG "%s\n", __func__); return platform_driver_register(&max77686_pmic_driver); } +#ifdef CONFIG_FAST_RESUME +beforeresume_initcall(max77686_pmic_init); +#else subsys_initcall(max77686_pmic_init); +#endif static void __exit max77686_pmic_cleanup(void) { diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index bcbdd3a..5293ea0 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c @@ -559,8 +559,11 @@ 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) { diff --git a/drivers/regulator/max8952_grande.c b/drivers/regulator/max8952_grande.c new file mode 100644 index 0000000..dad8a8e --- /dev/null +++ b/drivers/regulator/max8952_grande.c @@ -0,0 +1,258 @@ +/* + * max8952.c - Voltage and current regulation for the Maxim 8952 + * + * Copyright (C) 2010 Samsung Electronics + * MyungJoo Ham <myungjoo.ham@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/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/max8952.h> +#include <linux/mutex.h> +#include <linux/gpio.h> +#include <linux/io.h> +#include <linux/slab.h> + +/* Registers */ +enum { + MAX8952_REG_MODE0, + MAX8952_REG_MODE1, + MAX8952_REG_MODE2, + MAX8952_REG_MODE3, + MAX8952_REG_CONTROL, + MAX8952_REG_SYNC, + MAX8952_REG_RAMP, + MAX8952_REG_CHIP_ID1, + MAX8952_REG_CHIP_ID2, +}; + +struct max8952_data { + struct i2c_client *client; + struct device *dev; + struct mutex mutex; + struct max8952_platform_data *pdata; + struct regulator_dev *rdev; + + bool vid0; + bool vid1; + bool en; +}; + +static int max8952_read_reg(struct max8952_data *max8952, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(max8952->client, reg); + if (ret > 0) + ret &= 0xff; + + return ret; +} + +static int max8952_write_reg(struct max8952_data *max8952, + u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(max8952->client, reg, value); +} + +static int max8952_voltage(struct max8952_data *max8952, u8 mode) +{ + return (max8952->pdata->dvs_mode[mode] * 10 + 770) * 1000; +} + +static int max8952_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct max8952_data *max8952 = rdev_get_drvdata(rdev); + + if (rdev_get_id(rdev) != 0) + return -EINVAL; + + return max8952_voltage(max8952, selector); +} + +static int max8952_is_enabled(struct regulator_dev *rdev) +{ + struct max8952_data *max8952 = rdev_get_drvdata(rdev); + return max8952->en; +} + +static int max8952_enable(struct regulator_dev *rdev) +{ + struct max8952_data *max8952 = rdev_get_drvdata(rdev); + + /* If not valid, assume "ALWAYS_HIGH" */ + if (gpio_is_valid(max8952->pdata->gpio_en)) + gpio_set_value(max8952->pdata->gpio_en, 1); + + max8952->en = true; + return 0; +} + +static int max8952_disable(struct regulator_dev *rdev) +{ + struct max8952_data *max8952 = rdev_get_drvdata(rdev); + + /* If not valid, assume "ALWAYS_HIGH" -> not permitted */ + if (gpio_is_valid(max8952->pdata->gpio_en)) + gpio_set_value(max8952->pdata->gpio_en, 0); + else + return -EPERM; + + max8952->en = false; + return 0; +} + +static int max8952_get_voltage(struct regulator_dev *rdev) +{ + struct max8952_data *max8952 = rdev_get_drvdata(rdev); + u8 vid = 0; + + if (max8952->vid0) + vid += 1; + if (max8952->vid1) + vid += 2; + + return max8952_voltage(max8952, vid); +} + +static int max8952_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max8952_data *max8952 = rdev_get_drvdata(rdev); + s8 vid = -1, i; + + if (!gpio_is_valid(max8952->pdata->gpio_vid0) || + !gpio_is_valid(max8952->pdata->gpio_vid1)) { + /* DVS not supported */ + return -EPERM; + } + + for (i = 0; i < MAX8952_NUM_DVS_MODE; i++) { + int volt = max8952_voltage(max8952, i); + + /* Set the voltage as low as possible within the range */ + if (volt <= max_uV && volt >= min_uV) + if (vid == -1 || max8952_voltage(max8952, vid) > volt) + vid = i; + } + + if (vid >= 0 && vid < MAX8952_NUM_DVS_MODE) { + max8952->vid0 = (vid % 2 == 1); + max8952->vid1 = (((vid >> 1) % 2) == 1); + *selector = vid; + gpio_set_value(max8952->pdata->gpio_vid0, max8952->vid0); + gpio_set_value(max8952->pdata->gpio_vid1, max8952->vid1); + } else + return -EINVAL; + + return 0; +} + +static struct regulator_ops max8952_ops = { + .list_voltage = max8952_list_voltage, + .is_enabled = max8952_is_enabled, + .enable = max8952_enable, + .disable = max8952_disable, + .get_voltage = max8952_get_voltage, + .set_voltage = max8952_set_voltage, + .set_suspend_disable = max8952_disable, +}; + +static struct regulator_desc regulator = { + .name = "MAX8952_VOUT", + .id = 0, + .n_voltages = MAX8952_NUM_DVS_MODE, + .ops = &max8952_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +static int __devinit max8952_pmic_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct max8952_platform_data *pdata = client->dev.platform_data; + struct max8952_data *max8952; + + int ret = 0, err = 0; + int readbyte; + + max8952 = kzalloc(sizeof(struct max8952_data), GFP_KERNEL); + if (!max8952) + return -ENOMEM; + + max8952->client = client; + max8952->dev = &client->dev; + max8952->pdata = pdata; + mutex_init(&max8952->mutex); + + max8952_write_reg(max8952, MAX8952_REG_MODE3, 0x2B); + readbyte = max8952_read_reg(max8952, MAX8952_REG_MODE3); + printk(KERN_DEBUG "%s, readbyte : 0x%2x\n", __func__, readbyte); + + i2c_set_clientdata(client, max8952); + + return 0; + +err_reg: + kfree(max8952); + return ret; +} + +static int __devexit max8952_pmic_remove(struct i2c_client *client) +{ + struct max8952_data *max8952 = i2c_get_clientdata(client); + struct max8952_platform_data *pdata = max8952->pdata; + struct regulator_dev *rdev = max8952->rdev; + + kfree(max8952); + return 0; +} + +static const struct i2c_device_id max8952_ids[] = { + { "max8952_grande", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max8952_ids); + +static struct i2c_driver max8952_pmic_driver = { + .probe = max8952_pmic_probe, + .remove = __devexit_p(max8952_pmic_remove), + .driver = { + .name = "max8952_grande", + }, + .id_table = max8952_ids, +}; + +static int __init max8952_pmic_init(void) +{ + return i2c_add_driver(&max8952_pmic_driver); +} +subsys_initcall(max8952_pmic_init); + +static void __exit max8952_pmic_exit(void) +{ + i2c_del_driver(&max8952_pmic_driver); +} +module_exit(max8952_pmic_exit); + +MODULE_DESCRIPTION("MAXIM 8952 voltage regulator driver"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 2690b4c..da85667 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -21,6 +21,7 @@ #include <linux/regulator/machine.h> #include <linux/mfd/s5m87xx/s5m-core.h> #include <linux/mfd/s5m87xx/s5m-pmic.h> +#include <linux/sched.h> struct s5m8767_info { struct device *dev; @@ -28,7 +29,9 @@ struct s5m8767_info { int num_regulators; struct regulator_dev **rdev; struct s5m_opmode_data *opmode_data; + struct delayed_work set_buchg; + u8 device_id; int ramp_delay; bool buck2_ramp; bool buck3_ramp; @@ -190,9 +193,9 @@ unsigned int s5m8767_opmode_reg[][3] = { {0x3, 0x1, 0x1}, {0x3, 0x1, 0x1}, /* BUCK9 */ /* 32KHZ */ - {0x1, 0x1, 0x1}, - {0x2, 0x2, 0x2}, - {0x4, 0x4, 0x4}, + {0x1, 0x0, 0x0}, + {0x1, 0x0, 0x0}, + {0x1, 0x0, 0x0}, }; static int s5m8767_get_register(struct regulator_dev *rdev, int *reg, int *pmic_en) @@ -222,7 +225,8 @@ static int s5m8767_get_register(struct regulator_dev *rdev, int *reg, int *pmic_ break; case S5M8767_AP_EN32KHZ ... S5M8767_BT_EN32KHZ: *reg = S5M8767_REG_CTRL1; - break; + *pmic_en = 0x01 << (reg_id - S5M8767_AP_EN32KHZ); + return 0; default: return -EINVAL; } @@ -392,15 +396,15 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev) mask = 0x3f; break; case S5M8767_BUCK2: - if(s5m8767->buck2_gpiodvs) + if (s5m8767->buck2_gpiodvs) reg += s5m8767->buck_gpioindex; break; case S5M8767_BUCK3: - if(s5m8767->buck3_gpiodvs) + if (s5m8767->buck3_gpiodvs) reg += s5m8767->buck_gpioindex; break; case S5M8767_BUCK4: - if(s5m8767->buck4_gpiodvs) + if (s5m8767->buck4_gpiodvs) reg += s5m8767->buck_gpioindex; break; } @@ -475,7 +479,12 @@ static int s5m8767_set_voltage(struct regulator_dev *rdev, s5m_reg_read(i2c, reg, &val); val = val & mask; - ret = s5m_reg_update(i2c, reg, i, mask); + if (s5m8767->device_id == 4 && reg == S5M8767_REG_BUCK1CTRL2) + ret = s5m_reg_update(i2c, reg, (i >= 0x40) ? \ + (0x3F) : (i), mask); + else + ret = s5m_reg_update(i2c, reg, i, mask); + *selector = i; if (val < i) { @@ -536,8 +545,7 @@ static int s5m8767_set_voltage_buck(struct regulator_dev *rdev, if (s5m8767->buck2_gpiodvs) { while (s5m8767->buck2_vol[i] != new_val) i++; - } - else + } else return s5m8767_set_voltage(rdev, min_uV, max_uV, selector); break; case S5M8767_BUCK3: @@ -682,6 +690,20 @@ static struct regulator_desc regulators[] = { }, }; +static void s5m_set_buchg(struct work_struct *work) +{ + struct s5m8767_info *s5m8767; + u8 val; + val = 0x4f; /* set for BUCHG 100uA */ + + s5m8767 = container_of(work, struct s5m8767_info, set_buchg.work); + + s5m_reg_write(s5m8767->iodev->i2c, S5M8767_REG_BUCHG, val); + + s5m_reg_read(s5m8767->iodev->i2c, S5M8767_REG_BUCHG, &val); + pr_info("%s set S5M8767_REG_BUCHG = 0x%02x\n", __func__, val); +} + static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) { struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent); @@ -731,6 +753,17 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) s5m8767->buck4_ramp = pdata->buck4_ramp_enable; s5m8767->opmode_data = pdata->opmode_data; + s5m_reg_read(i2c, S5M8767_REG_ID, &s5m8767->device_id); + printk(KERN_DEBUG "%s: PMIC DEVICE ID=> 0x%x\n", + __func__, s5m8767->device_id); + + buck_init = s5m8767_convert_voltage(&buck_voltage_val1, + pdata->buck1_init, + pdata->buck1_init + + buck_voltage_val1.step); + + s5m_reg_write(i2c, S5M8767_REG_BUCK1DVS2, buck_init); + buck_init = s5m8767_convert_voltage(&buck_voltage_val2, pdata->buck2_init, pdata->buck2_init + @@ -855,6 +888,8 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) gpio_direction_output(pdata->buck_ds[1], 0x0); /* DS4 GPIO */ gpio_direction_output(pdata->buck_ds[2], 0x0); + /* BUCK1 DVS2 Enable */ + s5m_reg_update(i2c, S5M8767_REG_BUCK1CTRL1, 0x02, 0x02); if (pdata->buck2_gpiodvs) { if (pdata->buck3_gpiodvs || pdata->buck4_gpiodvs) { @@ -968,6 +1003,9 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) } } + INIT_DELAYED_WORK_DEFERRABLE(&s5m8767->set_buchg, s5m_set_buchg); + schedule_delayed_work(&s5m8767->set_buchg, msecs_to_jiffies(40000)); + return 0; err: for (i = 0; i < s5m8767->num_regulators; i++) |