diff options
| author | Wolfram Sang <w.sang@pengutronix.de> | 2009-09-25 03:39:26 -0400 |
|---|---|---|
| committer | Liam Girdwood <lrg@slimlogic.co.uk> | 2009-12-17 05:27:22 -0500 |
| commit | 27f37e4bfed803be338dcc78845d4a67eefb40a0 (patch) | |
| tree | 46f76cf673ab7870d4fc6182d6c60ffc3662a7a4 | |
| parent | 718deb6b61e34c200c1f2b706176d9aac334cb2d (diff) | |
regulator: add driver for MAX8660/8661
Tested with a MX25-based custom board.
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
| -rw-r--r-- | drivers/regulator/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
| -rw-r--r-- | drivers/regulator/max8660.c | 510 | ||||
| -rw-r--r-- | include/linux/regulator/max8660.h | 57 |
4 files changed, 575 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 7cfdd65bebb4..9e0aa14dc6af 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig | |||
| @@ -69,6 +69,13 @@ config REGULATOR_MAX1586 | |||
| 69 | regulator via I2C bus. The provided regulator is suitable | 69 | regulator via I2C bus. The provided regulator is suitable |
| 70 | for PXA27x chips to control VCC_CORE and VCC_USIM voltages. | 70 | for PXA27x chips to control VCC_CORE and VCC_USIM voltages. |
| 71 | 71 | ||
| 72 | config REGULATOR_MAX8660 | ||
| 73 | tristate "Maxim 8660/8661 voltage regulator" | ||
| 74 | depends on I2C | ||
| 75 | help | ||
| 76 | This driver controls a Maxim 8660/8661 voltage output | ||
| 77 | regulator via I2C bus. | ||
| 78 | |||
| 72 | config REGULATOR_TWL4030 | 79 | config REGULATOR_TWL4030 |
| 73 | bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC" | 80 | bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC" |
| 74 | depends on TWL4030_CORE | 81 | depends on TWL4030_CORE |
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 9ae3cc44e668..12285e41beec 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile | |||
| @@ -12,6 +12,7 @@ obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o | |||
| 12 | obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o | 12 | obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o |
| 13 | obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o | 13 | obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o |
| 14 | obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o | 14 | obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o |
| 15 | obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o | ||
| 15 | obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o | 16 | obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o |
| 16 | obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o | 17 | obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o |
| 17 | obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o | 18 | obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o |
diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c new file mode 100644 index 000000000000..acc2fb7b6087 --- /dev/null +++ b/drivers/regulator/max8660.c | |||
| @@ -0,0 +1,510 @@ | |||
| 1 | /* | ||
| 2 | * max8660.c -- Voltage regulation for the Maxim 8660/8661 | ||
| 3 | * | ||
| 4 | * based on max1586.c and wm8400-regulator.c | ||
| 5 | * | ||
| 6 | * Copyright (C) 2009 Wolfram Sang, Pengutronix e.K. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License as published by the Free | ||
| 10 | * Software Foundation; version 2 of the License. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 15 | * more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License along with | ||
| 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 19 | * Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | * Some info: | ||
| 22 | * | ||
| 23 | * Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8660-MAX8661.pdf | ||
| 24 | * | ||
| 25 | * This chip is a bit nasty because it is a write-only device. Thus, the driver | ||
| 26 | * uses shadow registers to keep track of its values. The main problem appears | ||
| 27 | * to be the initialization: When Linux boots up, we cannot know if the chip is | ||
| 28 | * in the default state or not, so we would have to pass such information in | ||
| 29 | * platform_data. As this adds a bit of complexity to the driver, this is left | ||
| 30 | * out for now until it is really needed. | ||
| 31 | * | ||
| 32 | * [A|S|M]DTV1 registers are currently not used, but [A|S|M]DTV2. | ||
| 33 | * | ||
| 34 | * If the driver is feature complete, it might be worth to check if one set of | ||
| 35 | * functions for V3-V7 is sufficient. For maximum flexibility during | ||
| 36 | * development, they are separated for now. | ||
| 37 | * | ||
| 38 | */ | ||
| 39 | |||
| 40 | #include <linux/module.h> | ||
| 41 | #include <linux/err.h> | ||
| 42 | #include <linux/i2c.h> | ||
| 43 | #include <linux/platform_device.h> | ||
| 44 | #include <linux/regulator/driver.h> | ||
| 45 | #include <linux/regulator/max8660.h> | ||
| 46 | |||
| 47 | #define MAX8660_DCDC_MIN_UV 725000 | ||
| 48 | #define MAX8660_DCDC_MAX_UV 1800000 | ||
| 49 | #define MAX8660_DCDC_STEP 25000 | ||
| 50 | #define MAX8660_DCDC_MAX_SEL 0x2b | ||
| 51 | |||
| 52 | #define MAX8660_LDO5_MIN_UV 1700000 | ||
| 53 | #define MAX8660_LDO5_MAX_UV 2000000 | ||
| 54 | #define MAX8660_LDO5_STEP 25000 | ||
| 55 | #define MAX8660_LDO5_MAX_SEL 0x0c | ||
| 56 | |||
| 57 | #define MAX8660_LDO67_MIN_UV 1800000 | ||
| 58 | #define MAX8660_LDO67_MAX_UV 3300000 | ||
| 59 | #define MAX8660_LDO67_STEP 100000 | ||
| 60 | #define MAX8660_LDO67_MAX_SEL 0x0f | ||
| 61 | |||
| 62 | enum { | ||
| 63 | MAX8660_OVER1, | ||
| 64 | MAX8660_OVER2, | ||
| 65 | MAX8660_VCC1, | ||
| 66 | MAX8660_ADTV1, | ||
| 67 | MAX8660_ADTV2, | ||
| 68 | MAX8660_SDTV1, | ||
| 69 | MAX8660_SDTV2, | ||
| 70 | MAX8660_MDTV1, | ||
| 71 | MAX8660_MDTV2, | ||
| 72 | MAX8660_L12VCR, | ||
| 73 | MAX8660_FPWM, | ||
| 74 | MAX8660_N_REGS, /* not a real register */ | ||
| 75 | }; | ||
| 76 | |||
| 77 | struct max8660 { | ||
| 78 | struct i2c_client *client; | ||
| 79 | u8 shadow_regs[MAX8660_N_REGS]; /* as chip is write only */ | ||
| 80 | struct regulator_dev *rdev[]; | ||
| 81 | }; | ||
| 82 | |||
| 83 | static int max8660_write(struct max8660 *max8660, u8 reg, u8 mask, u8 val) | ||
| 84 | { | ||
| 85 | static const u8 max8660_addresses[MAX8660_N_REGS] = | ||
| 86 | { 0x10, 0x12, 0x20, 0x23, 0x24, 0x29, 0x2a, 0x32, 0x33, 0x39, 0x80 }; | ||
| 87 | |||
| 88 | int ret; | ||
| 89 | u8 reg_val = (max8660->shadow_regs[reg] & mask) | val; | ||
| 90 | dev_vdbg(&max8660->client->dev, "Writing reg %02x with %02x\n", | ||
| 91 | max8660_addresses[reg], reg_val); | ||
| 92 | |||
| 93 | ret = i2c_smbus_write_byte_data(max8660->client, | ||
| 94 | max8660_addresses[reg], reg_val); | ||
| 95 | if (ret == 0) | ||
| 96 | max8660->shadow_regs[reg] = reg_val; | ||
| 97 | |||
| 98 | return ret; | ||
| 99 | } | ||
| 100 | |||
| 101 | |||
| 102 | /* | ||
| 103 | * DCDC functions | ||
| 104 | */ | ||
| 105 | |||
| 106 | static int max8660_dcdc_is_enabled(struct regulator_dev *rdev) | ||
| 107 | { | ||
| 108 | struct max8660 *max8660 = rdev_get_drvdata(rdev); | ||
| 109 | u8 val = max8660->shadow_regs[MAX8660_OVER1]; | ||
| 110 | u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4; | ||
| 111 | return !!(val & mask); | ||
| 112 | } | ||
| 113 | |||
| 114 | static int max8660_dcdc_enable(struct regulator_dev *rdev) | ||
| 115 | { | ||
| 116 | struct max8660 *max8660 = rdev_get_drvdata(rdev); | ||
| 117 | u8 bit = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4; | ||
| 118 | return max8660_write(max8660, MAX8660_OVER1, 0xff, bit); | ||
| 119 | } | ||
| 120 | |||
| 121 | static int max8660_dcdc_disable(struct regulator_dev *rdev) | ||
| 122 | { | ||
| 123 | struct max8660 *max8660 = rdev_get_drvdata(rdev); | ||
| 124 | u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? ~1 : ~4; | ||
| 125 | return max8660_write(max8660, MAX8660_OVER1, mask, 0); | ||
| 126 | } | ||
| 127 | |||
| 128 | static int max8660_dcdc_list(struct regulator_dev *rdev, unsigned selector) | ||
| 129 | { | ||
| 130 | if (selector > MAX8660_DCDC_MAX_SEL) | ||
| 131 | return -EINVAL; | ||
| 132 | return MAX8660_DCDC_MIN_UV + selector * MAX8660_DCDC_STEP; | ||
| 133 | } | ||
| 134 | |||
| 135 | static int max8660_dcdc_get(struct regulator_dev *rdev) | ||
| 136 | { | ||
| 137 | struct max8660 *max8660 = rdev_get_drvdata(rdev); | ||
| 138 | u8 reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2; | ||
| 139 | u8 selector = max8660->shadow_regs[reg]; | ||
| 140 | return MAX8660_DCDC_MIN_UV + selector * MAX8660_DCDC_STEP; | ||
| 141 | } | ||
| 142 | |||
| 143 | static int max8660_dcdc_set(struct regulator_dev *rdev, int min_uV, int max_uV) | ||
| 144 | { | ||
| 145 | struct max8660 *max8660 = rdev_get_drvdata(rdev); | ||
| 146 | u8 reg, selector, bits; | ||
| 147 | int ret; | ||
| 148 | |||
| 149 | if (min_uV < MAX8660_DCDC_MIN_UV || min_uV > MAX8660_DCDC_MAX_UV) | ||
| 150 | return -EINVAL; | ||
| 151 | if (max_uV < MAX8660_DCDC_MIN_UV || max_uV > MAX8660_DCDC_MAX_UV) | ||
| 152 | return -EINVAL; | ||
| 153 | |||
| 154 | selector = (min_uV - (MAX8660_DCDC_MIN_UV - MAX8660_DCDC_STEP + 1)) | ||
| 155 | / MAX8660_DCDC_STEP; | ||
| 156 | |||
| 157 | ret = max8660_dcdc_list(rdev, selector); | ||
| 158 | if (ret < 0 || ret > max_uV) | ||
| 159 | return -EINVAL; | ||
| 160 | |||
