diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3105d8b3f0b3..09ba5f9df79d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -834,6 +834,15 @@ config SENSORS_MAX17135 This driver can also be built as a module. If so, the module will be called max17135_sensor. +config SENSORS_TPS6518X + tristate "Texas Instruments TPS6518X EPD temperature sensor" + depends on I2C + help + If you say yes here you get support for TPS6518X PMIC sensor. + + This driver can also be built as a module. If so, the module + will be called tps6518x_sensor. + config SENSORS_MAX197 tristate "Maxim MAX197 and compatibles" help diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 28a22f6c7391..6762218bb26a 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -114,6 +114,7 @@ obj-$(CONFIG_SENSORS_MAX16065) += max16065.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX1668) += max1668.o obj-$(CONFIG_SENSORS_MAX17135) += max17135-hwmon.o +obj-$(CONFIG_SENSORS_TPS6518X) += tps6518x-hwmon.o obj-$(CONFIG_SENSORS_MAX197) += max197.o obj-$(CONFIG_SENSORS_MAX31722) += max31722.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o diff --git a/drivers/hwmon/tps6518x-hwmon.c b/drivers/hwmon/tps6518x-hwmon.c new file mode 100644 index 000000000000..92c5148c74e7 --- /dev/null +++ b/drivers/hwmon/tps6518x-hwmon.c @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* + * tps65185.c + * + * Based on the MAX1619 driver. + * Copyright (C) 2003-2004 Alexey Fisher + * Jean Delvare + * + * The TPS65185 is a sensor chip made by Texass Instruments. + * It reports up to two temperatures (its own plus up to + * one external one). + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Conversions + */ +static int temp_from_reg(int val) +{ + return val; +} + +/* + * Functions declaration + */ +static int tps6518x_sensor_probe(struct platform_device *pdev); +static int tps6518x_sensor_remove(struct platform_device *pdev); + +static const struct platform_device_id tps6518x_sns_id[] = { + { "tps6518x-sns", 0}, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, tps6518x_sns_id); + +/* + * Driver data (common to all clients) + */ +static struct platform_driver tps6518x_sensor_driver = { + .probe = tps6518x_sensor_probe, + .remove = tps6518x_sensor_remove, + .id_table = tps6518x_sns_id, + .driver = { + .name = "tps6518x_sensor", + }, +}; + + +/* + * Client data (each client gets its own) + */ +struct tps6518x_data { + struct device *hwmon_dev; +}; + +/* + * Sysfs stuff + */ +static ssize_t show_temp_input(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int reg_val; + /* + * begin Temperature conversion + */ + tps6518x_reg_read(REG_TPS6518x_REVID,®_val); + switch (reg_val & 0xff) + { + case TPS65180_PASS1 : + case TPS65180_PASS2 : + case TPS65181_PASS1 : + case TPS65181_PASS2 : + reg_val = 0x80; + tps6518x_reg_write(REG_TPS65180_TMST_CONFIG, reg_val); + // wait for completion completed + while ((0x20 & reg_val) == 0) + { + msleep(1); + tps6518x_reg_read(REG_TPS65180_TMST_CONFIG, ®_val); + } + break; + case TPS65185_PASS0 : + case TPS65186_PASS0 : + case TPS65185_PASS1 : + case TPS65186_PASS1 : + case TPS65185_PASS2 : + case TPS65186_PASS2 : + reg_val = 0x80; + tps6518x_reg_write(REG_TPS65185_TMST1, reg_val); + // wait for completion completed + while ((0x20 & reg_val) == 0) + { + msleep(1); + tps6518x_reg_read(REG_TPS65185_TMST1, ®_val); + } + break; + default: + break; + + } + + tps6518x_reg_read(REG_TPS6518x_TMST_VAL, ®_val); + return snprintf(buf, PAGE_SIZE, "%d\n", temp_from_reg(reg_val)); +} + +static ssize_t show_intr_regs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int reg_val; + unsigned int intr_reg_val; + /* + * get the interrupt status register value + */ + tps6518x_reg_read(REG_TPS6518x_REVID,®_val); + switch (reg_val & 0xff) + { + case TPS65180_PASS1 : + case TPS65180_PASS2 : + case TPS65181_PASS1 : + case TPS65181_PASS2 : + tps6518x_reg_read(REG_TPS65180_INT1, &intr_reg_val); + tps6518x_reg_read(REG_TPS65180_INT2, ®_val); + intr_reg_val |= reg_val<<8; + break; + case TPS65185_PASS0 : + case TPS65186_PASS0 : + case TPS65185_PASS1 : + case TPS65186_PASS1 : + case TPS65185_PASS2 : + case TPS65186_PASS2 : + tps6518x_reg_read(REG_TPS65185_INT1, &intr_reg_val); + tps6518x_reg_read(REG_TPS65185_INT2, ®_val); + intr_reg_val |= reg_val<<8; + break; + default: + break; + + } + + return snprintf(buf, PAGE_SIZE, "%d\n", intr_reg_val); +} + +static ssize_t show_vcom(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int reg_val; + unsigned int vcom_reg_val; + /* + * get the vcom registers + */ + tps6518x_reg_read(REG_TPS6518x_REVID,®_val); + switch (reg_val & 0xff) + { + case TPS65180_PASS1 : + case TPS65180_PASS2 : + case TPS65181_PASS1 : + case TPS65181_PASS2 : + tps6518x_reg_read(REG_TPS65180_VCOM_ADJUST, &vcom_reg_val); + break; + case TPS65185_PASS0 : + case TPS65186_PASS0 : + case TPS65185_PASS1 : + case TPS65186_PASS1 : + case TPS65185_PASS2 : + case TPS65186_PASS2 : + tps6518x_reg_read(REG_TPS65185_VCOM1, &vcom_reg_val); + tps6518x_reg_read(REG_TPS65185_VCOM2, ®_val); + vcom_reg_val |= reg_val<<8; + break; + default: + break; + + } + + return snprintf(buf, PAGE_SIZE, "%d\n", vcom_reg_val); +} + +static ssize_t set_vcom(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int reg_val; + long vcom_reg_val = simple_strtol(buf,NULL,10); + /* + * get the interrupt status register value + */ + tps6518x_reg_read(REG_TPS6518x_REVID,®_val); + switch (reg_val & 0xff) + { + case TPS65180_PASS1 : + case TPS65180_PASS2 : + case TPS65181_PASS1 : + case TPS65181_PASS2 : + tps6518x_reg_write(REG_TPS65180_VCOM_ADJUST, vcom_reg_val&0xff); + break; + case TPS65185_PASS0 : + case TPS65186_PASS0 : + case TPS65185_PASS1 : + case TPS65186_PASS1 : + case TPS65185_PASS2 : + case TPS65186_PASS2 : + tps6518x_reg_write(REG_TPS65185_VCOM1, vcom_reg_val&0xff); + tps6518x_reg_write(REG_TPS65185_VCOM2, (vcom_reg_val>>8)&0xff); + break; + default: + break; + + } + + return count; +} + + +static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL); +static DEVICE_ATTR(intr_input, S_IRUGO, show_intr_regs, NULL); +static DEVICE_ATTR(vcom_value, S_IWUSR | S_IRUGO, show_vcom, set_vcom); + +static struct attribute *tps6518x_attributes[] = { + &dev_attr_temp_input.attr, + &dev_attr_intr_input.attr, + &dev_attr_vcom_value.attr, + NULL +}; + +static const struct attribute_group tps6518x_group = { + .attrs = tps6518x_attributes, +}; + +/* + * Real code + */ +static int tps6518x_sensor_probe(struct platform_device *pdev) +{ + struct tps6518x_data *data; + int err; + printk("tps6518x_sensor_probe starting\n"); + + data = kzalloc(sizeof(struct tps6518x_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + err = sysfs_create_group(&pdev->dev.kobj, &tps6518x_group); + if (err) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + platform_set_drvdata(pdev, data); + + printk("tps6518x_sensor_probe success\n"); + return 0; + +exit_remove_files: + sysfs_remove_group(&pdev->dev.kobj, &tps6518x_group); +exit_free: + kfree(data); +exit: + return err; +} + +static int tps6518x_sensor_remove(struct platform_device *pdev) +{ + struct tps6518x_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &tps6518x_group); + + kfree(data); + return 0; +} + +static int __init sensors_tps6518x_init(void) +{ + return platform_driver_register(&tps6518x_sensor_driver); +} +module_init(sensors_tps6518x_init); + +static void __exit sensors_tps6518x_exit(void) +{ + platform_driver_unregister(&tps6518x_sensor_driver); +} +module_exit(sensors_tps6518x_exit); + +MODULE_DESCRIPTION("TPS6518x sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b024af98059f..552d45d6fbbb 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -590,6 +590,13 @@ config MFD_MAX17135 This is the MAX17135 PMIC support. It includes core support for communication with the MAX17135 chip. +config MFD_TPS6518X + tristate "Texas Instruments TPS6518X EPD PMIC core" + depends on I2C + help + This is the TPS6518X PMIC support. It includes + core support for communication with the TPS6518X chip. + config MFD_MAX77620 bool "Maxim Semiconductor MAX77620 and MAX20024 PMIC Support" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8ce82586c7b7..bea1e94f7880 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_MFD_TPS65912) += tps65912-core.o obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o obj-$(CONFIG_MFD_TPS80031) += tps80031.o +obj-$(CONFIG_MFD_TPS6518X) += tps6518x-core.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o diff --git a/drivers/mfd/tps6518x-core.c b/drivers/mfd/tps6518x-core.c new file mode 100644 index 000000000000..30d7f322441c --- /dev/null +++ b/drivers/mfd/tps6518x-core.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/*! + * @file pmic/core/tps6518x.c + * @brief This file contains TPS6518x specific PMIC code. This implementaion + * may differ for each PMIC chip. + * + * @ingroup PMIC_CORE + */ + +/* + * Includes + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int tps6518x_detect(struct i2c_client *client, struct i2c_board_info *info); +struct i2c_client *tps6518x_client; +static struct regulator *gpio_regulator; + +static struct mfd_cell tps6518x_devs[] = { + { .name = "tps6518x-pmic", }, + { .name = "tps6518x-sns", }, +}; + +static const unsigned short normal_i2c[] = {EPDC_PMIC_I2C_ADDR, I2C_CLIENT_END}; + +int tps6518x_reg_read(int reg_num, unsigned int *reg_val) +{ + int result; + + if (tps6518x_client == NULL) + return PMIC_ERROR; + + result = i2c_smbus_read_byte_data(tps6518x_client, reg_num); + if (result < 0) { + dev_err(&tps6518x_client->dev, + "Unable to read tps6518x register via I2C\n"); + return PMIC_ERROR; + } + + *reg_val = result; + return PMIC_SUCCESS; +} + +int tps6518x_reg_write(int reg_num, const unsigned int reg_val) +{ + int result; + + if (tps6518x_client == NULL) + return PMIC_ERROR; + + result = i2c_smbus_write_byte_data(tps6518x_client, reg_num, reg_val); + if (result < 0) { + // dev_err(&tps6518x_client->dev, + // "Unable to write TPS6518x register via I2C\n"); + return PMIC_ERROR; + } + + return PMIC_SUCCESS; +} + +#ifdef CONFIG_OF +static struct tps6518x_platform_data *tps6518x_i2c_parse_dt_pdata( + struct device *dev) +{ + struct tps6518x_platform_data *pdata; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "could not allocate memory for pdata\n"); + return ERR_PTR(-ENOMEM); + } + + return pdata; +} +#else +static struct tps6518x_platform_data *tps6518x_i2c_parse_dt_pdata( + struct device *dev) +{ + return NULL; +} +#endif /* !CONFIG_OF */ + +static int tps6518x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps6518x *tps6518x; + struct tps6518x_platform_data *pdata = client->dev.platform_data; + struct device_node *np = client->dev.of_node; + int ret = 0; + + printk("tps6518x_probe calling\n"); + + if (!np) + return -ENODEV; + + gpio_regulator = devm_regulator_get(&client->dev, "SENSOR"); + if (!IS_ERR(gpio_regulator)) { + ret = regulator_enable(gpio_regulator); + if (ret) { + dev_err(&client->dev, "gpio set voltage error\n"); + return ret; + } + } + + + /* Create the PMIC data structure */ + tps6518x = kzalloc(sizeof(struct tps6518x), GFP_KERNEL); + if (tps6518x == NULL) { + kfree(client); + return -ENOMEM; + } + + /* Initialize the PMIC data structure */ + i2c_set_clientdata(client, tps6518x); + tps6518x->dev = &client->dev; + tps6518x->i2c_client = client; + + tps6518x_client = client; + ret = tps6518x_detect(client, NULL); + if (ret) + goto err1; + + mfd_add_devices(tps6518x->dev, -1, tps6518x_devs, + ARRAY_SIZE(tps6518x_devs), + NULL, 0, NULL); + + if (tps6518x->dev->of_node) { + pdata = tps6518x_i2c_parse_dt_pdata(tps6518x->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err2; + } + + } + tps6518x->pdata = pdata; + + dev_info(&client->dev, "PMIC TPS6518x for eInk display\n"); + + printk("tps6518x_probe success\n"); + + return ret; + +err2: + mfd_remove_devices(tps6518x->dev); +err1: + kfree(tps6518x); + + return ret; +} + + +static int tps6518x_remove(struct i2c_client *i2c) +{ + struct tps6518x *tps6518x = i2c_get_clientdata(i2c); + + mfd_remove_devices(tps6518x->dev); + + return 0; +} + +static int tps6518x_suspend(struct i2c_client *client, pm_message_t state) +{ + return 0; +} + +static int tps6518x_resume(struct i2c_client *client) +{ + return 0; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int tps6518x_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + //struct tps6518x_platform_data *pdata = client->dev.platform_data; + struct i2c_adapter *adapter = client->adapter; + u8 revId; + printk("tps6518x_detect calling\n"); + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* identification */ + revId = i2c_smbus_read_byte_data(client, + REG_TPS6518x_REVID); + + /* + * Known rev-ids + * tps165180 pass 1 = 0x50, tps65180 pass2 = 0x60, tps65181 pass1 = 0x51, tps65181 pass2 = 0x61, + * tps65182, + * tps65185 pass0 = 0x45, tps65186 pass0 0x46, tps65185 pass1 = 0x55, tps65186 pass1 0x56, tps65185 pass2 = 0x65, tps65186 pass2 0x66 + */ + if (!((revId == TPS65180_PASS1) || + (revId == TPS65181_PASS1) || + (revId == TPS65180_PASS2) || + (revId == TPS65181_PASS2) || + (revId == TPS65185_PASS0) || + (revId == TPS65186_PASS0) || + (revId == TPS65185_PASS1) || + (revId == TPS65186_PASS1) || + (revId == TPS65185_PASS2) || + (revId == TPS65186_PASS2))) + { + dev_info(&adapter->dev, + "Unsupported chip (Revision ID=0x%02X).\n", revId); + return -ENODEV; + } + + if (info) { + strlcpy(info->type, "tps6518x_sensor", I2C_NAME_SIZE); + } + printk("tps6518x_detect success\n"); + + return 0; +} + +static const struct i2c_device_id tps6518x_id[] = { + { "tps6518x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tps6518x_id); + +static const struct of_device_id tps6518x_dt_ids[] = { + { + .compatible = "ti,tps6518x", + .data = (void *) &tps6518x_id[0], + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, tps6518x_dt_ids); + + +static struct i2c_driver tps6518x_driver = { + .driver = { + .name = "tps6518x", + .owner = THIS_MODULE, + .of_match_table = tps6518x_dt_ids, + }, + .probe = tps6518x_probe, + .remove = tps6518x_remove, + .suspend = tps6518x_suspend, + .resume = tps6518x_resume, + .id_table = tps6518x_id, + .detect = tps6518x_detect, + .address_list = &normal_i2c[0], +}; + +static int __init tps6518x_init(void) +{ + return i2c_add_driver(&tps6518x_driver); +} + +static void __exit tps6518x_exit(void) +{ + i2c_del_driver(&tps6518x_driver); +} + +/* + * Module entry points + */ +subsys_initcall(tps6518x_init); +module_exit(tps6518x_exit); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 9fa9aa29affe..2015e975e280 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -390,6 +390,10 @@ config REGULATOR_MAX77620 chip to control Step-Down DC-DC and LDOs. Say Y here to enable the regulator driver. +config REGULATOR_TPS6518X + tristate "Texas Instruments TPS6518X Regulator Support" + depends on MFD_TPS6518X + config REGULATOR_MAX8649 tristate "Maxim 8649 voltage regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 3a18d9bf57d8..1a04c692d59f 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_MAX17135) += max17135-regulator.o obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o +obj-$(CONFIG_REGULATOR_TPS6518X) += tps6518x-regulator.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o diff --git a/drivers/regulator/tps6518x-regulator.c b/drivers/regulator/tps6518x-regulator.c new file mode 100644 index 000000000000..5f0c79132ecc --- /dev/null +++ b/drivers/regulator/tps6518x-regulator.c @@ -0,0 +1,810 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tps6518x_data { + int num_regulators; + struct tps6518x *tps6518x; + struct regulator_dev **rdev; +}; + + +static int tps6518x_pass_num = { 2 }; +static int tps6518x_vcom = { -2680000 }; +static int tps65180_current_Enable_Register = 0; + +static int tps6518x_is_power_good(struct tps6518x *tps6518x); +/* + * to_reg_val(): Creates a register value with new data + * + * Creates a new register value for a particular field. The data + * outside of the new field is not modified. + * + * @cur_reg: current value in register + * @reg_mask: mask of field bits to be modified + * @fld_val: new value for register field. + */ +static unsigned int to_reg_val(unsigned int cur_reg, unsigned int fld_mask, + unsigned int fld_val) +{ + return (cur_reg & (~fld_mask)) | fld_val; +} + +/* + * Regulator operations + */ +/* Convert uV to the VCOM register bitfield setting */ + +static int vcom_rs_to_uV(unsigned int reg_setting) +{ + if (reg_setting <= TPS65180_VCOM_MIN_SET) + return TPS65180_VCOM_MIN_uV; + if (reg_setting >= TPS65180_VCOM_MAX_SET) + return TPS65180_VCOM_MAX_uV; + return -(reg_setting * TPS65180_VCOM_STEP_uV); +} +static int vcom2_rs_to_uV(unsigned int reg_setting) +{ + if (reg_setting <= TPS65185_VCOM_MIN_SET) + return TPS65185_VCOM_MIN_uV; + if (reg_setting >= TPS65185_VCOM_MAX_SET) + return TPS65185_VCOM_MAX_uV; + return -(reg_setting * TPS65185_VCOM_STEP_uV); +} + + +static int vcom_uV_to_rs(int uV) +{ + if (uV <= TPS65180_VCOM_MIN_uV) + return TPS65180_VCOM_MIN_SET; + if (uV >= TPS65180_VCOM_MAX_uV) + return TPS65180_VCOM_MAX_SET; + return (-uV) / TPS65180_VCOM_STEP_uV; +} + +static int vcom2_uV_to_rs(int uV) +{ + if (uV <= TPS65185_VCOM_MIN_uV) + return TPS65185_VCOM_MIN_SET; + if (uV >= TPS65185_VCOM_MAX_uV) + return TPS65185_VCOM_MAX_SET; + return (-uV) / TPS65185_VCOM_STEP_uV; +} + +static int epdc_pwr0_enable(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + + gpio_set_value(tps6518x->gpio_pmic_powerup, 1); + + return 0; + +} + +static int epdc_pwr0_disable(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + + gpio_set_value(tps6518x->gpio_pmic_powerup, 0); + + return 0; + +} +static int tps6518x_v3p3_enable(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + + gpio_set_value(tps6518x->gpio_pmic_powerup, 1); + return 0; +} + +static int tps6518x_v3p3_disable(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + + gpio_set_value(tps6518x->gpio_pmic_powerup, 0); + return 0; + +} +static int tps6518x_v3p3_is_enabled(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + int gpio = gpio_get_value(tps6518x->gpio_pmic_powerup); + + if (gpio == 0) + return 0; + else + return 1; +} + +static int tps6518x_vcom_set_voltage(struct regulator_dev *reg, + int minuV, int uV, unsigned *selector) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + unsigned int cur_reg_val; /* current register value to modify */ + unsigned int new_reg_val; /* new register value to write */ + int retval; + + /* + * this will not work on tps65182 + */ + if (tps6518x->revID == 65182) + return 0; + +#if 0 + if (uV < 200000) + return 0; +#endif + + switch (tps6518x->revID & 15) + { + case 0 : /* TPS65180 */ + case 1 : /* TPS65181 */ + case 4 : /* TPS65180-rev1 */ + tps6518x_reg_read(REG_TPS65180_VCOM_ADJUST,&cur_reg_val); + new_reg_val = to_reg_val(cur_reg_val, + BITFMASK(VCOM_SET), + BITFVAL(VCOM_SET, vcom_uV_to_rs(uV))); + + retval = tps6518x_reg_write(REG_TPS65180_VCOM_ADJUST, + new_reg_val); + break; + case 5 : /* TPS65185 */ + case 6 : /* TPS65186 */ + gpio_set_value(tps6518x->gpio_pmic_wakeup,1); + retval = tps6518x_reg_write(REG_TPS65185_VCOM1, + vcom2_uV_to_rs(uV) & 255); + tps6518x_reg_read( REG_TPS65185_VCOM2,&cur_reg_val); + new_reg_val = to_reg_val(cur_reg_val, + BITFMASK(VCOM2_SET), + BITFVAL(VCOM2_SET, vcom2_uV_to_rs(uV)/256)); + + retval = tps6518x_reg_write(REG_TPS65185_VCOM2, + new_reg_val); + + break; + default : + retval = -1; + } + return retval; +} + +static int tps6518x_vcom_get_voltage(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + unsigned int cur_reg_val; /* current register value */ + unsigned int cur_reg2_val; /* current register value */ + unsigned int cur_fld_val; /* current bitfield value*/ + int vcomValue; + + /* + * this will not work on tps65182 + */ + if (tps6518x->revID == 65182) + return 0; + + switch (tps6518x->revID & 15) + { + case 0 : /* TPS65180 */ + case 1 : /* TPS65181 */ + case 4 : /* TPS65180-rev1 */ + tps6518x_reg_read(REG_TPS65180_VCOM_ADJUST, &cur_reg_val); + cur_fld_val = BITFEXT(cur_reg_val, VCOM_SET); + vcomValue = vcom_rs_to_uV(cur_fld_val); + break; + case 5 : /* TPS65185 */ + case 6 : /* TPS65186 */ + tps6518x_reg_read(REG_TPS65185_VCOM1,&cur_reg_val); + tps6518x_reg_read(REG_TPS65185_VCOM2,&cur_reg2_val); + cur_reg_val |= 256 * (1 & cur_reg2_val); + vcomValue = vcom2_rs_to_uV(cur_reg_val); + break; + default: + vcomValue = 0; + } + + return vcomValue; + +} + +static int tps6518x_vcom_enable(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + unsigned int cur_reg_val; /* current register value */ + int vcomEnable = 0; + /* + * check for the TPS65182 device + */ + if (tps6518x->revID == 65182) + { + gpio_set_value(tps6518x->gpio_pmic_vcom_ctrl,vcomEnable); + return 0; + } + + /* + * Check to see if we need to set the VCOM voltage. + * Should only be done one time. And, we can + * only change vcom voltage if we have been enabled. + */ + if (!tps6518x->vcom_setup && tps6518x_is_power_good(tps6518x)) { + tps6518x_vcom_set_voltage(reg, + tps6518x->vcom_uV, + tps6518x->vcom_uV, + NULL); + tps6518x->vcom_setup = true; + } + + switch (tps6518x->revID & 15) + { + case 0 : /* TPS65180 */ + case 1 : /* TPS65181 */ + case 4 : /* TPS65180-rev1 */ + vcomEnable = 1; + break; + case 5 : /* TPS65185 */ + case 6 : /* TPS65186 */ + tps6518x_reg_read(REG_TPS65185_VCOM2,&cur_reg_val); + // do not enable vcom if HiZ bit is set + if (cur_reg_val & (1<gpio_pmic_vcom_ctrl,vcomEnable); + + return 0; +} + +static int tps6518x_vcom_disable(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + + gpio_set_value(tps6518x->gpio_pmic_vcom_ctrl,0); + return 0; +} + +static int tps6518x_vcom_is_enabled(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + + int gpio = gpio_get_value(tps6518x->gpio_pmic_vcom_ctrl); + if (gpio == 0) + return 0; + else + return 1; +} + + +static int tps6518x_is_power_good(struct tps6518x *tps6518x) +{ + /* + * XOR of polarity (starting value) and current + * value yields whether power is good. + */ + return gpio_get_value(tps6518x->gpio_pmic_pwrgood) ^ + tps6518x->pwrgood_polarity; +} + +static int tps6518x_wait_power_good(struct tps6518x *tps6518x) +{ + int i; + for (i = 0; i < tps6518x->max_wait * 3; i++) { + if (tps6518x_is_power_good(tps6518x)) + return 0; + + msleep(1); + } + return -ETIMEDOUT; +} + +static int tps6518x_display_enable(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + unsigned int cur_reg_val; /* current register value to modify */ + unsigned int fld_mask; /* register mask for bitfield to modify */ + unsigned int fld_val; /* new bitfield value to write */ + unsigned int new_reg_val; /* new register value to write */ + if (tps6518x->revID == 65182) + { + epdc_pwr0_enable(reg); + } + else + { + gpio_set_value(tps6518x->gpio_pmic_wakeup,1); + + /* enable display regulators */ + cur_reg_val = tps65180_current_Enable_Register & 0x3f; + fld_mask = BITFMASK(VDDH_EN) | BITFMASK(VPOS_EN) | + BITFMASK(VEE_EN) | BITFMASK(VNEG_EN); + fld_val = BITFVAL(VDDH_EN, true) | BITFVAL(VPOS_EN, true) | + BITFVAL(VEE_EN, true) | BITFVAL(VNEG_EN, true) | BITFVAL(VCOM_EN, true); + new_reg_val = tps65180_current_Enable_Register = to_reg_val(cur_reg_val, fld_mask, fld_val); + tps6518x_reg_write(REG_TPS65180_ENABLE, new_reg_val); + + /* turn on display regulators */ + cur_reg_val = tps65180_current_Enable_Register & 0x3f; + fld_mask = BITFMASK(ACTIVE); + fld_val = BITFVAL(ACTIVE, true); + new_reg_val = tps65180_current_Enable_Register = to_reg_val(cur_reg_val, fld_mask, fld_val); + tps6518x_reg_write(REG_TPS65180_ENABLE, new_reg_val); + } + + return tps6518x_wait_power_good(tps6518x); +} + +static int tps6518x_display_disable(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + unsigned int cur_reg_val; /* current register value to modify */ + unsigned int fld_mask; /* register mask for bitfield to modify */ + unsigned int fld_val; /* new bitfield value to write */ + unsigned int new_reg_val; /* new register value to write */ + + if (tps6518x->revID == 65182) + { + epdc_pwr0_disable(reg); + } + else + { + /* turn off display regulators */ + cur_reg_val = tps65180_current_Enable_Register & 0x3f; + fld_mask = BITFMASK(VCOM_EN) | BITFMASK(STANDBY); + fld_val = BITFVAL(VCOM_EN, true) | BITFVAL(STANDBY, true); + new_reg_val = tps65180_current_Enable_Register = to_reg_val(cur_reg_val, fld_mask, fld_val); + tps6518x_reg_write(REG_TPS65180_ENABLE, new_reg_val); + + } + + msleep(tps6518x->max_wait); + + return 0; +} + +static int tps6518x_display_is_enabled(struct regulator_dev *reg) +{ + struct tps6518x *tps6518x = rdev_get_drvdata(reg); + + if (tps6518x->revID == 65182) + return gpio_get_value(tps6518x->gpio_pmic_wakeup) ? 1:0; + else + return tps65180_current_Enable_Register & BITFMASK(ACTIVE); +} + +/* + * Regulator operations + */ + +static struct regulator_ops tps6518x_display_ops = { + .enable = tps6518x_display_enable, + .disable = tps6518x_display_disable, + .is_enabled = tps6518x_display_is_enabled, +}; + +static struct regulator_ops tps6518x_vcom_ops = { + .enable = tps6518x_vcom_enable, + .disable = tps6518x_vcom_disable, + .get_voltage = tps6518x_vcom_get_voltage, + .set_voltage = tps6518x_vcom_set_voltage, + .is_enabled = tps6518x_vcom_is_enabled, +}; + +static struct regulator_ops tps6518x_v3p3_ops = { + .enable = tps6518x_v3p3_enable, + .disable = tps6518x_v3p3_disable, + .is_enabled = tps6518x_v3p3_is_enabled, +}; + +/* + * Regulator descriptors + */ +static struct regulator_desc tps6518x_reg[TPS6518x_NUM_REGULATORS] = { +{ + .name = "DISPLAY", + .id = TPS6518x_DISPLAY, + .ops = &tps6518x_display_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}, +{ + .name = "VCOM", + .id = TPS6518x_VCOM, + .ops = &tps6518x_vcom_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}, +{ + .name = "V3P3", + .id = TPS6518x_V3P3, + .ops = &tps6518x_v3p3_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}, +}; + +static void tps6518x_setup_timings(struct tps6518x *tps6518x) +{ + + int temp0, temp1, temp2, temp3; + + /* read the current setting in the PMIC */ + if ((tps6518x->revID == TPS65180_PASS1) || (tps6518x->revID == TPS65181_PASS1) || + (tps6518x->revID == TPS65180_PASS2) || (tps6518x->revID == TPS65181_PASS2)) { + tps6518x_reg_read(REG_TPS65180_PWRSEQ0, &temp0); + tps6518x_reg_read(REG_TPS65180_PWRSEQ1, &temp1); + tps6518x_reg_read(REG_TPS65180_PWRSEQ2, &temp2); + + if ((temp0 != tps6518x->pwr_seq0) || + (temp1 != tps6518x->pwr_seq1) || + (temp2 != tps6518x->pwr_seq2)) { + tps6518x_reg_write(REG_TPS65180_PWRSEQ0, tps6518x->pwr_seq0); + tps6518x_reg_write(REG_TPS65180_PWRSEQ1, tps6518x->pwr_seq1); + tps6518x_reg_write(REG_TPS65180_PWRSEQ2, tps6518x->pwr_seq2); + } + } + + if ((tps6518x->revID == TPS65185_PASS0) || + (tps6518x->revID == TPS65186_PASS0) || + (tps6518x->revID == TPS65185_PASS1) || + (tps6518x->revID == TPS65186_PASS1) || + (tps6518x->revID == TPS65185_PASS2) || + (tps6518x->revID == TPS65186_PASS2)) { + tps6518x_reg_read(REG_TPS65185_UPSEQ0, &temp0); + tps6518x_reg_read(REG_TPS65185_UPSEQ1, &temp1); + tps6518x_reg_read(REG_TPS65185_DWNSEQ0, &temp2); + tps6518x_reg_read(REG_TPS65185_DWNSEQ1, &temp3); + if ((temp0 != tps6518x->upseq0) || + (temp1 != tps6518x->upseq1) || + (temp2 != tps6518x->dwnseq0) || + (temp3 != tps6518x->dwnseq1)) { + tps6518x_reg_write(REG_TPS65185_UPSEQ0, tps6518x->upseq0); + tps6518x_reg_write(REG_TPS65185_UPSEQ1, tps6518x->upseq1); + tps6518x_reg_write(REG_TPS65185_DWNSEQ0, tps6518x->dwnseq0); + tps6518x_reg_write(REG_TPS65185_DWNSEQ1, tps6518x->dwnseq1); + } + } +} + +#define CHECK_PROPERTY_ERROR_KFREE(prop) \ +do { \ + int ret = of_property_read_u32(tps6518x->dev->of_node, \ + #prop, &tps6518x->prop); \ + if (ret < 0) { \ + return ret; \ + } \ +} while (0); + +#ifdef CONFIG_OF +static int tps6518x_pmic_dt_parse_pdata(struct platform_device *pdev, + struct tps6518x_platform_data *pdata) +{ + struct tps6518x *tps6518x = dev_get_drvdata(pdev->dev.parent); + struct device_node *pmic_np, *regulators_np, *reg_np; + struct tps6518x_regulator_data *rdata; + int i, ret; + + pmic_np = of_node_get(tps6518x->dev->of_node); + if (!pmic_np) { + dev_err(&pdev->dev, "could not find pmic sub-node\n"); + return -ENODEV; + } + + regulators_np = of_find_node_by_name(pmic_np, "regulators"); + if (!regulators_np) { + dev_err(&pdev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + pdata->num_regulators = of_get_child_count(regulators_np); + dev_dbg(&pdev->dev, "num_regulators %d\n", pdata->num_regulators); + + rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + of_node_put(regulators_np); + dev_err(&pdev->dev, "could not allocate memory for" + "regulator data\n"); + return -ENOMEM; + } + + pdata->regulators = rdata; + for_each_child_of_node(regulators_np, reg_np) { + for (i = 0; i < ARRAY_SIZE(tps6518x_reg); i++) + if (!of_node_cmp(reg_np->name, tps6518x_reg[i].name)) + break; + + if (i == ARRAY_SIZE(tps6518x_reg)) { + dev_warn(&pdev->dev, "don't know how to configure" + "regulator %s\n", reg_np->name); + continue; + } + + rdata->id = i; + rdata->initdata = of_get_regulator_init_data(&pdev->dev, + reg_np); + rdata->reg_node = reg_np; + rdata++; + } + of_node_put(regulators_np); + + tps6518x->max_wait = (6 + 6 + 6 + 6); + + tps6518x->gpio_pmic_wakeup = of_get_named_gpio(pmic_np, + "gpio_pmic_wakeup", 0); + if (!gpio_is_valid(tps6518x->gpio_pmic_wakeup)) { + dev_err(&pdev->dev, "no epdc pmic wakeup pin available\n"); + goto err; + } + ret = devm_gpio_request_one(&pdev->dev, tps6518x->gpio_pmic_wakeup, + GPIOF_OUT_INIT_LOW, "epdc-pmic-wake"); + if (ret < 0) + goto err; + + tps6518x->gpio_pmic_vcom_ctrl = of_get_named_gpio(pmic_np, + "gpio_pmic_vcom_ctrl", 0); + if (!gpio_is_valid(tps6518x->gpio_pmic_vcom_ctrl)) { + dev_err(&pdev->dev, "no epdc pmic vcom_ctrl pin available\n"); + goto err; + } + ret = devm_gpio_request_one(&pdev->dev, tps6518x->gpio_pmic_vcom_ctrl, + GPIOF_OUT_INIT_LOW, "epdc-vcom"); + if (ret < 0) + goto err; + + tps6518x->gpio_pmic_powerup = of_get_named_gpio(pmic_np, + "gpio_pmic_powerup", 0); + if (!gpio_is_valid(tps6518x->gpio_pmic_powerup)) { + dev_err(&pdev->dev, "no epdc pmic powerup pin available\n"); + goto err; + } + ret = devm_gpio_request_one(&pdev->dev, tps6518x->gpio_pmic_powerup, + GPIOF_OUT_INIT_LOW, "epdc-powerup"); + if (ret < 0) + goto err; + + tps6518x->gpio_pmic_intr = of_get_named_gpio(pmic_np, + "gpio_pmic_intr", 0); + if (!gpio_is_valid(tps6518x->gpio_pmic_intr)) { + dev_err(&pdev->dev, "no epdc pmic intr pin available\n"); + goto err; + } + ret = devm_gpio_request_one(&pdev->dev, tps6518x->gpio_pmic_intr, + GPIOF_IN, "epdc-pmic-int"); + if (ret < 0) + goto err; + + tps6518x->gpio_pmic_pwrgood = of_get_named_gpio(pmic_np, + "gpio_pmic_pwrgood", 0); + if (!gpio_is_valid(tps6518x->gpio_pmic_pwrgood)) { + dev_err(&pdev->dev, "no epdc pmic pwrgood pin available\n"); + goto err; + } + ret = devm_gpio_request_one(&pdev->dev, tps6518x->gpio_pmic_pwrgood, + GPIOF_IN, "epdc-pwrstat"); + if (ret < 0) + goto err; + +err: + return 0; + +} +#else +static int tps6518x_pmic_dt_parse_pdata(struct platform_device *pdev, + struct tps6518x *tps6518x) +{ + return 0; +} +#endif /* !CONFIG_OF */ + +/* + * Regulator init/probing/exit functions + */ +static int tps6518x_regulator_probe(struct platform_device *pdev) +{ + struct tps6518x *tps6518x = dev_get_drvdata(pdev->dev.parent); + struct tps6518x_platform_data *pdata = tps6518x->pdata; + struct tps6518x_data *priv; + struct regulator_dev **rdev; + struct regulator_config config = { }; + int size, i, ret = 0; + + printk("tps6518x_regulator_probe starting\n"); + + if (tps6518x->dev->of_node) { + ret = tps6518x_pmic_dt_parse_pdata(pdev, pdata); + if (ret) + return ret; + } + priv = devm_kzalloc(&pdev->dev, sizeof(struct tps6518x_data), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * pdata->num_regulators; + priv->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!priv->rdev) + return -ENOMEM; + + rdev = priv->rdev; + priv->num_regulators = pdata->num_regulators; + platform_set_drvdata(pdev, priv); + + tps6518x->vcom_setup = false; + tps6518x->pass_num = tps6518x_pass_num; + tps6518x->vcom_uV = tps6518x_vcom; + + for (i = 0; i < pdata->num_regulators; i++) { + int id = pdata->regulators[i].id; + + config.dev = tps6518x->dev; + config.init_data = pdata->regulators[i].initdata; + config.driver_data = tps6518x; + config.of_node = pdata->regulators[i].reg_node; + + rdev[i] = regulator_register(&tps6518x_reg[id], &config); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(&pdev->dev, "regulator init failed for %d\n", + id); + rdev[i] = NULL; + goto err; + } + } + + /* + * Set up PMIC timing values. + * Should only be done one time! Timing values may only be + * changed a limited number of times according to spec. + */ + tps6518x_setup_timings(tps6518x); + + printk("tps6518x_regulator_probe success\n"); + return 0; +err: + while (--i >= 0) + regulator_unregister(rdev[i]); + return ret; +} + +static int tps6518x_regulator_remove(struct platform_device *pdev) +{ + struct tps6518x_data *priv = platform_get_drvdata(pdev); + struct regulator_dev **rdev = priv->rdev; + int i; + + for (i = 0; i < priv->num_regulators; i++) + regulator_unregister(rdev[i]); + return 0; +} + +static const struct platform_device_id tps6518x_pmic_id[] = { + { "tps6518x-pmic", 0}, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, tps6518x_pmic_id); + +static struct platform_driver tps6518x_regulator_driver = { + .probe = tps6518x_regulator_probe, + .remove = tps6518x_regulator_remove, + .id_table = tps6518x_pmic_id, + .driver = { + .name = "tps6518x-pmic", + }, +}; + +static int __init tps6518x_regulator_init(void) +{ + return platform_driver_register(&tps6518x_regulator_driver); +} +subsys_initcall_sync(tps6518x_regulator_init); + +static void __exit tps6518x_regulator_exit(void) +{ + platform_driver_unregister(&tps6518x_regulator_driver); +} +module_exit(tps6518x_regulator_exit); + + +/* + * Parse user specified options (`tps6518x:') + * example: + * tps6518x:pass=2,vcom=-1250000 + */ +static int __init tps6518x_setup(char *options) +{ + int ret; + char *opt; + unsigned long ulResult; + while ((opt = strsep(&options, ",")) != NULL) { + if (!*opt) + continue; + if (!strncmp(opt, "pass=", 5)) { + ret = strict_strtoul((const char *)(opt + 5), 0, &ulResult); + tps6518x_pass_num = ulResult; + if (ret < 0) + return ret; + } + if (!strncmp(opt, "vcom=", 5)) { + int offs = 5; + if (opt[5] == '-') + offs = 6; + ret = strict_strtoul((const char *)(opt + offs), 0, &ulResult); + tps6518x_vcom = (int) ulResult; + if (ret < 0) + return ret; + tps6518x_vcom = -tps6518x_vcom; + } + } + + return 1; +} + +__setup("tps6518x:", tps6518x_setup); + +static int __init tps65182_setup(char *options) +{ + int ret; + char *opt; + unsigned long ulResult; + while ((opt = strsep(&options, ",")) != NULL) { + if (!*opt) + continue; + if (!strncmp(opt, "pass=", 5)) { + ret = strict_strtoul((const char *)(opt + 5), 0, &ulResult); + tps6518x_pass_num = ulResult; + if (ret < 0) + return ret; + } + if (!strncmp(opt, "vcom=", 5)) { + int offs = 5; + if (opt[5] == '-') + offs = 6; + ret = strict_strtoul((const char *)(opt + offs), 0, &ulResult); + tps6518x_vcom = (int) ulResult; + if (ret < 0) + return ret; + tps6518x_vcom = -tps6518x_vcom; + } + } + + return 1; +} + +__setup("tps65182:", tps65182_setup); + + +/* Module information */ +MODULE_DESCRIPTION("TPS6518x regulator driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/tps6518x.h b/include/linux/mfd/tps6518x.h new file mode 100644 index 000000000000..82297c03cf04 --- /dev/null +++ b/include/linux/mfd/tps6518x.h @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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 + * + */ +#ifndef __LINUX_REGULATOR_TPS6518x_H_ +#define __LINUX_REGULATOR_TPS6518x_H_ + +/* + * EPDC PMIC I2C address + * PAPYRUS II 1p1 and later uses 0x68, others 0x48 + */ +#define EPDC_PMIC_I2C_ADDR 0x68 + +/* + * currently supported rev IDs + */ +//#define TPS65180_PASS1 0x54 +//#define TPS65181_PASS1 0x55 +#define TPS65180_PASS1 0x50 +#define TPS65181_PASS1 0x51 +#define TPS65180_PASS2 0x60 +#define TPS65181_PASS2 0x61 +#define TPS65185_PASS0 0x45 +#define TPS65186_PASS0 0x46 +#define TPS65185_PASS1 0x55 +#define TPS65186_PASS1 0x56 +#define TPS65185_PASS2 0x65 +#define TPS65186_PASS2 0x66 + +/* + * PMIC Register Addresses + */ +enum { + REG_TPS6518x_TMST_VAL = 0x0, + REG_TPS65185_ENABLE, + REG_TPS65185_VADJ, + REG_TPS65185_VCOM1, + REG_TPS65185_VCOM2, + REG_TPS65185_INT_EN1, + REG_TPS65185_INT_EN2, + REG_TPS65185_INT1, + REG_TPS65185_INT2, + REG_TPS65185_UPSEQ0, + REG_TPS65185_UPSEQ1, + REG_TPS65185_DWNSEQ0, + REG_TPS65185_DWNSEQ1, + REG_TPS65185_TMST1, + REG_TPS65185_TMST2, + REG_TPS6518x_PG, + REG_TPS6518x_REVID, + TPS6518x_REG_NUM, +}; + +enum { + REG_TPS65180_TMST_VAL = 0x0, + REG_TPS65180_ENABLE, + REG_TPS65180_VP_ADJUST, + REG_TPS65180_VN_ADJUST, + REG_TPS65180_VCOM_ADJUST, + REG_TPS65180_INT_EN1, + REG_TPS65180_INT_EN2, + REG_TPS65180_INT1, + REG_TPS65180_INT2, + REG_TPS65180_PWRSEQ0, + REG_TPS65180_PWRSEQ1, + REG_TPS65180_PWRSEQ2, + REG_TPS65180_TMST_CONFIG, + REG_TPS65180_TMST_OS, + REG_TPS65180_TMST_HYST, + REG_TPS65180_PG_STATUS, + REG_TPS65180_REVID, + REG_TPS65180_FIX_READ_PTR, + TPS65180_REG_NUM, +}; + +#define TPS6518x_MAX_REGISTER 0xFF + +/* + * Bitfield macros that use rely on bitfield width/shift information. + */ +#define BITFMASK(field) (((1U << (field ## _WID)) - 1) << (field ## _LSH)) +#define BITFVAL(field, val) ((val) << (field ## _LSH)) +#define BITFEXT(var, bit) ((var & BITFMASK(bit)) >> (bit ## _LSH)) + +/* + * Shift and width values for each register bitfield + */ +/* TMST_VALUE */ +#define TMST_VALUE_LSH 0 +#define TMST_VALUE_WID 8 +/* ENABLE */ +#define ACTIVE_LSH 7 +#define ACTIVE_WID 1 +#define STANDBY_LSH 6 +#define STANDBY_WID 1 +#define V3P3_SW_EN_LSH 5 +#define V3P3_SW_EN_WID 1 +#define VCOM_EN_LSH 4 +#define VCOM_EN_WID 1 +#define VDDH_EN_LSH 3 +#define VDDH_EN_WID 1 +#define VPOS_EN_LSH 2 +#define VPOS_EN_WID 1 +#define VEE_EN_LSH 1 +#define VEE_EN_WID 1 +#define VNEG_EN_LSH 0 +#define VNEG_EN_WID 1 +/* VCOM_ADJUST */ +#define VCOM_SET_LSH 0 +#define VCOM_SET_WID 8 +#define VCOM1_SET_LSH 0 +#define VCOM1_SET_WID 8 +#define VCOM2_SET_LSH 0 +#define VCOM2_SET_WID 1 +#define VCOM_ACQ_LSH 15 +#define VCOM_ACQ_WID 1 +#define VCOM_PROG_LSH 14 +#define VCOM_PEOG_WID 1 +#define VCOM_HiZ_LSH 13 +#define VCOM_HiZ_WID 1 +#define VCOM_AVG_LSH 11 +#define VCOM_AVG_WID 2 +/* INT_ENABLE1 */ +#define TSD_EN_LSH 6 +#define TSD_EN_WID 1 +#define HOT_EN_LSH 5 +#define HOT_EN_WID 1 +#define TMST_HOT_EN_LSH 4 +#define TMST_HOT_EN_WID 1 +#define TMST_COOL_EN_LSH 3 +#define TMST_COOL_EN_WID 1 +#define UVLO_EN_LSH 2 +#define UVLO_EN_WID 1 +/* INT_ENABLE2 */ +#define VB_UV_EN_LSH 7 +#define VB_UV_EN_WID 1 +#define VDDH_UV_EN_LSH 6 +#define VDDH_UV_EN_WID 1 +#define VN_UV_EN_LSH 5 +#define VN_UV_EN_WID 1 +#define VPOS_UV_EN_LSH 4 +#define VPOS_UV_EN_WID 1 +#define VEE_UV_EN_LSH 3 +#define VEE_UV_EN_WID 1 +#define VNEG_UV_EN_LSH 1 +#define VNEG_UV_EN_WID 1 +#define EOC_EN_LSH 0 +#define EOC_EN_WID 1 +/* INT_STATUS1 */ +#define TSDN_LSH 6 +#define TSDN_WID 1 +#define HOT_LSH 5 +#define HOT_WID 1 +#define TMST_HOT_LSH 4 +#define TMST_HOT_WID 1 +#define TMST_COOL_LSH 3 +#define TMST_COOL_WID 1 +#define UVLO_LSH 2 +#define UVLO_WID 1 +/* INT_STATUS2 */ +#define VB_UV_LSH 7 +#define VB_UV_WID 1 +#define VDDH_UV_LSH 6 +#define VDDH_UV_WID 1 +#define VN_UV_LSH 5 +#define VN_UV_WID 1 +#define VPOS_UV_LSH 4 +#define VPOS_UV_WID 1 +#define VEE_UV_LSH 3 +#define VEE_UV_WID 1 +#define VNEG_UV_LSH 1 +#define VNEG_UV_WID 1 +#define EOC_LSH 0 +#define EOC_WID 1 +/* PWR_SEQ0 */ +#define VDDH_SEQ_LSH 6 +#define VDDH_SEQ_WID 2 +#define VPOS_SEQ_LSH 4 +#define VPOS_SEQ_WID 2 +#define VEE_SEQ_LSH 2 +#define VEE_SEQ_WID 2 +#define VNEG_SEQ_LSH 0 +#define VNEG_SEQ_WID 2 +/* PWR_SEQ1 */ +#define DLY1_LSH 4 +#define DLY1_WID 4 +#define DLY0_LSH 0 +#define DLY0_WID 4 +/* PWR_SEQ2 */ +#define DLY3_LSH 4 +#define DLY3_WID 4 +#define DLY2_LSH 0 +#define DLY2_WID 4 +/* TMST_CONFIG */ +#define READ_THERM_LSH 7 +#define READ_THERM_WID 1 +#define CONV_END_LSH 5 +#define CONV_END_WID 1 +#define FAULT_QUE_LSH 3 +#define FAULT_QUE_WID 2 +#define FAULT_QUE_CLR_LSH 2 +#define FAULT_QUE_CLR_WID 1 +/* TMST_OS */ +#define TMST_HOT_SET_LSH 0 +#define TMST_HOT_SET_WID 8 +/* TMST_HYST */ +#define TMST_COOL_SET_LSH 0 +#define TMST_COOL_SET_WID 8 +/* PG_STATUS */ +#define VB_PG_LSH 7 +#define VB_PG_WID 1 +#define VDDH_PG_LSH 6 +#define VDDH_PG_WID 1 +#define VN_PG_LSH 5 +#define VN_PG_WID 1 +#define VPOS_PG_LSH 4 +#define VPOS_PG_WID 1 +#define VEE_PG_LSH 3 +#define VEE_PG_WID 1 +#define VNEG_PG_LSH 1 +#define VNEG_PG_WID 1 +/* REVID */ +#define MJREV_LSH 6 +#define MJREV_WID 2 +#define MNREV_LSH 4 +#define MNREV_WID 2 +#define VERSION_LSH 0 +#define VERSION_WID 4 +/* FIX_READ_POINTER */ +#define FIX_RD_PTR_LSH 0 +#define FIX_RD_PTR_WID 1 + +/* + * VCOM Definitions + * + * The register fields accept voltages in the range 0V to -2.75V, but the + * VCOM parametric performance is only guaranteed from -0.3V to -2.5V. + */ +#define TPS65180_VCOM_MIN_uV -2750000 +#define TPS65180_VCOM_MAX_uV 0 +#define TPS65180_VCOM_MIN_SET 0 +#define TPS65180_VCOM_MAX_SET 255 +#define TPS65180_VCOM_BASE_uV 10740 +#define TPS65180_VCOM_STEP_uV 10740 +#define TPS65185_VCOM_MIN_uV -5110000 +#define TPS65185_VCOM_MAX_uV 0 +#define TPS65185_VCOM_MIN_SET 0 +#define TPS65185_VCOM_MAX_SET 511 +#define TPS65185_VCOM_BASE_uV 10000 +#define TPS65185_VCOM_STEP_uV 10000 + + + +#define TPS6518x_VCOM_MIN_VAL 0 +#define TPS6518x_VCOM_MAX_VAL 255 + +struct regulator_init_data; + +struct tps6518x { + /* chip revision */ + int revID; + + struct device *dev; + struct tps6518x_platform_data *pdata; + + /* Platform connection */ + struct i2c_client *i2c_client; + + /* Timings */ + unsigned int pwr_seq0; + unsigned int pwr_seq1; + unsigned int pwr_seq2; + unsigned int upseq0; + unsigned int upseq1; + unsigned int dwnseq0; + unsigned int dwnseq1; + + /* GPIOs */ + int gpio_pmic_pwrgood; + int gpio_pmic_vcom_ctrl; + int gpio_pmic_wakeup; + int gpio_pmic_intr; + int gpio_pmic_powerup; + + /* TPS6518x part variables */ + int pass_num; + int vcom_uV; + + /* One-time VCOM setup marker */ + bool vcom_setup; + + /* powerup/powerdown wait time */ + int max_wait; + + /* Dynamically determined polarity for PWRGOOD */ + int pwrgood_polarity; +}; + +enum { + /* In alphabetical order */ + TPS6518x_DISPLAY, /* virtual master enable */ + TPS6518x_VCOM, + TPS6518x_V3P3, + TPS6518x_NUM_REGULATORS, +}; + +/* + * Declarations + */ +struct regulator_init_data; +struct tps6518x_regulator_data; + +struct tps6518x_platform_data { + unsigned int pwr_seq0; + unsigned int pwr_seq1; + unsigned int pwr_seq2; + unsigned int upseq0; + unsigned int upseq1; + unsigned int dwnseq0; + unsigned int dwnseq1; + int gpio_pmic_pwrgood; + int gpio_pmic_vcom_ctrl; + int gpio_pmic_wakeup; + int gpio_pmic_intr; + int gpio_pmic_powerup; + int pass_num; + int vcom_uV; + + /* PMIC */ + struct tps6518x_regulator_data *regulators; + int num_regulators; +}; + +struct tps6518x_regulator_data { + int id; + struct regulator_init_data *initdata; + struct device_node *reg_node; +}; + +int tps6518x_reg_read(int reg_num, unsigned int *reg_val); +int tps6518x_reg_write(int reg_num, const unsigned int reg_val); + +#endif