summaryrefslogtreecommitdiff
path: root/drivers/regulator/gmt2214.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator/gmt2214.c')
-rwxr-xr-xdrivers/regulator/gmt2214.c1248
1 files changed, 1248 insertions, 0 deletions
diff --git a/drivers/regulator/gmt2214.c b/drivers/regulator/gmt2214.c
new file mode 100755
index 00000000..3e5e6d4c
--- /dev/null
+++ b/drivers/regulator/gmt2214.c
@@ -0,0 +1,1248 @@
+/*++
+ drivers/regulator/gmt2214.c
+
+ Copyright (c) 2013 WonderMedia Technologies, Inc.
+
+ 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, see <http://www.gnu.org/licenses/>.
+
+ WonderMedia Technologies, Inc.
+ 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+--*/
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/slab.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/regulator/machine.h>
+#include <mach/hardware.h>
+#include <linux/interrupt.h>
+#include <asm/unaligned.h>
+#include <linux/delay.h>
+#include <mach/gmt-core.h>
+
+#undef DEBUG
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0)
+#else
+#define DPRINTK(fmt, args...) do { } while (0)
+#endif
+
+#define GMT_DC1_MAX_STAGE 16
+#define GMT_DC2_MAX_STAGE 16
+#define GMT_LDOX_MAX_STAGE 2
+#define GMT_LDO56_MAX_STAGE 4
+
+#define GMT_DC1_MIN_UV 1050000
+#define GMT_DC1_MAX_UV 1500000
+
+#define GMT_DC2_MIN_UV 1050000
+#define GMT_DC2_MAX_UV 1500000
+
+#define GMTV1_DC1_MIN_UV 1000000
+#define GMTV1_DC1_MAX_UV 1400000
+
+#define GMTV1_DC2_MIN_UV 1000000
+#define GMTV1_DC2_MAX_UV 1400000
+
+#define DC1_REG_IDX 4
+#define DC2_REG_IDX 4
+#define DC1_REG_SHIFT 4
+#define DC2_REG_SHIFT 0
+#define DC1_REG_MASK 0xf0
+#define DC2_REG_MASK 0xf
+#define LDXEN_REG_IDX 2
+
+#define GMT_LDOX_MIN_UV 1050000
+#define GMT_LDOX_MAX_UV 1500000
+
+#define LDO56_REG_IDX 3
+#define LDO5_REG_SHIFT 2
+#define LDO6_REG_SHIFT 0
+#define LDO5_REG_MASK 0x0c
+#define LDO6_REG_MASK 0x03
+#define GMT_LDO56_MIN_UV 1500000
+#define GMT_LDO56_MAX_UV 3300000
+
+#define SUPPORT_DC_NUM 7
+/*
+#define DEBUG
+*/
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+static unsigned int g_i2cbus_id = 2;
+static unsigned int g_pmic_en = 0;
+static unsigned int gmt_version = 0;
+
+struct gmt_data {
+ struct i2c_client *client;
+ struct gmt2214_dev *gmt_dev;
+ struct device *dev;
+ unsigned int dc1_min_uV;
+ unsigned int dc1_max_uV;
+ unsigned int dc1_current_uV;
+ unsigned int dc2_current_uV;
+ unsigned int dc2_min_uV;
+ unsigned int dc2_max_uV;
+ unsigned char enabled;
+ char regs[14];
+ struct regulator_dev *rdev;
+};
+static struct gmt_data *g_gmtdata;
+static unsigned int delay_time = 400;/*us*/
+static unsigned int swap_dc1_dc2 = 0;
+static int early_suspend_stage = 0;
+
+static int i2cWriteToGMT(struct gmt2214_dev *gmt2214, unsigned char bufferIndex, unsigned char value)
+{
+ return gmt2214_reg_write(gmt2214, bufferIndex, value);
+}
+
+#ifdef DEBUG
+static int i2cReadFromGMT(struct i2c_client *client, unsigned char bufferIndex,
+ unsigned char dataBuffer[], unsigned short dataLength)
+{
+ int ret;
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_NOSTART,
+ .len = 1,
+ .buf = &bufferIndex
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = dataLength,
+ .buf = dataBuffer
+ }
+ };
+
+ memset(dataBuffer, 0x00, dataLength);
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ return ret;
+}
+#endif
+static int gmt_read_i2c(struct gmt2214_dev *gmt_2214,
+ u8 reg, int *rt_value)
+{
+ int err;
+
+ if (!rt_value)
+ return -EINVAL;
+
+ err = gmt2214_reg_read(gmt_2214, reg, rt_value);
+
+ if (err < 0)
+ return err;
+
+ g_gmtdata->regs[reg] = *rt_value;
+ /*
+ printk("reg = %x, value = %x\n", reg, g_gmtdata->regs[reg]);
+ */
+
+ return 0;
+}
+
+static int gmt_dc1_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned *selector)
+{
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ struct gmt2214_dev *gmt_dev = gmt->gmt_dev;
+ u8 dc1_prog = 0;
+ int ret = 0;
+ int retry_count = 10;
+ dc1_prog = gmt->regs[DC1_REG_IDX];
+
+ if (min_uV > gmt->dc1_max_uV || max_uV < gmt->dc1_min_uV)
+ return -EINVAL;
+ if (max_uV > gmt->dc1_max_uV)
+ return -EINVAL;
+
+ if (min_uV < gmt->dc1_min_uV)
+ min_uV = gmt->dc1_min_uV;
+
+ if (gmt_version == 0) {
+ if (min_uV < 1200000) {
+ if ((min_uV - gmt->dc1_min_uV) % 50000)
+ *selector = ((min_uV - gmt->dc1_min_uV) / 50000) + 1;
+ else
+ *selector = (min_uV - gmt->dc1_min_uV) / 50000;
+
+ } else {
+ if ((min_uV - 1200000) % 25000)
+ *selector = ((min_uV - 1200000) / 25000) + 1;
+ else
+ *selector = (min_uV - 1200000) / 25000;
+ *selector += 3;
+ }
+ } else {
+ if (min_uV >= 1400000)
+ *selector = 15;
+ else {
+ if ((min_uV - 1000000) % 25000)
+ *selector = ((min_uV - 1000000) / 25000) + 1;
+ else
+ *selector = (min_uV - 1000000) / 25000;
+ }
+ }
+
+ if (!swap_dc1_dc2) {
+ dc1_prog &= ~DC1_REG_MASK;
+ dc1_prog |= *selector << DC1_REG_SHIFT;
+ } else {
+ dc1_prog &= ~DC2_REG_MASK;
+ dc1_prog |= *selector << DC2_REG_SHIFT;
+ }
+
+ /*
+ printk("changing voltage dc1 to %duv\n",
+ min_uV);
+ printk("dc1_prog = %x\n", dc1_prog);
+ */
+
+ if (early_suspend_stage == 1)
+ return ret;
+
+ while (retry_count > 0) {
+ ret = i2cWriteToGMT(gmt_dev, DC1_REG_IDX, dc1_prog);
+ if (ret >= 0)
+ break;
+ --retry_count;
+ }
+ if (ret < 0)
+ return ret;
+ gmt->regs[DC1_REG_IDX] = dc1_prog;
+ usleep_range(delay_time, delay_time + 50);
+ gmt->dc1_current_uV = min_uV;
+ return ret;
+
+}
+static int gmt_dc1_update_voltage(void)
+{
+ unsigned int tmp_buf = 0;
+ int current_uV;
+ int ret = 0;
+ ret = gmt_read_i2c(g_gmtdata->gmt_dev, DC1_REG_IDX, &tmp_buf);
+ if (!swap_dc1_dc2) {
+ tmp_buf &= DC1_REG_MASK;
+ tmp_buf >>= DC1_REG_SHIFT;
+ } else {
+ tmp_buf &= DC2_REG_MASK;
+ tmp_buf >>= DC2_REG_SHIFT;
+ }
+
+ if (gmt_version == 0) {
+ if (tmp_buf < 3)
+ current_uV = 1050000 + tmp_buf * 50000;
+ else {
+ tmp_buf -= 3;
+ current_uV = 1200000 + tmp_buf * 25000;
+ }
+ } else {
+ if (tmp_buf == 0xf)
+ current_uV = 1400000;
+ else
+ current_uV = 1000000 + tmp_buf * 25000;
+ }
+ g_gmtdata->dc1_current_uV = current_uV;
+ return ret;
+}
+
+static int gmt_dc1_get_voltage(struct regulator_dev *rdev)
+{
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ u32 tmp_buf = 0;
+ unsigned int current_uV;
+ int ret = 0;
+
+ ret = gmt_read_i2c(gmt->gmt_dev, DC1_REG_IDX, &tmp_buf);
+ if (!swap_dc1_dc2) {
+ tmp_buf &= DC1_REG_MASK;
+ tmp_buf >>= DC1_REG_SHIFT;
+ } else {
+ tmp_buf &= DC2_REG_MASK;
+ tmp_buf >>= DC2_REG_SHIFT;
+ }
+ if (gmt_version == 0) {
+ if (tmp_buf < 3)
+ current_uV = 1050000 + tmp_buf * 50000;
+ else {
+ tmp_buf -= 3;
+ current_uV = 1200000 + tmp_buf * 25000;
+ }
+ } else {
+ if (tmp_buf == 0xf)
+ current_uV = 1400000;
+ else
+ current_uV = 1000000 + tmp_buf * 25000;
+ }
+ gmt->dc1_current_uV = current_uV;
+ return gmt->dc1_current_uV;
+}
+
+static int gmt_dc1_is_enabled(struct regulator_dev *rdev)
+{
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ if (gmt->enabled == 1)
+ return 0;
+ else
+ return -EDOM;
+ return 0;
+}
+
+static int _gmt_dc2_set_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ struct gmt2214_dev *gmt_dev = gmt->gmt_dev;
+ int retry_count = 10;
+ unsigned selector;
+ int ret = 0;
+ u8 dc2_prog;
+ dc2_prog = gmt->regs[DC2_REG_IDX];
+ /*
+ if ((uV - gmt->dc2_min_uV) % 25000)
+ selector = ((uV - gmt->dc2_min_uV) / 25000) + 1;
+ else
+ selector = (uV - gmt->dc2_min_uV) / 25000;
+ */
+ if (gmt_version == 0) {
+ if (uV < 1200000) {
+ if ((uV - gmt->dc2_min_uV) % 50000)
+ selector = ((uV - gmt->dc2_min_uV) / 50000) + 1;
+ else
+ selector = (uV - gmt->dc2_min_uV) / 50000;
+
+ } else {
+ if ((uV - 1200000) % 25000)
+ selector = ((uV - 1200000) / 25000) + 1;
+ else
+ selector = (uV - 1200000) / 25000;
+ selector += 3;
+ }
+ } else {
+ if (uV >= 1400000)
+ selector = 15;
+ else {
+ if ((uV - 1000000) % 25000)
+ selector = ((uV - 1000000) / 25000) + 1;
+ else
+ selector = (uV - 1000000) / 25000;
+ }
+ }
+
+ if (!swap_dc1_dc2) {
+ dc2_prog &= ~DC2_REG_MASK;
+ dc2_prog |= selector << DC2_REG_SHIFT;
+ } else {
+ dc2_prog &= ~DC1_REG_MASK;
+ dc2_prog |= selector << DC1_REG_SHIFT;
+ }
+
+ /*
+ printk("dc2_prog = %x\n", dc2_prog);
+ */
+ while (retry_count > 0) {
+ ret = i2cWriteToGMT(gmt_dev, DC2_REG_IDX, dc2_prog);
+ if (ret >= 0)
+ break;
+ --retry_count;
+ }
+ gmt->regs[DC2_REG_IDX] = dc2_prog;
+ return selector;
+}
+extern void wmt_mc5_autotune(void);
+static int gmt_dc2_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned *selector)
+{
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ int ret = 0;
+ int i = 0;
+ int adj_uV;
+
+ if (min_uV > gmt->dc2_max_uV || max_uV < gmt->dc2_min_uV)
+ return -EINVAL;
+ if (max_uV > gmt->dc2_max_uV)
+ return -EINVAL;
+
+ if (min_uV < gmt->dc2_min_uV)
+ min_uV = gmt->dc2_min_uV;
+ /*
+ printk("current_uV = %d, min_uV = %d\n", gmt->dc2_current_uV, min_uV);
+ */
+ if (gmt->dc2_current_uV > min_uV) {
+ while (gmt->dc2_current_uV >= min_uV + i * 50000) {
+ if (i != 0) {
+ adj_uV = gmt->dc2_current_uV - i * 50000;
+ /*
+ printk("adj_uV = %d\n", adj_uV);
+ */
+ *selector = _gmt_dc2_set_voltage(rdev, adj_uV);
+ usleep_range(delay_time, delay_time + 50);
+ wmt_mc5_autotune();
+ }
+ ++i;
+ }
+ --i;
+ if (gmt->dc2_current_uV != min_uV + i * 50000) {
+ adj_uV = min_uV;
+ *selector = _gmt_dc2_set_voltage(rdev, adj_uV);
+ usleep_range(delay_time, delay_time + 50);
+ }
+ } else {
+ while (gmt->dc2_current_uV + i * 50000 <= min_uV) {
+ if (i != 0) {
+ adj_uV = gmt->dc2_current_uV + i * 50000;
+ /*
+ printk("adj uV = %d\n", adj_uV);
+ */
+ *selector = _gmt_dc2_set_voltage(rdev, gmt->dc2_current_uV + i * 50000);
+ usleep_range(delay_time, delay_time + 50);
+ wmt_mc5_autotune();
+ }
+ ++i;
+ }
+ --i;
+ if (gmt->dc2_current_uV != min_uV + i * 50000) {
+ adj_uV = min_uV;
+ *selector = _gmt_dc2_set_voltage(rdev, adj_uV);
+ usleep_range(delay_time, delay_time + 50);
+ }
+ }
+
+ gmt->dc2_current_uV = min_uV;
+ return ret;
+}
+static unsigned int gmt_read_version(void)
+{
+ unsigned int ver = 0;
+ int ret = 0;
+ ret = gmt_read_i2c(g_gmtdata->gmt_dev, 13, &ver);
+ ver &= 0x30;
+ ver >>= 4;
+ return ver;
+#if 0
+ unsigned int ver = 0;
+ int ret = 0;
+ g_gmtdata->gmt_dev->i2c->addr = 0x01;
+ while(g_gmtdata->gmt_dev->i2c->addr < 0x80){
+ printk("addr:0x%x, ret: %d\n", g_gmtdata->gmt_dev->i2c->addr, gmt_read_i2c(g_gmtdata->gmt_dev, 13, &ver));
+ ver &= 0x30;
+ ver >>= 4;
+ g_gmtdata->gmt_dev->i2c->addr++;
+ }
+ return ver;
+#endif
+}
+
+static int gmt_dc2_update_voltage(void)
+{
+ unsigned int tmp_buf = 0;
+ int current_uV;
+ int ret = 0;
+
+ ret = gmt_read_i2c(g_gmtdata->gmt_dev, DC2_REG_IDX, &tmp_buf);
+
+ if (!swap_dc1_dc2) {
+ tmp_buf &= DC2_REG_MASK;
+ tmp_buf >>= DC2_REG_SHIFT;
+ } else {
+ tmp_buf &= DC1_REG_MASK;
+ tmp_buf >>= DC1_REG_SHIFT;
+ }
+ if (gmt_version == 0) {
+ if (tmp_buf < 3)
+ current_uV = 1050000 + tmp_buf * 50000;
+ else {
+ tmp_buf -= 3;
+ current_uV = 1200000 + tmp_buf * 25000;
+ }
+ } else {
+ if (tmp_buf == 0xf)
+ current_uV = 1400000;
+ else
+ current_uV = 1000000 + tmp_buf * 25000;
+ }
+
+ g_gmtdata->dc2_current_uV = current_uV;
+ return ret;
+}
+
+static int gmt_dc2_get_voltage(struct regulator_dev *rdev)
+{
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ u32 tmp_buf = 0;
+ unsigned int current_uV;
+ int ret = 0;
+ ret = gmt_read_i2c(gmt->gmt_dev, DC2_REG_IDX, &tmp_buf);
+
+ if (!swap_dc1_dc2) {
+ tmp_buf &= DC2_REG_MASK;
+ tmp_buf >>= DC2_REG_SHIFT;
+ } else {
+ tmp_buf &= DC1_REG_MASK;
+ tmp_buf >>= DC1_REG_SHIFT;
+ }
+ if (gmt_version == 0) {
+ if (tmp_buf < 3)
+ current_uV = 1050000 + tmp_buf * 50000;
+ else {
+ tmp_buf -= 3;
+ current_uV = 1200000 + tmp_buf * 25000;
+ }
+ } else {
+ if (tmp_buf == 0xf)
+ current_uV = 1400000;
+ else
+ current_uV = 1000000 + tmp_buf * 25000;
+ }
+
+ gmt->dc2_current_uV = current_uV;
+ return gmt->dc2_current_uV;
+}
+
+static int recovery_ldox_state(void)
+{
+ unsigned int ret = 0;
+ ret = i2cWriteToGMT(g_gmtdata->gmt_dev, LDXEN_REG_IDX, g_gmtdata->regs[LDXEN_REG_IDX]);
+ return ret;
+}
+static int update_ldox_state(void)
+{
+ unsigned int tmp_buf;
+ unsigned int ret;
+ ret = gmt_read_i2c(g_gmtdata->gmt_dev, LDXEN_REG_IDX, &tmp_buf);
+ g_gmtdata->regs[LDXEN_REG_IDX] = (unsigned char)tmp_buf;
+ return ret;
+}
+static int gmt_dc2_is_enabled(struct regulator_dev *rdev)
+{
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ if (gmt->enabled == 1)
+ return 0;
+ else
+ return -EDOM;
+ return 0;
+}
+static int gmt_ldox_enable(struct regulator_dev *rdev, unsigned int ldo_num)
+{
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ unsigned char reg_val;
+ unsigned int tmp_buf;
+ if (ldo_num < 2 || ldo_num > 6)
+ return -1;
+ gmt_read_i2c(g_gmtdata->gmt_dev, LDXEN_REG_IDX, &tmp_buf);
+ gmt->regs[LDXEN_REG_IDX] = (unsigned char)tmp_buf;
+ reg_val = gmt->regs[LDXEN_REG_IDX];
+ switch (ldo_num) {
+ case 2:
+ reg_val |= BIT7;
+ i2cWriteToGMT(gmt->gmt_dev, LDXEN_REG_IDX, reg_val);
+ gmt->regs[LDXEN_REG_IDX] |= BIT7;
+ break;
+ case 3:
+ reg_val |= BIT6;
+ i2cWriteToGMT(gmt->gmt_dev, LDXEN_REG_IDX, reg_val);
+ gmt->regs[LDXEN_REG_IDX] |= BIT6;
+ break;
+ case 4:
+ reg_val |= BIT5;
+ i2cWriteToGMT(gmt->gmt_dev, LDXEN_REG_IDX, reg_val);
+ gmt->regs[LDXEN_REG_IDX] |= BIT5;
+ break;
+ case 5:
+ reg_val |= BIT4;
+ i2cWriteToGMT(gmt->gmt_dev, LDXEN_REG_IDX, reg_val);
+ gmt->regs[LDXEN_REG_IDX] |= BIT4;
+ break;
+ case 6:
+ reg_val |= BIT3;
+ i2cWriteToGMT(gmt->gmt_dev, LDXEN_REG_IDX, reg_val);
+ gmt->regs[LDXEN_REG_IDX] |= BIT3;
+ break;
+ }
+
+ return 0;
+}
+
+static int gmt_ldox_disable(struct regulator_dev *rdev, unsigned int ldo_num)
+{
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ unsigned char reg_val;
+ unsigned int tmp_buf = 0;
+ if (ldo_num < 2 || ldo_num > 6)
+ return -1;
+ gmt_read_i2c(g_gmtdata->gmt_dev, LDXEN_REG_IDX, &tmp_buf);
+ gmt->regs[LDXEN_REG_IDX] = (unsigned char)tmp_buf;
+ reg_val = gmt->regs[LDXEN_REG_IDX];
+ switch (ldo_num) {
+ case 2:
+ reg_val &= ~BIT7;
+ i2cWriteToGMT(gmt->gmt_dev, LDXEN_REG_IDX, reg_val);
+ gmt->regs[LDXEN_REG_IDX] &= ~BIT7;
+ break;
+ case 3:
+ reg_val &= ~BIT6;
+ i2cWriteToGMT(gmt->gmt_dev, LDXEN_REG_IDX, reg_val);
+ gmt->regs[LDXEN_REG_IDX] &= ~BIT6;
+ break;
+ case 4:
+ reg_val &= ~BIT5;
+ i2cWriteToGMT(gmt->gmt_dev, LDXEN_REG_IDX, reg_val);
+ gmt->regs[LDXEN_REG_IDX] &= ~BIT5;
+ break;
+ case 5:
+ reg_val &= ~BIT4;
+ i2cWriteToGMT(gmt->gmt_dev, LDXEN_REG_IDX, reg_val);
+ gmt->regs[LDXEN_REG_IDX] &= ~BIT4;
+ break;
+ case 6:
+ reg_val &= ~BIT3;
+ i2cWriteToGMT(gmt->gmt_dev, LDXEN_REG_IDX, reg_val);
+ gmt->regs[LDXEN_REG_IDX] &= ~BIT3;
+ break;
+ }
+
+ return 0;
+}
+static int get_ldox_state(struct regulator_dev *rdev, unsigned int ldo_num)
+{
+ unsigned int tmp_buf = 0;
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ if (ldo_num < 2 || ldo_num > 6)
+ return -1;
+ gmt_read_i2c(gmt->gmt_dev, LDXEN_REG_IDX, &tmp_buf);
+ if (tmp_buf & (0x1 << (9 - ldo_num)))
+ return 1;/*enabled*/
+ else
+ return 0;
+}
+static int gmt_ldo2_is_enabled(struct regulator_dev *rdev)
+{
+ return get_ldox_state(rdev, 2);
+}
+static int gmt_ldo2_enable(struct regulator_dev *rdev)
+{
+ gmt_ldox_enable(rdev, 2);
+ return 0;
+}
+
+static int gmt_ldo2_disable(struct regulator_dev *rdev)
+{
+ gmt_ldox_disable(rdev, 2);
+ return 0;
+}
+
+static int gmt_ldo3_is_enabled(struct regulator_dev *rdev)
+{
+ return get_ldox_state(rdev, 3);
+}
+static int gmt_ldo3_enable(struct regulator_dev *rdev)
+{
+ gmt_ldox_enable(rdev, 3);
+ return 0;
+}
+
+static int gmt_ldo3_disable(struct regulator_dev *rdev)
+{
+ gmt_ldox_disable(rdev, 3);
+ return 0;
+}
+
+static int gmt_ldo4_is_enabled(struct regulator_dev *rdev)
+{
+ return get_ldox_state(rdev, 4);
+}
+static int gmt_ldo4_enable(struct regulator_dev *rdev)
+{
+ gmt_ldox_enable(rdev, 4);
+ return 0;
+}
+
+static int gmt_ldo4_disable(struct regulator_dev *rdev)
+{
+ gmt_ldox_disable(rdev, 4);
+ return 0;
+}
+
+static int gmt_ldo5_is_enabled(struct regulator_dev *rdev)
+{
+ return get_ldox_state(rdev, 5);
+}
+static int gmt_ldo5_enable(struct regulator_dev *rdev)
+{
+ gmt_ldox_enable(rdev, 5);
+ return 0;
+}
+
+static int gmt_ldo5_disable(struct regulator_dev *rdev)
+{
+ gmt_ldox_disable(rdev, 5);
+ return 0;
+}
+
+static int gmt_ldo6_is_enabled(struct regulator_dev *rdev)
+{
+ return get_ldox_state(rdev, 6);
+}
+static int gmt_ldo6_enable(struct regulator_dev *rdev)
+{
+ gmt_ldox_enable(rdev, 6);
+ return 0;
+}
+
+static int gmt_ldo6_disable(struct regulator_dev *rdev)
+{
+ gmt_ldox_disable(rdev, 6);
+ return 0;
+}
+
+static int gmt_ldo56_list_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+ int ret;
+ switch (selector) {
+ case 0:
+ ret = 1500000;
+ break;
+ case 1:
+ ret = 1800000;
+ break;
+ case 2:
+ ret = 2800000;
+ break;
+ case 3:
+ ret = 3300000;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+static int gmt_ldo56_set_voltage_sel(struct regulator_dev *rdev, unsigned selector)
+{
+ struct gmt_data *gmt = rdev_get_drvdata(rdev);
+ const char *rdev_name = rdev->desc->name;
+ u8 reg_val = gmt->regs[LDO56_REG_IDX];
+
+ if (!strcmp(rdev_name, "ldo5")) {
+ reg_val &= ~LDO5_REG_MASK;
+ reg_val |= ((selector << LDO5_REG_SHIFT) & LDO5_REG_MASK);
+ } else if (!strcmp(rdev_name, "ldo6")) {
+ reg_val &= ~LDO6_REG_MASK;
+ reg_val |= ((selector << LDO6_REG_SHIFT) & LDO6_REG_MASK);
+ } else
+ return -EINVAL;
+
+ i2cWriteToGMT(gmt->gmt_dev, LDO56_REG_IDX, reg_val);
+ gmt->regs[LDO56_REG_IDX] = reg_val;
+ return 0;
+}
+
+static struct regulator_ops gmt_dc1_ops = {
+ .set_voltage = gmt_dc1_set_voltage,
+ .is_enabled = gmt_dc1_is_enabled,
+ .get_voltage = gmt_dc1_get_voltage,
+};
+
+static struct regulator_ops gmt_dc2_ops = {
+ .set_voltage = gmt_dc2_set_voltage,
+ .is_enabled = gmt_dc2_is_enabled,
+ .get_voltage = gmt_dc2_get_voltage,
+};
+
+static struct regulator_ops gmt_ldo2_ops = {
+ .enable = gmt_ldo2_enable,
+ .disable = gmt_ldo2_disable,
+ .is_enabled = gmt_ldo2_is_enabled,
+};
+
+static struct regulator_ops gmt_ldo3_ops = {
+ .enable = gmt_ldo3_enable,
+ .disable = gmt_ldo3_disable,
+ .is_enabled = gmt_ldo3_is_enabled,
+};
+
+static struct regulator_ops gmt_ldo4_ops = {
+ .enable = gmt_ldo4_enable,
+ .disable = gmt_ldo4_disable,
+ .is_enabled = gmt_ldo4_is_enabled,
+};
+
+static struct regulator_ops gmt_ldo5_ops = {
+ .enable = gmt_ldo5_enable,
+ .disable = gmt_ldo5_disable,
+ .is_enabled = gmt_ldo5_is_enabled,
+ .list_voltage = gmt_ldo56_list_voltage,
+ .set_voltage_sel = gmt_ldo56_set_voltage_sel,
+};
+
+static struct regulator_ops gmt_ldo6_ops = {
+ .enable = gmt_ldo6_enable,
+ .disable = gmt_ldo6_disable,
+ .is_enabled = gmt_ldo6_is_enabled,
+ .list_voltage = gmt_ldo56_list_voltage,
+ .set_voltage_sel = gmt_ldo56_set_voltage_sel,
+};
+
+static struct regulator_desc gmt_reg[] = {
+ {
+ .name = "wmt_corepower",
+ .id = 0,
+ .ops = &gmt_dc1_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = GMT_DC1_MAX_STAGE,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "wmt_vdd",
+ .id = 0,
+ .ops = &gmt_dc2_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = GMT_DC2_MAX_STAGE,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo2",
+ .id = 0,
+ .ops = &gmt_ldo2_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = GMT_LDOX_MAX_STAGE,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo3",
+ .id = 0,
+ .ops = &gmt_ldo3_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = GMT_LDOX_MAX_STAGE,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo4",
+ .id = 0,
+ .ops = &gmt_ldo4_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = GMT_LDOX_MAX_STAGE,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo5",
+ .id = 0,
+ .ops = &gmt_ldo5_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = GMT_LDO56_MAX_STAGE,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo6",
+ .id = 0,
+ .ops = &gmt_ldo6_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = GMT_LDO56_MAX_STAGE,
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct regulator_consumer_supply gmt_dc1_supply =
+ REGULATOR_SUPPLY("wmt_corepower", NULL);
+static struct regulator_consumer_supply gmt_dc2_supply =
+ REGULATOR_SUPPLY("wmt_vdd", NULL);
+static struct regulator_consumer_supply gmt_ldo2_supply =
+ REGULATOR_SUPPLY("ldo2", NULL);
+static struct regulator_consumer_supply gmt_ldo3_supply =
+ REGULATOR_SUPPLY("ldo3", NULL);
+static struct regulator_consumer_supply gmt_ldo4_supply =
+ REGULATOR_SUPPLY("ldo4", NULL);
+static struct regulator_consumer_supply gmt_ldo5_supply =
+ REGULATOR_SUPPLY("ldo5", NULL);
+static struct regulator_consumer_supply gmt_ldo6_supply =
+ REGULATOR_SUPPLY("ldo6", NULL);
+
+static struct regulator_init_data gmt_dc1_power = {
+ .constraints = {
+ .min_uV = GMT_DC1_MIN_UV,
+ .max_uV = GMT_DC1_MAX_UV,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &gmt_dc1_supply,
+};
+static struct regulator_init_data gmtv1_dc1_power = {
+ .constraints = {
+ .min_uV = GMTV1_DC1_MIN_UV,
+ .max_uV = GMTV1_DC1_MAX_UV,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &gmt_dc1_supply,
+};
+
+static struct regulator_init_data gmt_dc2_power = {
+ .constraints = {
+ .min_uV = GMT_DC2_MIN_UV,
+ .max_uV = GMT_DC2_MAX_UV,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &gmt_dc2_supply,
+};
+static struct regulator_init_data gmtv1_dc2_power = {
+ .constraints = {
+ .min_uV = GMTV1_DC2_MIN_UV,
+ .max_uV = GMTV1_DC2_MAX_UV,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &gmt_dc2_supply,
+};
+
+static struct regulator_init_data gmt_ldo2_power = {
+ .constraints = {
+ .min_uV = GMT_LDOX_MIN_UV,
+ .max_uV = GMT_LDOX_MAX_UV,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &gmt_ldo2_supply,
+};
+static struct regulator_init_data gmt_ldo3_power = {
+ .constraints = {
+ .min_uV = GMT_LDOX_MIN_UV,
+ .max_uV = GMT_LDOX_MAX_UV,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &gmt_ldo3_supply,
+};
+static struct regulator_init_data gmt_ldo4_power = {
+ .constraints = {
+ .min_uV = GMT_LDOX_MIN_UV,
+ .max_uV = GMT_LDOX_MAX_UV,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &gmt_ldo4_supply,
+};
+static struct regulator_init_data gmt_ldo5_power = {
+ .constraints = {
+ .min_uV = GMT_LDO56_MIN_UV,
+ .max_uV = GMT_LDO56_MAX_UV,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &gmt_ldo5_supply,
+};
+static struct regulator_init_data gmt_ldo6_power = {
+ .constraints = {
+ .min_uV = GMT_LDO56_MIN_UV,
+ .max_uV = GMT_LDO56_MAX_UV,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &gmt_ldo6_supply,
+};
+#ifdef CONFIG_PM
+static int g2214_read(u8 reg)
+{
+ unsigned int rt_value =0;
+
+ gmt2214_reg_read(g_gmtdata->gmt_dev, reg, &rt_value);
+
+ return rt_value;
+}
+
+static void g2214reg_dump(void)
+{
+ printk("reg A%d: 0x%x\n ", 0, g2214_read(0));
+ printk("reg A%d: 0x%x\n ", 1, g2214_read(1));
+ printk("reg A%d: 0x%x\n ", 2, g2214_read(2));
+ printk("reg A%d: 0x%x\n ", 3, g2214_read(3));
+ printk("reg A%d: 0x%x\n ", 4, g2214_read(4));
+ printk("reg A%d: 0x%x\n ", 5, g2214_read(5));
+ printk("reg A%d: 0x%x\n ", 6, g2214_read(6));
+ printk("reg A%d: 0x%x\n ", 7, g2214_read(7));
+ printk("reg A%d: 0x%x\n ", 8, g2214_read(8));
+ printk("reg A%d: 0x%x\n ", 9, g2214_read(9));
+ printk("reg A%d: 0x%x\n ", 10, g2214_read(10));
+ printk("reg A%d: 0x%x\n ", 11, g2214_read(11));
+ printk("reg A%d: 0x%x\n ", 12, g2214_read(12));
+ printk("reg A%d: 0x%x\n ", 13, g2214_read(13));
+}
+static int gmt_i2c_suspend(struct device *dev)
+{
+ int ret=0;
+
+ g_gmtdata->enabled = 0;
+ early_suspend_stage = 1;
+
+ printk(KERN_ERR "%s: =============\n", __func__);
+#ifdef G2214_DUMP
+ g2214reg_dump();
+#endif
+
+ if (ret) {
+ printk(KERN_ERR "%s: Buck mode can't set to be auto mode.\n", __func__);
+ return ret;
+ }
+ return ret;
+}
+static int gmt_i2c_resume(struct device *dev)
+{
+ int ret=0;
+ printk(KERN_ERR "%s: =============\n", __func__);
+
+ if (ret) {
+ printk(KERN_ERR "%s: Buck mode can't set to be force PWM mode.\n", __func__);
+ return ret;
+ }
+
+ gmt_dc2_update_voltage();
+ gmt_dc1_update_voltage();
+ recovery_ldox_state();
+ g_gmtdata->enabled = 1;
+
+#ifdef G2214_DUMP
+ g2214reg_dump();
+#endif
+ early_suspend_stage = 0;
+ return ret;
+}
+static SIMPLE_DEV_PM_OPS(gmt_dev_pm_ops,
+ gmt_i2c_suspend, gmt_i2c_resume);
+#endif
+
+static int __devinit gmt_pmic_probe(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev;
+ struct regulator_init_data *initdata;
+ struct gmt_data *gmt;
+ int ret = 0;
+
+ gmt = kzalloc(sizeof(struct gmt_data) +
+ SUPPORT_DC_NUM * sizeof(struct regulator_dev *),
+ GFP_KERNEL);
+ if (!gmt)
+ goto out;
+
+ gmt->gmt_dev = dev_get_drvdata(pdev->dev.parent);
+ gmt->dev = &pdev->dev;
+
+ gmt->enabled = 1;
+
+ g_gmtdata = gmt;
+ gmt_version = gmt_read_version();
+ rdev = &gmt->rdev[0];
+ if (gmt_version == 1)
+ initdata = &gmtv1_dc1_power;
+ else
+ initdata = &gmt_dc1_power;
+ rdev = regulator_register(&gmt_reg[0], gmt->dev,
+ initdata,
+ gmt,
+ NULL);
+ rdev = &gmt->rdev[1];
+ if (gmt_version == 1)
+ initdata = &gmtv1_dc2_power;
+ else
+ initdata = &gmt_dc2_power;
+ rdev = regulator_register(&gmt_reg[1], gmt->dev,
+ initdata,
+ gmt,
+ NULL);
+ rdev = &gmt->rdev[2];
+ initdata = &gmt_ldo2_power;
+ rdev = regulator_register(&gmt_reg[2], gmt->dev,
+ initdata,
+ gmt,
+ NULL);
+ rdev = &gmt->rdev[3];
+ initdata = &gmt_ldo3_power;
+ rdev = regulator_register(&gmt_reg[3], gmt->dev,
+ initdata,
+ gmt,
+ NULL);
+ rdev = &gmt->rdev[4];
+ initdata = &gmt_ldo4_power;
+ rdev = regulator_register(&gmt_reg[4], gmt->dev,
+ initdata,
+ gmt,
+ NULL);
+ rdev = &gmt->rdev[5];
+ initdata = &gmt_ldo5_power;
+ rdev = regulator_register(&gmt_reg[5], gmt->dev,
+ initdata,
+ gmt,
+ NULL);
+ rdev = &gmt->rdev[6];
+ initdata = &gmt_ldo6_power;
+ rdev = regulator_register(&gmt_reg[6], gmt->dev,
+ initdata,
+ gmt,
+ NULL);
+
+ platform_set_drvdata(pdev, gmt);
+ if (gmt_version == 0) {
+ gmt->dc1_min_uV = GMT_DC1_MIN_UV;
+ gmt->dc1_max_uV = GMT_DC1_MAX_UV;
+ gmt->dc2_min_uV = GMT_DC2_MIN_UV;
+ gmt->dc2_max_uV = GMT_DC2_MAX_UV;
+ } else {
+ gmt->dc1_min_uV = GMTV1_DC1_MIN_UV;
+ gmt->dc1_max_uV = GMTV1_DC1_MAX_UV;
+ gmt->dc2_min_uV = GMTV1_DC2_MIN_UV;
+ gmt->dc2_max_uV = GMTV1_DC2_MAX_UV;
+ }
+ gmt_dc2_update_voltage();
+ gmt_dc1_update_voltage();
+ update_ldox_state();
+
+ printk("GMT PMIC ver:0x%x\n", gmt_version);
+ dev_info(gmt->dev, "GMT regulator driver loaded\n");
+ return 0;
+
+out:
+ return ret;
+}
+
+static int __devexit gmt_pmic_remove(struct platform_device *pdev)
+{
+ struct gmt_data *gmt = platform_get_drvdata(pdev);
+
+ regulator_unregister(gmt->rdev);
+ kfree(gmt);
+
+ return 0;
+}
+
+#define par_len 80
+static int parse_pmic_param(void)
+{
+ int retval;
+ unsigned char buf[par_len];
+ unsigned char tmp_buf[par_len];
+ int i = 0;
+ int j = 0;
+ int varlen = par_len;
+ char *varname = "wmt.pmic.param";
+ unsigned int pmic_en = 0;
+ retval = wmt_getsyspara(varname, buf, &varlen);
+ if (retval == 0) {
+ pmic_en = (buf[i] - '0' == 1)?1:0;
+ if (pmic_en == 0) {/*disable*/
+ g_pmic_en = 0;
+ return -1;
+ }
+ i += 2;
+ for (; i < par_len; ++i) {
+ if (buf[i] == ':')
+ break;
+ tmp_buf[j] = buf[i];
+ ++j;
+ }
+ if (strncmp(tmp_buf,"gmt2214",7)) {
+ g_pmic_en = 0;
+ return -1;
+ }
+ ++i;
+
+ sscanf((buf + i), "%d:%d:%d", &g_i2cbus_id, &delay_time, &swap_dc1_dc2);
+ } else {
+ g_pmic_en = 0;
+ return -1;
+ }
+ g_pmic_en = 1;
+ return 0;
+}
+
+static struct platform_driver gmt_pmic_driver = {
+ .probe = gmt_pmic_probe,
+ .remove = __devexit_p(gmt_pmic_remove),
+ .driver = {
+ .name = "gmt-pmic",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &gmt_dev_pm_ops,
+#endif
+ },
+
+};
+static int __init gmt_pmic_init(void)
+{
+ parse_pmic_param();
+ printk("GMT2214_PMIC_INIT\n");
+ if (g_pmic_en == 0) {
+ printk("No gmt pmic\n");
+ return -ENODEV;
+ }
+ return platform_driver_register(&gmt_pmic_driver);
+}
+module_init(gmt_pmic_init);
+
+static void __exit gmt_pmic_exit(void)
+{
+ platform_driver_unregister(&gmt_pmic_driver);
+}
+module_exit(gmt_pmic_exit);
+
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_DESCRIPTION("GMT2214 Driver");
+MODULE_LICENSE("GPL");