/*++ drivers/regulator/upi6631.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 . WonderMedia Technologies, Inc. 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. --*/ #include #include #include #include #include #include #ifdef CONFIG_HAS_EARLYSUSPEND #include #endif #include #include #include #include #include #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 UPI_BUCK4_MAX_STAGE 21 #define UPI_BUCK3_MAX_STAGE 21 #define MAX1586_V6_MAX_VSEL 3 #define UPI_BUCK4_MIN_UV 1000000 #define UPI_BUCK4_MAX_UV 1500000 #define UPI_BUCK3_MIN_UV 1000000 #define UPI_BUCK3_MAX_UV 1500000 #define BUCK3_REG_IDX 1 #define BUCK4_REG_IDX 2 #define BUCK5_REG_IDX 3 #define SUPPORT_BUCK_NUM 2 /* #define DEBUG */ #ifdef CONFIG_MTD_WMT_SF extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); #endif static unsigned int g_i2cbus_id = 2; static unsigned int g_pmic_en = 0; struct upi_data { struct i2c_client *client; unsigned int min_uV; unsigned int max_uV; unsigned int current_uV;/*buck4*/ unsigned int buck3_current_uV; unsigned char enabled; unsigned int pwm_mode; struct regulator_dev *rdev; }; struct upi_data *g_upidata; DECLARE_WAIT_QUEUE_HEAD(upi6631_wait); static unsigned int delay_time = 400;/*us*/ static int early_suspend_stage = 0; #ifdef CONFIG_HAS_EARLYSUSPEND static struct early_suspend upi_pmic_early_suspend; #endif static int i2cWriteToUPI(struct i2c_client *client, unsigned char bufferIndex, unsigned char const dataBuffer[], unsigned short dataLength) { unsigned char buffer4Write[256]; struct i2c_msg msgs[1] = { { .addr = client->addr, .flags = 0, .len = dataLength + 1, .buf = buffer4Write } }; buffer4Write[0] = bufferIndex; memcpy(&(buffer4Write[1]), dataBuffer, dataLength); return i2c_transfer(client->adapter, msgs, 1); } #ifdef DEBUG static int i2cReadFromUPI(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; } static int upi_read_ready(struct i2c_client *client) { unsigned char buf[1]; buf[0] = 0; i2cReadFromUPI(client, BUCK4_REG_IDX, buf, 1); if (buf[0] & 0x20) return 0; return -1; } #endif int upi6631_read_i2c(struct i2c_client *client, u8 reg, int *rt_value, int b_single) { struct i2c_msg msg[2]; unsigned char data[4]; int err; if (!client || !client->adapter) return -ENODEV; if (!rt_value) return -EINVAL; data[0] = reg; msg[0].addr = client->addr; msg[0].flags = 0 | I2C_M_NOSTART; msg[0].len = 1; msg[0].buf = (unsigned char *)data; msg[1].addr = client->addr; msg[1].flags = (I2C_M_RD); msg[1].len = b_single ? 1 : 2; msg[1].buf = (unsigned char *)data; err = i2c_transfer(client->adapter, msg, sizeof(msg)/sizeof(struct i2c_msg)); if (err < 0) return err; if (b_single) *rt_value = data[0]; else *rt_value = get_unaligned_le16(data); return 0; } static int upi6631_set_PWM_mode(struct i2c_client *i2c) { u32 tmp_buf=0; int ret=0; struct i2c_client *client = NULL; printk("%s\n", __func__); if (i2c) client = i2c; else client = g_upidata->client; ret = upi6631_read_i2c(client, BUCK3_REG_IDX, &tmp_buf, 1); if (ret < 0) return ret; DPRINTK("%d = 0x%02X\n", BUCK3_REG_IDX, tmp_buf); tmp_buf |= BIT6; ret = i2cWriteToUPI(client, BUCK3_REG_IDX, (unsigned char const *)&tmp_buf, 1); if (ret < 0) return ret; ret = upi6631_read_i2c(client, BUCK4_REG_IDX, &tmp_buf, 1); if (ret < 0) return ret; DPRINTK("%d = 0x%02X\n", BUCK4_REG_IDX, tmp_buf); tmp_buf |= BIT6; ret = i2cWriteToUPI(client, BUCK4_REG_IDX, (unsigned char const *)&tmp_buf, 1); if (ret < 0) return ret; g_upidata->pwm_mode = 1; ret = upi6631_read_i2c(client, BUCK5_REG_IDX, &tmp_buf, 1); if (ret < 0) return ret; DPRINTK("%d = 0x%02X\n", BUCK5_REG_IDX, tmp_buf); tmp_buf |= BIT6; ret = i2cWriteToUPI(client, BUCK5_REG_IDX, (unsigned char const *)&tmp_buf, 1); if (ret < 0) return ret; g_upidata->pwm_mode = 1; return 0; } #ifdef CONFIG_PM static int upi6631_set_auto_mode(struct i2c_client *i2c) { u32 tmp_buf = 0; int ret = 0; struct i2c_client *client = NULL; printk("%s\n", __func__); if (i2c) client = i2c; else client = g_upidata->client; ret = upi6631_read_i2c(client, BUCK3_REG_IDX, &tmp_buf, 1); if (ret < 0) return ret; DPRINTK("%d = 0x%02X\n", BUCK3_REG_IDX, tmp_buf); tmp_buf &= ~BIT6; ret = i2cWriteToUPI(client, BUCK3_REG_IDX, (unsigned char const *)&tmp_buf, 1); if (ret < 0) return ret; ret = upi6631_read_i2c(client, BUCK4_REG_IDX, &tmp_buf, 1); if (ret < 0) return ret; DPRINTK("%d = 0x%02X\n", BUCK4_REG_IDX, tmp_buf); tmp_buf &= ~BIT6; ret = i2cWriteToUPI(client, BUCK4_REG_IDX, (unsigned char const *)&tmp_buf, 1); if (ret < 0) return ret; g_upidata->pwm_mode = 0; ret = upi6631_read_i2c(client, BUCK5_REG_IDX, &tmp_buf, 1); if (ret < 0) return ret; DPRINTK("%d = 0x%02X\n", BUCK5_REG_IDX, tmp_buf); tmp_buf &= ~BIT6; ret = i2cWriteToUPI(client, BUCK5_REG_IDX, (unsigned char const *)&tmp_buf, 1); if (ret < 0) return ret; return 0; } #endif static int upi_buck4_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { struct upi_data *upi = rdev_get_drvdata(rdev); struct i2c_client *client = upi->client; u8 buck4_prog; int ret = 0; int retry_count = 10; if (min_uV > upi->max_uV || max_uV < upi->min_uV) return -EINVAL; if (max_uV > upi->max_uV) return -EINVAL; if (min_uV < upi->min_uV) min_uV = upi->min_uV; if ((min_uV - upi->min_uV) % 25000) *selector = ((min_uV - upi->min_uV) / 25000) + 1; else *selector = (min_uV - upi->min_uV) / 25000; buck4_prog = *selector; dev_dbg(&client->dev, "changing voltage buck4 to %duv\n", min_uV); if (early_suspend_stage == 1) return ret; if (upi->pwm_mode == 1) buck4_prog |= BIT6; while (retry_count > 0) { ret = i2cWriteToUPI(client, BUCK4_REG_IDX, &buck4_prog, 1); if (ret >= 0) break; --retry_count; } if (ret < 0) return ret; /* while (upi_read_ready(client) == 1) schedule(); */ usleep_range(delay_time, delay_time + 50); upi->current_uV = min_uV; return ret; } static int upi_buck4_update_voltage(void) { unsigned int tmp_buf = 0; struct i2c_client *client = NULL; int current_uV; int ret = 0; client = g_upidata->client; ret = upi6631_read_i2c(client, BUCK4_REG_IDX, &tmp_buf, 1); tmp_buf &= 0x1F; current_uV = 1000000 + tmp_buf * 25000; g_upidata->current_uV = current_uV; return ret; } static int upi_buck4_get_voltage(struct regulator_dev *rdev) { struct upi_data *upi = rdev_get_drvdata(rdev); u32 tmp_buf = 0; unsigned int current_uV; int ret = 0; struct i2c_client *client = NULL; client = upi->client; ret = upi6631_read_i2c(client, BUCK4_REG_IDX, &tmp_buf, 1); tmp_buf &= 0x1F; current_uV = 1000000 + tmp_buf * 25000; upi->current_uV = current_uV; return upi->current_uV; } static int upi_buck4_is_enabled(struct regulator_dev *rdev) { struct upi_data *upi = rdev_get_drvdata(rdev); if (upi->enabled == 1) return 0; else return -EDOM; return 0; } static int _upi_buck3_set_voltage(struct regulator_dev *rdev, int uV) { struct upi_data *upi = rdev_get_drvdata(rdev); struct i2c_client *client = upi->client; int retry_count = 10; unsigned selector; int ret = 0; u8 buck3_prog; if ((uV - upi->min_uV) % 25000) selector = ((uV - upi->min_uV) / 25000) + 1; else selector = (uV - upi->min_uV) / 25000; buck3_prog = selector; if (upi->pwm_mode == 1) buck3_prog |= BIT6; while (retry_count > 0) { ret = i2cWriteToUPI(client, BUCK3_REG_IDX, &buck3_prog, 1); if (ret >= 0) break; --retry_count; } return selector; } extern void wmt_mc5_autotune(void); static int upi_buck3_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { struct upi_data *upi = rdev_get_drvdata(rdev); int ret = 0; int i = 0; int adj_uV; if (min_uV > upi->max_uV || max_uV < upi->min_uV) return -EINVAL; if (max_uV > upi->max_uV) return -EINVAL; if (min_uV < upi->min_uV) min_uV = upi->min_uV; /* printk("current_uV = %d, min_uV = %d\n", upi->buck3_current_uV, min_uV); */ if (upi->buck3_current_uV > min_uV) { while (upi->buck3_current_uV >= min_uV + i * 50000) { if (i != 0) { adj_uV = upi->buck3_current_uV - i * 50000; /* printk("adj_uV = %d\n", adj_uV); */ *selector = _upi_buck3_set_voltage(rdev, adj_uV); usleep_range(delay_time, delay_time + 50); wmt_mc5_autotune(); } ++i; } --i; if (upi->buck3_current_uV != min_uV + i * 50000) { adj_uV = min_uV; *selector = _upi_buck3_set_voltage(rdev, adj_uV); usleep_range(delay_time, delay_time + 50); } } else { while (upi->buck3_current_uV + i * 50000 <= min_uV) { if (i != 0) { adj_uV = upi->buck3_current_uV + i * 50000; /* printk("adj uV = %d\n", adj_uV); */ *selector = _upi_buck3_set_voltage(rdev, upi->buck3_current_uV + i * 50000); usleep_range(delay_time, delay_time + 50); wmt_mc5_autotune(); } ++i; } --i; if (upi->buck3_current_uV != min_uV + i * 50000) { adj_uV = min_uV; *selector = _upi_buck3_set_voltage(rdev, adj_uV); usleep_range(delay_time, delay_time + 50); } } upi->buck3_current_uV = min_uV; return ret; } static int upi_buck3_update_voltage(void) { unsigned int tmp_buf = 0; struct i2c_client *client = NULL; int current_uV; int ret = 0; client = g_upidata->client; ret = upi6631_read_i2c(client, BUCK3_REG_IDX, &tmp_buf, 1); tmp_buf &= 0x1F; current_uV = 1000000 + tmp_buf * 25000; g_upidata->buck3_current_uV = current_uV; return ret; } static int upi_buck3_get_voltage(struct regulator_dev *rdev) { struct upi_data *upi = rdev_get_drvdata(rdev); u32 tmp_buf = 0; unsigned int current_uV; int ret = 0; struct i2c_client *client = NULL; client = upi->client; ret = upi6631_read_i2c(client, BUCK3_REG_IDX, &tmp_buf, 1); tmp_buf &= 0x1F; current_uV = 1000000 + tmp_buf * 25000; upi->buck3_current_uV = current_uV; return upi->buck3_current_uV; } static int upi_buck3_is_enabled(struct regulator_dev *rdev) { struct upi_data *upi = rdev_get_drvdata(rdev); if (upi->enabled == 1) return 0; else return -EDOM; return 0; } static struct regulator_ops upi_buck4_ops = { .set_voltage = upi_buck4_set_voltage, .is_enabled = upi_buck4_is_enabled, .get_voltage = upi_buck4_get_voltage, }; static struct regulator_ops upi_buck3_ops = { .set_voltage = upi_buck3_set_voltage, .is_enabled = upi_buck3_is_enabled, .get_voltage = upi_buck3_get_voltage, }; static struct regulator_desc upi_reg[] = { { .name = "wmt_corepower", .id = 0, .ops = &upi_buck4_ops, .type = REGULATOR_VOLTAGE, .n_voltages = UPI_BUCK4_MAX_STAGE, .owner = THIS_MODULE, }, { .name = "wmt_vdd", .id = 0, .ops = &upi_buck3_ops, .type = REGULATOR_VOLTAGE, .n_voltages = UPI_BUCK3_MAX_STAGE, .owner = THIS_MODULE, }, }; static struct regulator_consumer_supply upi_buck4_supply = REGULATOR_SUPPLY("wmt_corepower", NULL); static struct regulator_consumer_supply upi_buck3_supply = REGULATOR_SUPPLY("wmt_vdd", NULL); static struct regulator_init_data upi_buck4_power = { .constraints = { .min_uV = UPI_BUCK4_MIN_UV, .max_uV = UPI_BUCK4_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 = &upi_buck4_supply, }; static struct regulator_init_data upi_buck3_power = { .constraints = { .min_uV = UPI_BUCK3_MIN_UV, .max_uV = UPI_BUCK3_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 = &upi_buck3_supply, }; #ifdef DEBUG static int upi_dump_reg() { u32 tmp_buf=0; int ret=0; struct i2c_client *client = g_upidata->client; /* ret = i2cReadFromUPI(client, BUCK3_REG_IDX, (u8 *)&tmp_buf, 1); */ ret = upi6631_read_i2c(client, BUCK3_REG_IDX, &tmp_buf, 1); if (ret < 0) return ret; printk(KERN_INFO "%s: %d = 0x%02X\n", __func__, BUCK3_REG_IDX, tmp_buf); ret = upi6631_read_i2c(client, BUCK4_REG_IDX, &tmp_buf, 1); if (ret < 0) return ret; printk(KERN_INFO "%s: %d = 0x%02X\n", __func__, BUCK4_REG_IDX, tmp_buf); ret = upi6631_read_i2c(client, BUCK5_REG_IDX, &tmp_buf, 1); if (ret < 0) return ret; printk(KERN_INFO "%s: %d = 0x%02X\n", __func__, BUCK5_REG_IDX, tmp_buf); } #endif static void upi6631_delay_init(void) { int ret=0; struct i2c_client *client = g_upidata->client; ret = upi6631_set_PWM_mode(client); if (ret) { printk(KERN_ERR "Can't set 'force PWM mode' .\n"); } /* ret = upi_dump_reg(); if (ret) { printk(KERN_ERR "Dump upi6631 register\n"); return ret; } */ } #ifdef CONFIG_HAS_EARLYSUSPEND static void upi_early_suspend(struct early_suspend *h) { int ret=0; /* struct i2c_client *client = g_upidata->client; u8 buck_reg_val=0; */ g_upidata->enabled = 0; early_suspend_stage = 1; printk(KERN_ERR "%s: =============\n", __func__); ret = upi6631_set_auto_mode(NULL); if (ret) { printk(KERN_ERR "%s: Buck mode can't set to be auto mode.\n", __func__); return; } /* ret = upi_dump_reg(); if (ret) { printk(KERN_ERR "%s: dump error.\n", __func__); return; } */ } static void upi_late_resume(struct early_suspend *h) { int ret=0; printk(KERN_ERR "%s: =============\n", __func__); ret = upi6631_set_PWM_mode(NULL); if (ret) { printk(KERN_ERR "%s: Buck mode can't set to be force PWM mode.\n", __func__); return; } /* ret = upi_dump_reg(); if (ret) { printk(KERN_ERR "%s: dump error.\n", __func__); return; } */ g_upidata->enabled = 1; early_suspend_stage = 0; } #endif #ifdef CONFIG_PM static int upi_i2c_suspend(struct device *dev) { int ret=0; g_upidata->enabled = 0; early_suspend_stage = 1; printk(KERN_ERR "%s: =============\n", __func__); ret = upi6631_set_auto_mode(NULL); if (ret) { printk(KERN_ERR "%s: Buck mode can't set to be auto mode.\n", __func__); return ret; } return ret; } static int upi_i2c_resume(struct device *dev) { int ret=0; printk(KERN_ERR "%s: =============\n", __func__); ret = upi6631_set_PWM_mode(NULL); if (ret) { printk(KERN_ERR "%s: Buck mode can't set to be force PWM mode.\n", __func__); return ret; } upi_buck3_update_voltage(); upi_buck4_update_voltage(); g_upidata->enabled = 1; early_suspend_stage = 0; return ret; } static SIMPLE_DEV_PM_OPS(upi_dev_pm_ops, upi_i2c_suspend, upi_i2c_resume); #endif static int __devinit upi_pmic_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { struct regulator_dev *rdev; struct regulator_init_data *initdata; struct upi_data *upi; int ret = 0; printk("%s\n", __func__); upi = kzalloc(sizeof(struct upi_data) + SUPPORT_BUCK_NUM * sizeof(struct regulator_dev *), GFP_KERNEL); if (!upi) goto out; upi->client = client; upi->min_uV = UPI_BUCK4_MIN_UV; upi->max_uV = UPI_BUCK4_MAX_UV; upi->enabled = 1; rdev = &upi->rdev[0]; initdata = &upi_buck4_power; rdev = regulator_register(&upi_reg[0], &client->dev, initdata, upi, NULL); rdev = &upi->rdev[1]; initdata = &upi_buck3_power; rdev = regulator_register(&upi_reg[1], &client->dev, initdata, upi, NULL); i2c_set_clientdata(client, upi); #ifdef CONFIG_HAS_EARLYSUSPEND upi_pmic_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; upi_pmic_early_suspend.suspend = upi_early_suspend; upi_pmic_early_suspend.resume = upi_late_resume; register_early_suspend(&upi_pmic_early_suspend); #endif g_upidata = upi; upi_buck3_update_voltage(); upi_buck4_update_voltage(); upi6631_delay_init(); dev_info(&client->dev, "UPI regulator driver loaded\n"); return 0; out: return ret; } static int __devexit upi_pmic_remove(struct i2c_client *client) { struct upi_data *upi = i2c_get_clientdata(client); regulator_unregister(upi->rdev); kfree(upi); return 0; } static const struct i2c_device_id upi6631_id[] = { {"upi6631", 0}, {} }; MODULE_DEVICE_TABLE(i2c, upi6631_id); static struct i2c_driver upi6631_pmic_driver = { .probe = upi_pmic_probe, .remove = __devexit_p(upi_pmic_remove), .driver = { .name = "upi6631", .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &upi_dev_pm_ops, #endif }, .id_table = upi6631_id, }; static struct i2c_board_info upi_i2c_board_info = { .type = "upi6631", .flags = 0x00, .addr = 0x66, .platform_data = NULL, .archdata = NULL, .irq = -1, }; #ifdef CONFIG_MTD_WMT_SF #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,"upi6631",7)) { g_pmic_en = 0; return -1; } ++i; sscanf((buf + i), "%d:%d", &g_i2cbus_id, &delay_time); } else { g_pmic_en = 0; return -1; } g_pmic_en = 1; return 0; } #endif static int __init upi_pmic_init(void) { struct i2c_board_info *upi6631_i2c_bi; struct i2c_adapter *adapter = NULL; struct i2c_client *client = NULL; int ret = 0; #ifdef CONFIG_MTD_WMT_SF parse_pmic_param(); #endif if (g_pmic_en == 0) { printk("No upi pmic\n"); return -ENODEV; } upi6631_i2c_bi = &upi_i2c_board_info; adapter = i2c_get_adapter(g_i2cbus_id);/*in bus 2*/ if (NULL == adapter) { printk("can not get i2c adapter, client address error\n"); ret = -ENODEV; return -1; } client = i2c_new_device(adapter, upi6631_i2c_bi); if (client == NULL) { printk("allocate i2c client failed\n"); ret = -ENOMEM; return -1; } i2c_put_adapter(adapter); return i2c_add_driver(&upi6631_pmic_driver); } module_init(upi_pmic_init); static void __exit upi_pmic_exit(void) { i2c_del_driver(&upi6631_pmic_driver); } module_exit(upi_pmic_exit); MODULE_AUTHOR("WonderMedia Technologies, Inc."); MODULE_DESCRIPTION("WMT GPIO PMIC Driver"); MODULE_LICENSE("GPL");