Say yes here to access the GPIO signals of various multi-function
power management chips from Texas Instruments.
+config GPIO_TPS65910
+ bool "TPS65910 GPIOs"
+ depends on TPS65910_CORE
+ help
+ Say yes here to access the GPIO signal of TPS65910x multi-function
+ power management chips from Texas Instruments.
+
config GPIO_WM831X
tristate "WM831x GPIOs"
depends on MFD_WM831X
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
obj-$(CONFIG_GPIO_PL061) += pl061.o
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
+obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o
obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o
obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o
obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
--- /dev/null
+/*
+ * tps65910_gpio.c -- access to GPIOs on TPS65910x chips
+ *
+ * Copyright (C) 2010 Mistral solutions Pvt Ltd <www.mistralsolutions.com>
+ *
+ * Based on twl4030-gpio.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/i2c/tps65910.h>
+
+static int gpio_tps65910_remove(struct platform_device *pdev);
+
+/*
+ * The GPIO "subchip" supports 1 GPIOs which can be configured as
+ * inputs or outputs, with pullups or pulldowns on each pin. Each
+ * GPIO can trigger interrupts on either or both edges.
+ */
+
+
+/* Data structures */
+static struct gpio_chip tps65910_gpiochip;
+static DEFINE_MUTEX(gpio_lock);
+static unsigned int gpio_usage_count;
+static struct work_struct gpio_work;
+static struct mutex work_lock;
+/*
+ * To configure TPS65910 GPIO registers
+ */
+static inline int gpio_tps65910_write(u8 address, u8 data)
+{
+ return tps65910_i2c_write_u8(TPS65910_I2C_ID0, data, address);
+}
+
+
+/*
+ * To read a TPS65910 GPIO module register
+ */
+static inline int gpio_tps65910_read(u8 address)
+{
+ u8 data;
+ int ret = 0;
+
+ ret = tps65910_i2c_read_u8(TPS65910_I2C_ID0, &data, address);
+ return (ret < 0) ? ret : data;
+}
+
+static int tps65910_request(struct gpio_chip *chip, unsigned offset)
+{
+ int status = 0;
+
+ mutex_lock(&gpio_lock);
+
+ /* initialize TPS65910 GPIO */
+ /* By default the GPIO_CKSYNC signal is GPIO */
+ if (!gpio_usage_count)
+ gpio_usage_count++;
+
+ mutex_unlock(&gpio_lock);
+ return status;
+}
+
+static void tps65910_free(struct gpio_chip *chip, unsigned offset)
+{
+ mutex_lock(&gpio_lock);
+
+ /* on last use, switch off GPIO module */
+ if (!gpio_usage_count)
+ gpio_usage_count--;
+
+ mutex_unlock(&gpio_lock);
+}
+
+static int tps65910_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ /* Configure TPS65910 GPIO as input */
+ u8 val;
+
+ mutex_lock(&gpio_lock);
+
+ val = gpio_tps65910_read(TPS65910_REG_GPIO0);
+
+ val &= ~(TPS65910_GPIO_CFG_OUTPUT);
+
+ val = gpio_tps65910_read(TPS65910_REG_GPIO0);
+
+ mutex_unlock(&gpio_lock);
+
+ return 0;
+}
+
+static int tps65910_get(struct gpio_chip *chip, unsigned offset)
+{
+ int status = 0;
+
+ mutex_lock(&gpio_lock);
+
+ status = gpio_tps65910_read(TPS65910_REG_GPIO0);
+
+ mutex_unlock(&gpio_lock);
+ if (status & 0x01)
+ return 1;
+ else
+ return 0;
+}
+static
+int tps65910_direction_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+ /* Configure TPS65910 GPIO as input */
+ u8 val;
+ u32 ret;
+ mutex_lock(&gpio_lock);
+ val = gpio_tps65910_read(TPS65910_REG_GPIO0);
+
+ val |= TPS65910_GPIO_CFG_OUTPUT;
+
+ ret = gpio_tps65910_write(TPS65910_REG_GPIO0, val);
+ mutex_unlock(&gpio_lock);
+
+ if (ret != 0)
+ return -EIO;
+ return 0;
+}
+
+static void tps65910_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ int val = 0;
+ u32 ret;
+
+ mutex_lock(&gpio_lock);
+ val = gpio_tps65910_read(TPS65910_REG_GPIO0);
+
+ if (value == 1)
+ val |= 0x01;
+ else
+ val &= 0xFE;
+
+ ret = gpio_tps65910_write(TPS65910_REG_GPIO0, val);
+
+ mutex_unlock(&gpio_lock);
+}
+
+
+
+static void tps65910_gpio_set_debounce(u8 debounce)
+{
+ u8 val;
+
+ mutex_lock(&gpio_lock);
+
+ val = gpio_tps65910_read(TPS65910_REG_GPIO0);
+
+ if (debounce == TPS65910_DEBOUNCE_91_5_MS)
+ val = (0<<4);
+ else if (debounce == TPS65910_DEBOUNCE_150_MS)
+ val = (1<<4);
+ else
+ printk(KERN_ERR "Invalid argument to %s\n", __func__);
+
+ gpio_tps65910_write(TPS65910_REG_GPIO0, val);
+
+ mutex_unlock(&gpio_lock);
+}
+EXPORT_SYMBOL(tps65910_gpio_set_debounce);
+
+
+static void tps65910_gpio_pullup_enable(void)
+{
+ u8 val;
+ u32 ret;
+
+ mutex_lock(&gpio_lock);
+
+ val = gpio_tps65910_read(TPS65910_REG_GPIO0);
+
+ val = (1<<3);
+
+ ret = gpio_tps65910_write(TPS65910_REG_GPIO0, val);
+
+ mutex_unlock(&gpio_lock);
+
+ if (ret != 0)
+ printk(KERN_ERR "Error writing to TPS65910_REG_GPIO0 in %s \n",
+ __func__);
+}
+EXPORT_SYMBOL(tps65910_gpio_pullup_enable);
+
+static void tps65910_gpio_pullup_disable(void)
+{
+ u8 val;
+ u32 ret;
+
+ mutex_lock(&gpio_lock);
+
+ val = gpio_tps65910_read(TPS65910_REG_GPIO0);
+
+ val = (0<<3);
+
+ ret = gpio_tps65910_write(TPS65910_REG_GPIO0, val);
+
+ mutex_unlock(&gpio_lock);
+}
+EXPORT_SYMBOL(tps65910_gpio_pullup_disable);
+
+static void tps65910_gpio_work(struct work_struct *work)
+{
+
+ /* Read the status register and take action */
+ u8 status2;
+ int err;
+ mutex_lock(&work_lock);
+ err = tps65910_i2c_read_u8(TPS65910_I2C_ID0, &status2,
+ TPS65910_REG_INT_STS);
+ if (!err) {
+ switch (status2) {
+ case TPS65910_GPIO_F_IT:
+ printk(KERN_NOTICE "Received TPS65910 GPIO falling \
+ edge interrupt \n");
+ /* Clear interrupt */
+ tps65910_i2c_write_u8(TPS65910_I2C_ID0, status2,
+ TPS65910_REG_INT_STS);
+ /* Add code accroding to board requirment */
+ break;
+ case TPS65910_GPIO_R_IT:
+ printk(KERN_NOTICE "Received TPS65910 GPIO Raising \
+ edge interrupt \n");
+ /* Clear interrupt */
+ tps65910_i2c_write_u8(TPS65910_I2C_ID0, status2,
+ TPS65910_REG_INT_STS);
+ /* Add code accroding to board requirment */
+ break;
+ }
+ } else {
+ printk(KERN_ERR"Could not read TPS65910_REG_INT_STS\n");
+ }
+
+ mutex_unlock(&work_lock);
+
+}
+
+
+
+static irqreturn_t tps65910_gpio_isr(int irq, void *_tps65910)
+{
+ /* Disable IRQ, schedule work, enable IRQ and acknowledge */
+ disable_irq(irq);
+ (void) schedule_work(&gpio_work);
+ enable_irq(irq);
+ return IRQ_HANDLED;
+}
+
+
+static struct gpio_chip tps65910_gpiochip = {
+ .label = "tps65910",
+ .owner = THIS_MODULE,
+ .request = tps65910_request,
+ .free = tps65910_free,
+ .direction_input = tps65910_direction_in,
+ .get = tps65910_get,
+ .direction_output = tps65910_direction_out,
+ .set = tps65910_set,
+};
+
+
+static int __devinit gpio_tps65910_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ int status = 0;
+
+ struct tps65910_gpio *pdata = pdev->dev.platform_data;
+
+ if (pdata->gpio_mode == TPS65910_GPIO_AS_IRQ) {
+
+ if (pdata->irq_num) {
+ status = request_irq(pdata->irq_num, tps65910_gpio_isr,
+ IRQF_SHARED, "tps65910_gpio", pdev);
+ if (status < 0) {
+ pr_err("tps65910: could not claim irq%d: %d\n",
+ pdata->irq_num, status);
+ }
+
+ }
+
+ INIT_WORK(&gpio_work, tps65910_gpio_work);
+ mutex_init(&work_lock);
+
+ tps65910_gpiochip.ngpio = TPS65910_GPIO_MAX;
+ tps65910_gpiochip.dev = &pdev->dev;
+
+ ret = gpiochip_add(&tps65910_gpiochip);
+
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not register gpiochip \
+ %d\n", ret);
+ tps65910_gpiochip.ngpio = 0;
+ gpio_tps65910_remove(pdev);
+ return -ENODEV;
+ }
+ if (pdata->gpio_setup)
+ pdata->gpio_setup(pdata);
+ }
+ return ret;
+}
+
+static int gpio_tps65910_remove(struct platform_device *pdev)
+{
+ struct tps65910_gpio *pdata = pdev->dev.platform_data;
+ int status;
+
+ if (pdata->gpio_taredown)
+ pdata->gpio_taredown(pdata);
+ if (pdata->gpio_mode == TPS65910_GPIO_AS_IRQ)
+ free_irq(pdata->irq_num, NULL);
+
+ status = gpiochip_remove(&tps65910_gpiochip);
+ if (status < 0)
+ return status;
+ return 0;
+}
+
+static struct platform_driver gpio_tps65910_driver = {
+ .driver.name = "tps65910_gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = gpio_tps65910_probe,
+ .remove = gpio_tps65910_remove,
+};
+
+static int __init gpio_tps65910_init(void)
+{
+ return platform_driver_register(&gpio_tps65910_driver);
+}
+subsys_initcall(gpio_tps65910_init);
+
+static void __exit gpio_tps65910_exit(void)
+{
+ platform_driver_unregister(&gpio_tps65910_driver);
+}
+module_exit(gpio_tps65910_exit);
+
+MODULE_AUTHOR("Mistral Solutions Pvt Ltd.");
+MODULE_DESCRIPTION("GPIO interface for TPS65910");
+MODULE_LICENSE("GPL");
To compile this driver as a module, choose M here. The module will
be called twl4030_pwrbutton.
+config INPUT_TPS65910_PWRBUTTON
+ tristate "TPS65910 Power button Driver"
+ depends on TPS65910_CORE
+ help
+ Say Y here if you want to enable power key reporting via the
+ TPS65910 family of chips.
+
+ To compile this driver as a module, choose M here. The module will
+ be called tps65910_pwrbutton.
+
config INPUT_UINPUT
tristate "User level driver support"
help
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
+obj-$(CONFIG_INPUT_TPS65910_PWRBUTTON) += tps65910-pwrbutton.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
--- /dev/null
+/**
+ * tps65910-pwrbutton.c - TPS65910 Power Button Input Driver
+ *
+ * Copyright (C) 2010 Mistral Solutions Pvt Ltd <www.mistralsolutions.com>
+ *
+ * Based on twl4030-pwrbutton.c
+ *
+ * Written by Srinath.R <srinath@mistralsolutions.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/tps65910.h>
+
+#define TPS65910_PWR_PWRON_IRQ (1 << 2)
+
+
+static irqreturn_t powerbutton_irq(int irq, void *_pwr)
+{
+ struct input_dev *pwr = _pwr;
+ int err;
+ u8 value;
+
+#ifdef CONFIG_LOCKDEP
+ /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+ * we don't want and can't tolerate since this is a threaded
+ * IRQ and can sleep due to the i2c reads it has to issue.
+ * Although it might be friendlier not to borrow this thread
+ * context...
+ */
+ local_irq_enable();
+#endif
+ err = tps65910_i2c_read_u8(TPS65910_I2C_ID0, &value,
+ TPS65910_REG_INT_STS);
+ if (!err && (value & TPS65910_PWR_PWRON_IRQ)) {
+
+ if (value & TPS65910_PWR_PWRON_IRQ) {
+
+ input_report_key(pwr, KEY_POWER,
+ TPS65910_PWR_PWRON_IRQ);
+ input_sync(pwr);
+ return IRQ_HANDLED;
+ }
+ } else {
+ dev_err(pwr->dev.parent, "tps65910: i2c error %d while reading"
+ " TPS65910_REG_INT_STS register\n", err);
+ }
+ return IRQ_HANDLED;
+}
+
+static int __devinit tps65910_pwrbutton_probe(struct platform_device *pdev)
+{
+ struct input_dev *pwr;
+ int irq = platform_get_irq(pdev, 0);
+ int err;
+
+ pwr = input_allocate_device();
+ if (!pwr) {
+ dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ return -ENOMEM;
+ }
+
+ pwr->evbit[0] = BIT_MASK(EV_KEY);
+ pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+ pwr->name = "tps65910_pwrbutton";
+ pwr->phys = "tps65910_pwrbutton/input0";
+ pwr->dev.parent = &pdev->dev;
+
+ err = request_irq(irq, powerbutton_irq,
+ (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+ IRQF_SHARED), "tps65910_pwrbutton", pwr);
+ if (err < 0) {
+ dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err);
+ goto free_input_dev;
+ }
+
+ err = input_register_device(pwr);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
+ goto free_irq;
+ }
+
+ platform_set_drvdata(pdev, pwr);
+
+ return 0;
+
+free_irq:
+ free_irq(irq, NULL);
+free_input_dev:
+ input_free_device(pwr);
+ return err;
+}
+
+static int __devexit tps65910_pwrbutton_remove(struct platform_device *pdev)
+{
+ struct input_dev *pwr = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ free_irq(irq, pwr);
+ input_unregister_device(pwr);
+
+ return 0;
+}
+
+struct platform_driver tps65910_pwrbutton_driver = {
+ .probe = tps65910_pwrbutton_probe,
+ .remove = __devexit_p(tps65910_pwrbutton_remove),
+ .driver = {
+ .name = "tps65910_pwrbutton",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init tps65910_pwrbutton_init(void)
+{
+ return platform_driver_register(&tps65910_pwrbutton_driver);
+}
+module_init(tps65910_pwrbutton_init);
+
+static void __exit tps65910_pwrbutton_exit(void)
+{
+ platform_driver_unregister(&tps65910_pwrbutton_driver);
+}
+module_exit(tps65910_pwrbutton_exit);
+
+MODULE_ALIAS("platform:tps65910_pwrbutton");
+MODULE_DESCRIPTION("TPS65910 Power Button");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Srinath R <srinath@mistralsolutions.com>");
+
int ret;
int gpio = client->irq;
struct eeti_egalax_platform_data *pdata = pdata = client->dev.platform_data;
- DBG();
+
printk(KERN_DEBUG "[egalax_i2c]: start probe\n");
p_egalax_i2c_dev = (struct _egalax_i2c *)kzalloc(sizeof(struct _egalax_i2c), GFP_KERNEL);
and load scripts controling which resources are switched off/on
or reset when a sleep, wakeup or warm reset event occurs.
+config TPS65910_CORE
+ bool "Texas Instruments TPS65910 Support"
+ depends on I2C=y && GENERIC_HARDIRQS
+ help
+ Say yes here if you have TPS65910 family chip on your board.
+ This core driver provides register access and registers devices
+ for the various functions so that function-specific drivers can
+ bind to them.
+
+ These multi-function chips are found on many AM35xx boards,
+ providing power management, RTC, GPIO features.
+
config MFD_TMIO
bool
default n
obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
+obj-$(CONFIG_TPS65910_CORE) += tps65910-core.o
+
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
tps, DEBUG_FOPS);
/* optionally register GPIOs */
- if (board && board->base > 0) {
+ if (board && board->base != 0) {
tps->outmask = board->outmask;
tps->chip.label = client->name;
}
EXPORT_SYMBOL(tps65010_config_vregs1);
+int tps65010_config_vdcdc2(unsigned value)
+{
+ struct i2c_client *c;
+ int status;
+
+ if (!the_tps)
+ return -ENODEV;
+
+ c = the_tps->client;
+ mutex_lock(&the_tps->lock);
+
+ pr_debug("%s: vdcdc2 0x%02x\n", DRIVER_NAME,
+ i2c_smbus_read_byte_data(c, TPS_VDCDC2));
+
+ status = i2c_smbus_write_byte_data(c, TPS_VDCDC2, value);
+
+ if (status != 0)
+ printk(KERN_ERR "%s: Failed to write vdcdc2 register\n",
+ DRIVER_NAME);
+ else
+ pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
+ i2c_smbus_read_byte_data(c, TPS_VDCDC2));
+
+ mutex_unlock(&the_tps->lock);
+ return status;
+}
+EXPORT_SYMBOL(tps65010_config_vdcdc2);
+
/*-------------------------------------------------------------------------*/
/* tps65013_set_low_pwr parameter:
* mode: ON or OFF
--- /dev/null
+/*
+ * tps65910-core.c -- Multifunction core driver for TPS65910x chips
+ *
+ * Copyright (C) 2010 Mistral solutions Pvt Ltd <www.mistralsolutions.com>
+ *
+ * Based on twl-core.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <linux/regulator/machine.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c/tps65910.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#if 1
+#define DBG(x...) printk(KERN_INFO x)
+#else
+#define DBG(x...)
+#endif
+#define TPS65910_SPEED 400 * 1000
+
+
+#define DRIVER_NAME "tps65910"
+
+#if defined(CONFIG_GPIO_TPS65910)
+#define tps65910_has_gpio() true
+#else
+#define tps65910_has_gpio() false
+#endif
+
+#if defined(CONFIG_REGULATOR_TPS65910)
+#define tps65910_has_regulator() true
+#else
+#define tps65910_has_regulator() false
+#endif
+
+#if defined(CONFIG_RTC_DRV_TPS65910)
+#define tps65910_has_rtc() true
+#else
+#define tps65910_has_rtc() false
+#endif
+
+#define TPS65910_GENERAL 0
+#define TPS65910_SMARTREFLEX 1
+
+
+struct tps65910_platform_data *the_tps65910;
+
+enum tps65910x_model {
+ TPS65910, /* TI processors OMAP3 family */
+ TPS659101, /* Samsung - S5PV210, S5PC1xx */
+ TPS659102, /* Samsung - S3C64xx */
+ TPS659103, /* Reserved */
+ TPS659104, /* Reserved */
+ TPS659105, /* TI processors - DM643x, DM644x */
+ TPS659106, /* Reserved */
+ TPS659107, /* Reserved */
+ TPS659108, /* Reserved */
+ TPS659109, /* Freescale - i.MX51 */
+
+};
+
+static bool inuse;
+static struct work_struct core_work;
+static struct mutex work_lock;
+
+/* Structure for each TPS65910 Slave */
+struct tps65910_client {
+ struct i2c_client *client;
+ u8 address;
+ /* max numb of i2c_msg required for read = 2 */
+ struct i2c_msg xfer_msg[2];
+ /* To lock access to xfer_msg */
+ struct mutex xfer_lock;
+};
+static struct tps65910_client tps65910_modules[TPS65910_NUM_SLAVES];
+
+/* bbch = Back-up battery charger control register */
+int tps65910_enable_bbch(u8 voltage)
+{
+ u8 val = 0;
+ int err;
+
+ if (voltage == TPS65910_BBSEL_3P0 || voltage == TPS65910_BBSEL_2P52 ||
+ voltage == TPS65910_BBSEL_3P15 ||
+ voltage == TPS65910_BBSEL_VBAT) {
+ val = (voltage | TPS65910_BBCHEN);
+ err = tps65910_i2c_write_u8(TPS65910_I2C_ID0, val,
+ TPS65910_REG_BBCH);
+ if (err) {
+ printk(KERN_ERR "Unable write TPS65910_REG_BBCH reg\n");
+ return -EIO;
+ }
+ } else {
+ printk(KERN_ERR"Invalid argumnet for %s \n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(tps65910_enable_bbch);
+
+int tps65910_disable_bbch(void)
+{
+ u8 val = 0;
+ int err;
+
+ err = tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_BBCH);
+
+ if (!err) {
+ val &= ~TPS65910_BBCHEN;
+
+ err = tps65910_i2c_write_u8(TPS65910_I2C_ID0, val,
+ TPS65910_REG_BBCH);
+ if (err) {
+ printk(KERN_ERR "Unable write TPS65910_REG_BBCH \
+ reg\n");
+ return -EIO;
+ }
+ } else {
+ printk(KERN_ERR "Unable to read TPS65910_REG_BBCH reg\n");
+ return -EIO;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(tps65910_disable_bbch);
+
+int tps65910_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
+{
+ struct tps65910_client *tps65910;
+ int ret;
+
+ switch (mod_no) {
+ case TPS65910_I2C_ID0:
+ tps65910 = &tps65910_modules[0];
+ tps65910->address = TPS65910_I2C_ID0;
+ break;
+ case TPS65910_I2C_ID1:
+ tps65910 = &tps65910_modules[1];
+ tps65910->address = TPS65910_I2C_ID1;
+ break;
+ default:
+ printk(KERN_ERR "Invalid Slave address for TPS65910\n");
+ return -ENODEV;
+ }
+
+ ret = i2c_master_reg8_recv(tps65910->client, reg, (char *)value, 1, TPS65910_SPEED);
+ DBG("%s: ret=%d, slave_addr=0x%x, reg=0x%x, value=0x%x\n", __FUNCTION__, (ret > 0 ? 0: -EINVAL), mod_no, reg, *value);
+
+ return (ret > 0 ? 0: -EINVAL);
+}
+EXPORT_SYMBOL(tps65910_i2c_read_u8);
+
+int tps65910_i2c_write_u8(u8 slave_addr, u8 value, u8 reg)
+{
+ struct tps65910_client *tps65910;
+ int ret;
+
+ switch (slave_addr) {
+ case TPS65910_I2C_ID0:
+ tps65910 = &tps65910_modules[0];
+ tps65910->address = TPS65910_I2C_ID0;
+ break;
+ case TPS65910_I2C_ID1:
+ tps65910 = &tps65910_modules[1];
+ tps65910->address = TPS65910_I2C_ID1;
+ break;
+ default:
+ printk(KERN_ERR "Invalid Slave address for TPS65910\n");
+ return -ENODEV;
+ }
+
+ ret = i2c_master_reg8_send(tps65910->client, reg, (char *)&value, 1, TPS65910_SPEED);
+ DBG("%s: ret=%d, slave_addr=0x%x, reg=0x%x, value=0x%x\n", __FUNCTION__, ret, slave_addr, reg, value);
+
+ if (ret < 0)
+ return -EIO;
+ else
+ return 0;
+}
+EXPORT_SYMBOL(tps65910_i2c_write_u8);
+
+
+int tps65910_enable_irq(int irq)
+{
+ u8 mask = 0x00;
+
+ if (irq > 7) {
+ irq -= 8;
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0,
+ &mask, TPS65910_REG_INT_MSK2);
+ mask &= ~(1 << irq);
+ return tps65910_i2c_write_u8(TPS65910_I2C_ID0,
+ mask, TPS65910_REG_INT_MSK2);
+ } else {
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0,
+ &mask, TPS65910_REG_INT_MSK);
+ mask &= ~(1 << irq);
+ return tps65910_i2c_write_u8(TPS65910_I2C_ID0,
+ mask, TPS65910_REG_INT_MSK);
+ }
+}
+EXPORT_SYMBOL(tps65910_enable_irq);
+
+int tps65910_disable_irq(int irq)
+{
+ u8 mask = 0x00;
+
+ if (irq > 7) {
+ irq -= 8;
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0,
+ &mask, TPS65910_REG_INT_MSK2);
+ mask |= (1 << irq);
+ return tps65910_i2c_write_u8(TPS65910_I2C_ID0,
+ mask, TPS65910_REG_INT_MSK2);
+ } else {
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0,
+ &mask, TPS65910_REG_INT_MSK);
+ mask = (1 << irq);
+ return tps65910_i2c_write_u8(TPS65910_I2C_ID0,
+ mask, TPS65910_REG_INT_MSK);
+ }
+}
+EXPORT_SYMBOL(tps65910_disable_irq);
+
+int tps65910_add_irq_work(int irq,
+ void (*handler)(void *data))
+{
+ int ret = 0;
+ the_tps65910->handlers[irq] = handler;
+ ret = tps65910_enable_irq(irq);
+
+ return ret;
+}
+EXPORT_SYMBOL(tps65910_add_irq_work);
+
+int tps65910_remove_irq_work(int irq)
+{
+ int ret = 0;
+ ret = tps65910_disable_irq(irq);
+ the_tps65910->handlers[irq] = NULL;
+ return ret;
+}
+EXPORT_SYMBOL(tps65910_remove_irq_work);
+
+static void tps65910_core_work(struct work_struct *work)
+{
+ /* Read the status register and take action */
+ u8 status = 0x00;
+ u8 status2 = 0x00;
+ u8 mask = 0x00;
+ u8 mask2 = 0x00;
+ u16 isr = 0x00;
+ u16 irq = 0;
+ void (*handler)(void *data) = NULL;
+
+ mutex_lock(&work_lock);
+ while (1) {
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &status2,
+ TPS65910_REG_INT_STS2);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &mask2,
+ TPS65910_REG_INT_MSK2);
+ status2 &= (~mask2);
+ isr = (status2 << 8);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &status,
+ TPS65910_REG_INT_STS);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &mask,
+ TPS65910_REG_INT_MSK);
+ status &= ~(mask);
+ isr |= status;
+ if (!isr)
+ break;
+
+ while (isr) {
+ irq = fls(isr) - 1;
+ isr &= ~(1 << irq);
+ handler = the_tps65910->handlers[irq];
+ if (handler)
+ handler(the_tps65910);
+ }
+ }
+ enable_irq(the_tps65910->irq_num);
+ mutex_unlock(&work_lock);
+}
+
+
+static irqreturn_t tps65910_isr(int irq, void *data)
+{
+ disable_irq_nosync(irq);
+ (void) schedule_work(&core_work);
+ return IRQ_HANDLED;
+}
+
+
+static struct device *add_numbered_child(unsigned chip, const char *name,
+ int num, void *pdata, unsigned pdata_len, bool can_wakeup, int irq)
+{
+
+ struct platform_device *pdev;
+ struct tps65910_client *tps65910 = &tps65910_modules[chip];
+ int status;
+
+ pdev = platform_device_alloc(name, num);
+ if (!pdev) {
+ dev_dbg(&tps65910->client->dev, "can't alloc dev\n");
+ status = -ENOMEM;
+ goto err;
+ }
+ device_init_wakeup(&pdev->dev, can_wakeup);
+ pdev->dev.parent = &tps65910->client->dev;
+
+ if (pdata) {
+ status = platform_device_add_data(pdev, pdata, pdata_len);
+ if (status < 0) {
+ dev_dbg(&pdev->dev, "can't add platform_data\n");
+ goto err;
+ }
+ }
+ status = platform_device_add(pdev);
+
+err:
+ if (status < 0) {
+ platform_device_put(pdev);
+ dev_err(&tps65910->client->dev, "can't add %s dev\n", name);
+ return ERR_PTR(status);
+ }
+ return &pdev->dev;
+
+}
+
+static inline struct device *add_child(unsigned chip, const char *name,
+ void *pdata, unsigned pdata_len,
+ bool can_wakeup, int irq)
+{
+ return add_numbered_child(chip, name, -1, pdata, pdata_len,
+ can_wakeup, irq);
+}
+
+static
+struct device *add_regulator_linked(int num, struct regulator_init_data *pdata,
+ struct regulator_consumer_supply *consumers,
+ unsigned num_consumers)
+{
+ /* regulator framework demands init_data */
+ if (!pdata)
+ return NULL;
+
+ if (consumers) {
+ pdata->consumer_supplies = consumers;
+ pdata->num_consumer_supplies = num_consumers;
+ }
+ return add_numbered_child(TPS65910_GENERAL, "tps65910_regulator", num,
+ pdata, sizeof(*pdata), false, TPS65910_HOST_IRQ);
+}
+
+ static struct device *
+add_regulator(int num, struct regulator_init_data *pdata)
+{
+ return add_regulator_linked(num, pdata, NULL, 0);
+}
+
+static int
+add_children(struct tps65910_platform_data *pdata, unsigned long features)
+{
+ int status;
+ struct device *child;
+
+ struct platform_device *pdev = NULL;
+
+ if (tps65910_has_gpio() && (pdata->gpio != NULL)) {
+
+ pdev = platform_device_alloc("tps65910_gpio", -1);
+ if (!pdev) {
+ status = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.parent = &tps65910_modules[0].client->dev;
+ device_init_wakeup(&pdev->dev, 0);
+ if (pdata) {
+ status = platform_device_add_data(pdev, pdata,
+ sizeof(*pdata));
+ if (status < 0) {
+ dev_dbg(&pdev->dev,
+ "can't add platform_data\n");
+ goto err;
+ }
+ }
+ }
+ if (tps65910_has_rtc()) {
+ child = add_child(TPS65910_GENERAL, "tps65910_rtc",
+ NULL, 0, true, pdata->irq_num);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
+ if (tps65910_has_regulator()) {
+
+ child = add_regulator(TPS65910_VIO, pdata->vio);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VDD1, pdata->vdd1);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VDD2, pdata->vdd2);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VDD3, pdata->vdd3);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VDIG1, pdata->vdig1);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VDIG2, pdata->vdig2);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VAUX33, pdata->vaux33);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VMMC, pdata->vmmc);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VAUX1, pdata->vaux1);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VAUX2, pdata->vaux2);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VDAC, pdata->vdac);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TPS65910_VPLL, pdata->vpll);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+ return 0;
+
+err:
+ return -1;
+
+}
+
+static int tps65910_remove(struct i2c_client *client)
+{
+ unsigned i;
+
+ for (i = 0; i < TPS65910_NUM_SLAVES; i++) {
+
+ struct tps65910_client *tps65910 = &tps65910_modules[i];
+
+ if (tps65910->client && tps65910->client != client)
+ i2c_unregister_device(tps65910->client);
+
+ tps65910_modules[i].client = NULL;
+ }
+ inuse = false;
+ return 0;
+}
+
+static int __init
+tps65910_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int status;
+ unsigned i;
+ struct tps65910_platform_data *pdata;
+
+ pdata = client->dev.platform_data;
+ the_tps65910 = pdata;
+
+ DBG("cwz: tps65910_i2c_probe\n");
+
+ if (!pdata) {
+ dev_dbg(&client->dev, "no platform data?\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter,I2C_FUNC_I2C)) {
+ dev_dbg(&client->dev, "can't talk I2C?\n");
+ return -EIO;
+ }
+
+ if (inuse) {
+ dev_dbg(&client->dev, "driver is already in use\n");
+ return -EBUSY;
+ }
+ for (i = 0; i < TPS65910_NUM_SLAVES; i++) {
+
+ struct tps65910_client *tps65910 = &tps65910_modules[i];
+
+ tps65910->address = client->addr;
+
+ if (i == 0)
+ tps65910->client = client;
+ else {
+ tps65910->client = i2c_new_dummy(client->adapter,
+ tps65910->address);
+
+ if (!tps65910->client) {
+ dev_err(&client->dev,
+ "can't attach client %d\n", i);
+ status = -ENOMEM;
+ goto fail;
+ }
+ }
+ mutex_init(&tps65910->xfer_lock);
+ }
+
+ inuse = true;
+
+ if (pdata->board_tps65910_config != NULL)
+ pdata->board_tps65910_config(pdata);
+
+#if 0 // cwz close, the tps65910_core_work may have some error.
+ if (pdata->irq_num) {
+ /* TPS65910 power ON interrupt(s) would have already been
+ * occurred, so immediately after request_irq the control will
+ * be transferred to tps65910_isr, if we do core_work
+ * initialization after requesting IRQ, the system crashes
+ * and does not boot; to avoid this we do core_work
+ * initialization before requesting IRQ
+ */
+ mutex_init(&work_lock);
+ INIT_WORK(&core_work, tps65910_core_work);
+
+ status = request_irq(pdata->irq_num, tps65910_isr,
+ IRQF_DISABLED, "tps65910", pdata);
+ if (status < 0) {
+ pr_err("tps65910: could not claim irq%d: %d\n",
+ pdata->irq_num, status);
+ goto fail;
+ }
+ }
+#endif
+
+ status = add_children(pdata, 0x00);
+ if (status < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ if (status < 0)
+ tps65910_remove(client);
+
+ return status;
+}
+
+
+static int tps65910_i2c_remove(struct i2c_client *client)
+{
+ unsigned i;
+
+ for (i = 0; i < TPS65910_NUM_SLAVES; i++) {
+
+ struct tps65910_client *tps65910 = &tps65910_modules[i];
+
+ if (tps65910->client && tps65910->client != client)
+ i2c_unregister_device(tps65910->client);
+
+ tps65910_modules[i].client = NULL;
+ }
+ inuse = false;
+ return 0;
+}
+
+/* chip-specific feature flags, for i2c_device_id.driver_data */
+static const struct i2c_device_id tps65910_i2c_ids[] = {
+ { "tps65910", TPS65910 },
+ { "tps659101", TPS659101 },
+ { "tps659102", TPS659102 },
+ { "tps659103", TPS659103 },
+ { "tps659104", TPS659104 },
+ { "tps659105", TPS659105 },
+ { "tps659106", TPS659106 },
+ { "tps659107", TPS659107 },
+ { "tps659108", TPS659108 },
+ { "tps659109", TPS659109 },
+ {/* end of list */ },
+};
+MODULE_DEVICE_TABLE(i2c, tps65910_i2c_ids);
+
+/* One Client Driver ,3 Clients - Regulator, RTC , GPIO */
+static struct i2c_driver tps65910_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .id_table = tps65910_i2c_ids,
+ .probe = tps65910_i2c_probe,
+ .remove = __devexit_p(tps65910_i2c_remove),
+};
+
+static int __init tps65910_init(void)
+{
+ int res;
+
+ res = i2c_add_driver(&tps65910_i2c_driver);
+ if (res < 0) {
+ pr_err(DRIVER_NAME ": driver registration failed\n");
+ return res;
+ }
+
+ return 0;
+}
+subsys_initcall_sync(tps65910_init);
+
+static void __exit tps65910_exit(void)
+{
+ i2c_del_driver(&tps65910_i2c_driver);
+}
+module_exit(tps65910_exit);
+
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static int proc_tps65910_show(struct seq_file *s, void *v)
+{
+ u8 val = 0;
+ struct regulator *vldo;
+
+ seq_printf(s, "\n\nTPS65910 Registers is:\n");
+
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VDD1);
+ seq_printf(s, "VDD1_REG=0x%x, Value=0x%x\n", TPS65910_REG_VDD1, val);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VDD1_OP);
+ seq_printf(s, "VDD1_OP_REG=0x%x, Value=0x%x\n", TPS65910_REG_VDD1_OP, val);
+
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VDD2);
+ seq_printf(s, "VDD2_REG=0x%x, Value=0x%x\n", TPS65910_REG_VDD2, val);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VDD2_OP);
+ seq_printf(s, "VDD2_OP_REG=0x%x, Value=0x%x\n", TPS65910_REG_VDD2_OP, val);
+
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VIO);
+ seq_printf(s, "VIO_REG=0x%x, Value=0x%x\n", TPS65910_REG_VIO, val);
+
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VDIG1);
+ seq_printf(s, "VDIG1_REG=0x%x, Value=0x%x\n", TPS65910_REG_VDIG1, val);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VDIG2);
+ seq_printf(s, "VDIG2_REG=0x%x, Value=0x%x\n", TPS65910_REG_VDIG2, val);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VAUX1);
+ seq_printf(s, "VAUX1_REG=0x%x, Value=0x%x\n", TPS65910_REG_VAUX1, val);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VAUX2);
+ seq_printf(s, "VAUX2_REG=0x%x, Value=0x%x\n", TPS65910_REG_VAUX2, val);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VAUX33);
+ seq_printf(s, "VAUX33_REG=0x%x, Value=0x%x\n", TPS65910_REG_VAUX33, val);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VMMC);
+ seq_printf(s, "VMMC_REG=0x%x, Value=0x%x\n", TPS65910_REG_VMMC, val);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VPLL);
+ seq_printf(s, "VPLL_REG=0x%x, Value=0x%x\n", TPS65910_REG_VPLL, val);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_VDAC);
+ seq_printf(s, "VDAC_REG=0x%x, Value=0x%x\n", TPS65910_REG_VDAC, val);
+
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_DEVCTRL);
+ seq_printf(s, "DEVCTRL_REG=0x%x, Value=0x%x\n", TPS65910_REG_DEVCTRL, val);
+ tps65910_i2c_read_u8(TPS65910_I2C_ID0, &val, TPS65910_REG_DEVCTRL2);
+ seq_printf(s, "DEVCTRL2_REG=0x%x, Value=0x%x\n", TPS65910_REG_DEVCTRL2, val);
+
+#if 0 // cwz 1 test vcore
+ vldo = regulator_get(NULL, "vcore");
+ if (vldo != NULL)
+ {
+ int uV = 0;
+
+ seq_printf(s, "Set VCORE.\n");
+ regulator_set_voltage(vldo,1100000,1100000);
+
+ uV = regulator_get_voltage(vldo);
+ seq_printf(s, "Get VCORE=%d(uV).\n", uV);
+ }
+#endif
+ return 0;
+}
+
+static int proc_tps65910_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, proc_tps65910_show, NULL);
+}
+
+static const struct file_operations proc_tps65910_fops = {
+ .open = proc_tps65910_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init proc_tps65910_init(void)
+{
+ proc_create("tps65910", 0, NULL, &proc_tps65910_fops);
+ return 0;
+}
+late_initcall(proc_tps65910_init);
+#endif /* CONFIG_PROC_FS */
+
+MODULE_AUTHOR("cwz <cwz@rock-chips.com>");
+MODULE_DESCRIPTION("I2C Core interface for TPS65910");
+MODULE_LICENSE("GPL");
This driver supports the voltage regulators provided by
this family of companion chips.
+config REGULATOR_TPS65910
+ bool "TI TPS69510x PMIC"
+ depends on TPS65910_CORE
+ help
+ This driver supports the voltage regulators provided by
+ this family of companion chips.
+
config REGULATOR_WM831X
tristate "Wolfson Microelcronics WM831x PMIC regulators"
depends on MFD_WM831X
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl4030-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
--- /dev/null
+/*
+ * tps65910-regulator.c -- support regulators in tps65910x family chips
+ *
+ *
+ * Copyright (C) 2010 Mistral Solutions Pvt Ltd <www.mistralsolutions.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/i2c/tps65910.h>
+
+#if 1
+#define DBG(x...) printk(KERN_INFO x)
+#else
+#define DBG(x...)
+#endif
+
+/*
+ * The TPS65910x family chips include power management, a GPIO
+ * RTC. These chips are often used in AM35xx-based systems.
+ *
+ * This driver implements software-based resource control for various
+ * voltage regulators. This is usually augmented with state machine
+ * based control.
+ */
+
+
+struct tps65910reg_info {
+ /* tps65910 resource ID, for resource control state machine */
+ u8 id;
+ /* voltage in mV = table[VSEL]; table_len must be a power-of-two */
+ u8 table_len;
+ const u16 *table;
+
+ /* regulator specific turn-on delay */
+ u32 delay;
+ /* chip constraints on regulator behavior */
+ u16 min_mV;
+ u16 max_mV;
+ /* used by regulator core */
+ struct regulator_desc desc;
+};
+
+
+/* Supported voltage values for regulators */
+
+/* TPS65910 VIO */
+static const u16 VIO_VSEL_table[] = {
+ 1500, 1800, 2500, 3300,
+};
+
+/* TPS65910 VDD1 and VDD2 */
+/* value round off 12.5 is made as 12 */
+static const u16 VDD1_VSEL_table[] = {
+ 0, 600, 600, 600, 612, 625, 637, 650,
+ 662, 675, 687, 700, 712, 725, 737, 750,
+ 762, 775, 787, 800, 812, 825, 837, 850,
+ 862, 875, 887, 900, 912, 925, 937, 950,
+ 962, 975, 987, 1000, 1012, 1025, 1037, 1050,
+ 1062, 1075, 1087, 1100, 1112, 1125, 1137, 1150,
+ 1162, 1175, 1187, 1200, 1212, 1225, 1237, 1250,
+ 1262, 1275, 1287, 1300, 1312, 1325, 1337, 1350,
+ 1362, 1375, 1387, 1400, 1412, 1425, 1437, 1450,
+ 1462, 1475, 1487, 1500,
+};
+
+static const u16 VDD2_VSEL_table[] = {
+ 0, 600, 600, 600, 612, 625, 637, 650,
+ 662, 675, 687, 700, 712, 725, 737, 750,
+ 762, 775, 787, 800, 812, 825, 837, 850,
+ 862, 875, 887, 900, 912, 925, 937, 950,
+ 962, 975, 987, 1000, 1012, 1025, 1037, 1050,
+ 1062, 1075, 1087, 1100, 1112, 1125, 1137, 1150,
+ 1162, 1175, 1187, 1200, 1212, 1225, 1237, 1250,
+ 1262, 1275, 1287, 1300, 1312, 1325, 1337, 1350,
+ 1362, 1375, 1387, 1400, 1412, 1425, 1437, 1450,
+ 1462, 1475, 1487, 1500,
+};
+
+/* TPS65910 VDD3 */
+static const u16 VDD3_VSEL_table[] = {
+ 5000,
+};
+
+/* VDIG1 */
+static const u16 VDIG1_VSEL_table[] = {
+ 1200, 1500, 1800, 2700,
+};
+
+/* VDIG2 */
+static const u16 VDIG2_VSEL_table[] = {
+ 1000, 1100, 1200, 1800,
+};
+
+/* VAUX33 */
+static const u16 VAUX33_VSEL_table[] = {
+ 1800, 2000, 2800, 3300,
+};
+
+/* VMMC */
+static const u16 VMMC_VSEL_table[] = {
+ 1800, 2800, 3000, 3300,
+};
+
+/* VAUX1 */
+static const u16 VAUX1_VSEL_table[] = {
+ 1800, 2000, 2800, 3300,
+};
+
+/* VAUX2 */
+static const u16 VAUX2_VSEL_table[] = {
+ 1800, 2800, 2900, 3300,
+};
+
+/* VDAC */
+static const u16 VDAC_VSEL_table[] = {
+ 1800, 2600, 2800, 2850,
+};
+
+
+/* VPLL */
+static const u16 VPLL_VSEL_table[] = {
+ 1000, 1100, 1800, 2500,
+};
+
+/* VRTC, supports only enable/disable */
+static const u16 VRTC_VSEL_table[] = {
+ 1800,
+};
+
+static inline int
+tps65910reg_read(struct tps65910reg_info *info, unsigned slave_addr,
+ u8 offset)
+{
+ u8 value;
+ int status;
+ status = tps65910_i2c_read_u8(slave_addr, &value, offset);
+
+ return (status < 0) ? status : value;
+}
+
+static inline int
+tps65910reg_write(struct tps65910reg_info *info, unsigned slave_addr,
+ u8 offset, u8 value)
+{
+ if (0 == tps65910_i2c_write_u8(slave_addr, value, offset))
+ return 0;
+ else
+ return -1;
+}
+
+static u8 tps65910reg_find_offset(u8 regulator_id)
+{
+ u8 offset = 0;
+
+ switch (regulator_id) {
+
+ case TPS65910_VIO:
+ offset = TPS65910_REG_VIO;
+ break;
+ case TPS65910_VDD1:
+ offset = TPS65910_REG_VDD1_OP;
+ break;
+ case TPS65910_VDD2:
+ offset = TPS65910_REG_VDD2_OP;
+ break;
+ case TPS65910_VDD3:
+ offset = TPS65910_REG_VDD3;
+ break;
+ case TPS65910_VDIG1:
+ offset = TPS65910_REG_VDIG1;
+ break;
+ case TPS65910_VDIG2:
+ offset = TPS65910_REG_VDIG2;
+ break;
+ case TPS65910_VAUX33:
+ offset = TPS65910_REG_VAUX33;
+ break;
+ case TPS65910_VMMC:
+ offset = TPS65910_REG_VMMC;
+ break;
+ case TPS65910_VAUX1:
+ offset = TPS65910_REG_VAUX1;
+ break;
+ case TPS65910_VAUX2:
+ offset = TPS65910_REG_VAUX2;
+ break;
+ case TPS65910_VDAC:
+ offset = TPS65910_REG_VDAC;
+ break;
+ case TPS65910_VPLL:
+ offset = TPS65910_REG_VPLL;
+ break;
+ }
+ return offset;
+}
+
+static int tps65910reg_is_enabled(struct regulator_dev *rdev)
+{
+ int val;
+ u8 offset;
+
+ struct tps65910reg_info *info = rdev_get_drvdata(rdev);
+
+ offset = tps65910reg_find_offset(info->id);
+
+ val = tps65910reg_read(info, TPS65910_I2C_ID0, offset);
+ if (val < 0) {
+ printk(KERN_ERR "Unable to read TPS65910 Reg at offset 0x%x= \
+ \n", offset);
+ return -EIO;
+ }
+ if ((val & TPS65910_REG_OHP) || (val & TPS65910_REG_OLP))
+ return 1;
+ else
+ return 0;
+}
+
+
+static int tps65910reg_enable(struct regulator_dev *rdev)
+{
+ int val;
+ u8 offset;
+ struct tps65910reg_info *info = rdev_get_drvdata(rdev);
+
+ offset = tps65910reg_find_offset(info->id);
+
+ val = tps65910reg_read(info, TPS65910_I2C_ID0, offset);
+
+ if (val < 0) {
+
+ printk(KERN_ERR "Unable to read TPS65910 Reg at offset = 0x%x \
+ \n", offset);
+ return -EIO;
+ }
+ val |= TPS65910_REG_OHP;
+
+ return tps65910reg_write(info, TPS65910_I2C_ID0, offset, val);
+}
+
+static int tps65910reg_disable(struct regulator_dev *rdev)
+{
+ int val;
+ u8 offset;
+
+ struct tps65910reg_info *info = rdev_get_drvdata(rdev);
+
+ offset = tps65910reg_find_offset(info->id);
+
+ val = tps65910reg_read(info, TPS65910_I2C_ID0, offset);
+
+ if (val < 0) {
+
+ printk(KERN_ERR "Unable to read TPS65910 Reg at offset = \
+ 0x%x\n", offset);
+ return -EIO;
+ }
+ val &= TPS65910_REG_OFF_00;
+
+ return tps65910reg_write(info, TPS65910_I2C_ID0, offset, val);
+}
+
+static int tps65910reg_get_status(struct regulator_dev *rdev)
+{
+ int val;
+ u8 offset;
+ u8 ret;
+ struct tps65910reg_info *info = rdev_get_drvdata(rdev);
+
+ offset = tps65910reg_find_offset(info->id);
+
+ val = tps65910reg_read(info, TPS65910_I2C_ID0, offset);
+
+ if (val < 0) {
+
+ printk(KERN_ERR "Unable to read TPS65910 Reg at offset = \
+ 0x%x\n", offset);
+ return -EIO;
+ }
+ switch ((val & SUPPLY_STATE_FLAG)) {
+
+ case TPS65910_REG_OFF_00:
+ case TPS65910_REG_OFF_10:
+ ret = REGULATOR_STATUS_OFF;
+ break;
+ case TPS65910_REG_OHP:
+ case TPS65910_REG_OLP:
+ ret = REGULATOR_STATUS_ON;
+ break;
+ default:
+ ret = REGULATOR_STATUS_OFF;
+ }
+ return ret;
+}
+
+
+static int tps65910reg_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct tps65910reg_info *info = rdev_get_drvdata(rdev);
+ u8 offset;
+ u8 val;
+
+ offset = tps65910reg_find_offset(info->id);
+ val = tps65910reg_read(info, TPS65910_I2C_ID0, offset);
+
+ if (val < 0) {
+ printk(KERN_ERR"Unable to read TPS65910 Reg at offset \
+ = 0x%x\n", offset);
+ return -EIO;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ return tps65910reg_write(info, TPS65910_I2C_ID0, offset,
+ (val | TPS65910_REG_OHP));
+ case REGULATOR_MODE_STANDBY:
+ return tps65910reg_write(info, TPS65910_I2C_ID0, offset,
+ (val | TPS65910_REG_OLP));
+ default:
+ return -EINVAL;
+ }
+}
+
+static
+int tps65910_ldo_list_voltage(struct regulator_dev *rdev, unsigned index)
+{
+ struct tps65910reg_info *info = rdev_get_drvdata(rdev);
+ int mV = info->table[index];
+ return mV * 1000;
+}
+
+static int get_voltage_index(int ldo_id, int uv)
+{
+ int i = 0;
+ int size = 0;
+ u16 *ptr = NULL;
+
+ uv = uv/1000;
+
+ if (((ldo_id == TPS65910_VDD1) || (ldo_id == TPS65910_VDD2))) {
+ for (i = 0; i < ARRAY_SIZE(VDD1_VSEL_table); i++) {
+ if (VDD1_VSEL_table[i] == uv) {
+ DBG("%s: regulator id=%d, volage=%d, get index=%d\n", __FUNCTION__, ldo_id, uv, i);
+ return i;
+ }
+ }
+ if (i == ARRAY_SIZE(VDD1_VSEL_table)) {
+ DBG("%s: regulator id=%d, can't find volage index\n", __FUNCTION__, ldo_id);
+ return -1;
+ }
+ }
+
+ /* Lookup table to match LDO volatge to Index*/
+ switch (ldo_id) {
+
+ case TPS65910_VIO:
+ ptr = (u16 *)&VIO_VSEL_table[0];
+ size = ARRAY_SIZE(VIO_VSEL_table);
+ break;
+ case TPS65910_VDIG1:
+ ptr = (u16 *)&VDIG1_VSEL_table[0];
+ size = ARRAY_SIZE(VDIG1_VSEL_table);
+ break;
+ case TPS65910_VDIG2:
+ ptr = (u16 *)&VDIG2_VSEL_table[0];
+ size = ARRAY_SIZE(VDIG2_VSEL_table);
+ break;
+ case TPS65910_VAUX33:
+ ptr = (u16 *)&VAUX33_VSEL_table[0];
+ size = ARRAY_SIZE(VAUX33_VSEL_table);
+ break;
+ case TPS65910_VMMC:
+ ptr = (u16 *)&VMMC_VSEL_table[0];
+ size = ARRAY_SIZE(VMMC_VSEL_table);
+ break;
+ case TPS65910_VAUX1:
+ ptr = (u16 *)&VAUX1_VSEL_table[0];
+ size = ARRAY_SIZE(VAUX1_VSEL_table);
+ break;
+ case TPS65910_VAUX2:
+ ptr = (u16 *)&VAUX2_VSEL_table[0];
+ size = ARRAY_SIZE(VAUX2_VSEL_table);
+ break;
+ case TPS65910_VDAC:
+ ptr = (u16 *)&VDAC_VSEL_table[0];
+ size = ARRAY_SIZE(VDAC_VSEL_table);
+ break;
+ case TPS65910_VPLL:
+ ptr = (u16 *)&VPLL_VSEL_table[0];
+ size = ARRAY_SIZE(VPLL_VSEL_table);
+ break;
+ default:
+ ptr = NULL;
+ break;
+ }
+
+ if (ptr != NULL) {
+ for (i = 0; i < size; i++) {
+ if (*ptr++ == uv) {
+ DBG("%s: regulator id=%d, volage=%d, get index=%d\n", __FUNCTION__, ldo_id, uv, i);
+ return i;
+ }
+ }
+ }
+ DBG("%s: regulator id=%d, can't find volage index\n", __FUNCTION__, ldo_id);
+
+ if (ptr == NULL || i == size)
+ return -1;
+ /* For warning */
+ return -1;
+}
+
+static int get_index_voltage(int ldo_id, int index)
+{
+ int vsel;
+ int size = 0;
+ u16 *ptr = NULL;
+
+ /* Get the index of voltage value from Reg and map to table */
+ if (ldo_id == TPS65910_VDD1 || ldo_id == TPS65910_VDD2) {
+ index &= 0x7F;
+ ptr = (u16 *)&VDD1_VSEL_table[0];
+ size = ARRAY_SIZE(VDD1_VSEL_table);
+ /* For VDD1 and VDD2 */
+ if (index >= size) {
+ vsel = size - 1;
+ } else {
+ vsel = index;
+ }
+ } else {
+ vsel = (index & 0xF3);
+ vsel = (vsel >> 2);
+
+ /* Lookup table to match LDO volatge to Index*/
+ switch (ldo_id) {
+
+ case TPS65910_VIO:
+ ptr = (u16 *)&VIO_VSEL_table[0];
+ size = ARRAY_SIZE(VIO_VSEL_table);
+ break;
+ case TPS65910_VDIG1:
+ ptr = (u16 *)&VDIG1_VSEL_table[0];
+ size = ARRAY_SIZE(VDIG1_VSEL_table);
+ break;
+ case TPS65910_VDIG2:
+ ptr = (u16 *)&VDIG2_VSEL_table[0];
+ size = ARRAY_SIZE(VDIG2_VSEL_table);
+ break;
+ case TPS65910_VAUX33:
+ ptr = (u16 *)&VAUX33_VSEL_table[0];
+ size = ARRAY_SIZE(VAUX33_VSEL_table);
+ break;
+ case TPS65910_VMMC:
+ ptr = (u16 *)&VMMC_VSEL_table[0];
+ size = ARRAY_SIZE(VMMC_VSEL_table);
+ break;
+ case TPS65910_VAUX1:
+ ptr = (u16 *)&VAUX1_VSEL_table[0];
+ size = ARRAY_SIZE(VAUX1_VSEL_table);
+ break;
+ case TPS65910_VAUX2:
+ ptr = (u16 *)&VAUX2_VSEL_table[0];
+ size = ARRAY_SIZE(VAUX2_VSEL_table);
+ break;
+ case TPS65910_VDAC:
+ ptr = (u16 *)&VDAC_VSEL_table[0];
+ size = ARRAY_SIZE(VDAC_VSEL_table);
+ break;
+ case TPS65910_VPLL:
+ ptr = (u16 *)&VPLL_VSEL_table[0];
+ size = ARRAY_SIZE(VPLL_VSEL_table);
+ break;
+ default:
+ ptr = NULL;
+ break;
+ }
+
+ if (vsel >= size)
+ vsel = size - 1;
+ }
+
+ if (ptr != NULL) {
+ DBG("%s: regulator id=%d index=%d, get volage=%d(mV)\n", __FUNCTION__, ldo_id, index, ptr[vsel]);
+ return ptr[vsel] * 1000;
+ }
+ /* For warning */
+ DBG("%s: regulator id=%d index=%d, get volage error\n", __FUNCTION__, ldo_id, index);
+ return -1;
+}
+
+static int
+tps65910_ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
+{
+ struct tps65910reg_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+ u8 offset;
+ u8 val;
+ u8 index;
+
+ if (info == NULL)
+ return 0;
+
+ DBG("%s: regulator(%s) id=%d, set volage=(min_uV:%d, max_uV:%d)\n", __FUNCTION__, info->desc.name, info->id, min_uV, max_uV);
+ if (rdev->constraints) {
+ if (min_uV < rdev->constraints->min_uV || min_uV > rdev->constraints->max_uV)
+ return -EINVAL;
+ if (max_uV < rdev->constraints->min_uV || max_uV > rdev->constraints->max_uV)
+ return -EINVAL;
+ }
+
+ for (vsel = 0; vsel < info->table_len; vsel++) {
+
+ int mV = info->table[vsel];
+ int uV;
+
+ uV = mV * 1000;
+
+ /* Break at the first in-range value */
+ if (min_uV <= uV && uV <= max_uV) {
+ offset = tps65910reg_find_offset(info->id);
+ val = tps65910reg_read(info, TPS65910_I2C_ID0, offset);
+ if (val < 0) {
+ printk(KERN_ERR"Unable to read TPS65910 Reg at offset = 0x%x\n",
+ offset);
+ return -EIO;
+ }
+
+ index = get_voltage_index(info->id, uV);
+ /* For VDD1 and VDD2 */
+ if (info->id == TPS65910_VDD1 || info->id == TPS65910_VDD2) {
+ val = index;
+ return tps65910reg_write(info, TPS65910_I2C_ID0, offset, val);
+ }
+
+ val &= 0xF3;
+ val = (index << 2);
+ val |= 0x01;
+ if (index < 0) {
+ printk(KERN_ERR "Invaild voltage for LDO \n");
+ return -EINVAL;
+ }
+
+ return tps65910reg_write(info, TPS65910_I2C_ID0, offset, val);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int tps65910_ldo_get_voltage(struct regulator_dev *rdev)
+{
+ struct tps65910reg_info *info = rdev_get_drvdata(rdev);
+ int vsel;
+ u8 offset;
+
+ offset = tps65910reg_find_offset(info->id);
+ vsel = tps65910reg_read(info, TPS65910_I2C_ID0, offset);
+ if (vsel < 0) {
+ printk(KERN_ERR"Unable to read TPS65910 Reg at offset = \
+ 0x%x\n", offset);
+ return -EIO;
+ }
+
+ /* Get the index of voltage value from Reg and map to table */
+ return get_index_voltage(info->id, vsel);
+}
+
+
+static struct regulator_ops tps65910_ldo_ops = {
+ .list_voltage = tps65910_ldo_list_voltage,
+ .set_voltage = tps65910_ldo_set_voltage,
+ .get_voltage = tps65910_ldo_get_voltage,
+ .enable = tps65910reg_enable,
+ .disable = tps65910reg_disable,
+ .is_enabled = tps65910reg_is_enabled,
+ .set_mode = tps65910reg_set_mode,
+ .get_status = tps65910reg_get_status,
+};
+
+static
+int tps65910_fixed_list_voltage(struct regulator_dev *rdev, unsigned index)
+{
+ struct tps65910reg_info *info = rdev_get_drvdata(rdev);
+
+ return info->min_mV * 1000;
+}
+
+static int tps65910_fixed_get_voltage(struct regulator_dev *rdev)
+{
+ struct tps65910reg_info *info = rdev_get_drvdata(rdev);
+
+ return info->min_mV * 1000;
+}
+
+static struct regulator_ops tps65910_fixed_ops = {
+ .list_voltage = tps65910_fixed_list_voltage,
+ .get_voltage = tps65910_fixed_get_voltage,
+ .enable = tps65910reg_enable,
+ .disable = tps65910reg_disable,
+ .is_enabled = tps65910reg_is_enabled,
+ .set_mode = tps65910reg_set_mode,
+ .get_status = tps65910reg_get_status,
+};
+
+#define TPS65910_ADJUSTABLE_LDO(label, num, min_mVolts, max_mVolts,\
+ turnon_delay) { \
+ .id = num, \
+ .table_len = ARRAY_SIZE(label##_VSEL_table), \
+ .table = label##_VSEL_table, \
+ .min_mV = min_mVolts, \
+ .max_mV = max_mVolts, \
+ .delay = turnon_delay, \
+ .desc = { \
+ .name = #label, \
+ .id = TPS65910_##label, \
+ .n_voltages = ARRAY_SIZE(label##_VSEL_table), \
+ .ops = &tps65910_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+}
+
+#define TPS65910_FIXED_LDO(label, num, mVolts, turnon_delay) { \
+ .id = num, \
+ .min_mV = mVolts, \
+ .delay = turnon_delay, \
+ .desc = { \
+ .name = #label, \
+ .id = TPS65910_##label, \
+ .n_voltages = 1, \
+ .ops = &tps65910_fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+}
+
+/*
+ * We list regulators here if systems need some level of
+ * software control over them after boot.
+ */
+static struct tps65910reg_info tps65910_regs[] = {
+
+ TPS65910_ADJUSTABLE_LDO(VDD1, TPS65910_VDD1, 950, 1400, 1500),
+ TPS65910_FIXED_LDO(VDD2, TPS65910_VDD2, 1200, 1500),
+ TPS65910_FIXED_LDO(VIO, TPS65910_VIO, 3300, 3300),
+
+ TPS65910_FIXED_LDO(VDD3, TPS65910_VDD3, 5000, 200),
+
+ TPS65910_FIXED_LDO(VDIG1, TPS65910_VDIG1, 2700, 2700),
+ TPS65910_FIXED_LDO(VDIG2, TPS65910_VDIG2, 1200, 1800),
+ TPS65910_FIXED_LDO(VAUX33, TPS65910_VAUX33, 3300, 3300),
+ TPS65910_FIXED_LDO(VMMC, TPS65910_VMMC, 3000, 3300),
+ TPS65910_FIXED_LDO(VAUX1, TPS65910_VAUX1, 2800, 3300),
+ TPS65910_FIXED_LDO(VAUX2, TPS65910_VAUX1, 2900, 3300),
+ TPS65910_FIXED_LDO(VDAC, TPS65910_VDAC, 1800, 2850),
+ TPS65910_FIXED_LDO(VPLL, TPS65910_VPLL, 2500, 2500),
+};
+
+static int tps65910_regulator_probe(struct platform_device *pdev)
+{
+ int i;
+ struct tps65910reg_info *info;
+ struct regulator_init_data *initdata;
+ struct regulation_constraints *c;
+ struct regulator_dev *rdev;
+
+
+ DBG("%s\n", __FUNCTION__);
+ for (i = 0, info = NULL; i < ARRAY_SIZE(tps65910_regs); i++) {
+ if (tps65910_regs[i].desc.id != pdev->id)
+ continue;
+ info = tps65910_regs + i;
+ break;
+ }
+ if (!info)
+ return -ENODEV;
+
+ DBG("%s: reguloter(%s) id=%d\n", __FUNCTION__, info->desc.name, info->id);
+
+ initdata = pdev->dev.platform_data;
+ if (!initdata)
+ return -EINVAL;
+
+ /* Constrain board-specific capabilities according to what
+ * this driver and the chip itself can actually do.
+ */
+ c = &initdata->constraints;
+ c->valid_modes_mask &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY;
+ c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS;
+
+ switch (pdev->id) {
+ case TPS65910_REG_VDD1:
+ case TPS65910_REG_VIO:
+ case TPS65910_REG_VDD2:
+ case TPS65910_REG_VPLL:
+ case TPS65910_REG_VDIG2:
+ c->always_on = true;
+ break;
+ default:
+ break;
+ }
+
+ rdev = regulator_register(&info->desc, &pdev->dev, initdata, info);
+
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "can't register %s, %ld\n",
+ info->desc.name, PTR_ERR(rdev));
+ return PTR_ERR(rdev);
+ }
+ platform_set_drvdata(pdev, rdev);
+
+ DBG("%s: reguloter register OK.\n", __FUNCTION__);
+ return 0;
+}
+
+static int __devexit tps65910_regulator_remove(struct platform_device *pdev)
+{
+ regulator_unregister(platform_get_drvdata(pdev));
+ return 0;
+}
+
+static struct platform_driver tps65910_regulator_driver = {
+ .probe = tps65910_regulator_probe,
+ .remove = tps65910_regulator_remove,
+ .driver.name = "tps65910_regulator",
+ .driver.owner = THIS_MODULE,
+};
+
+static int __init tps65910_regulator_init(void)
+{
+ return platform_driver_register(&tps65910_regulator_driver);
+}
+module_init(tps65910_regulator_init);
+
+static void __exit tps65910_regulator_exit(void)
+{
+ platform_driver_unregister(&tps65910_regulator_driver);
+}
+module_exit(tps65910_regulator_exit)
+
+MODULE_AUTHOR("cwz <cwz@rock-chips.com>");
+MODULE_DESCRIPTION("TPS65910 voltage regulator driver");
+MODULE_LICENSE("GPL");
This driver can also be built as a module. If so, the module
will be called rtc-twl4030.
+config RTC_DRV_TPS65910
+ boolean "TI TPS65910"
+ depends on RTC_CLASS && TPS65910_CORE
+ help
+ If you say yes here you get support for the RTC on the
+ TPS65910 family chips, used mostly with OMAP3/AM35xx platforms.
+
config RTC_DRV_S35390A
tristate "Seiko Instruments S-35390A"
select BITREVERSE
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o
+obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
--- /dev/null
+/*
+ * rtc-tps65910.c -- TPS65910 Real Time Clock interface
+ *
+ * Copyright (C) 2010 Mistral Solutions Pvt Ltd. <www.mistralsolutions.com>
+ * Author: Umesh K <umeshk@mistralsolutions.com>
+ *
+ * Based on rtc-twl.c
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/i2c/tps65910.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#if 0
+#define DBG(x...) printk(KERN_INFO x)
+#else
+#define DBG(x...)
+#endif
+
+/* RTC Definitions */
+/* RTC_CTRL_REG bitfields */
+#define BIT_RTC_CTRL_REG_STOP_RTC_M 0x01
+#define BIT_RTC_CTRL_REG_ROUND_30S_M 0x02
+#define BIT_RTC_CTRL_REG_AUTO_COMP_M 0x04
+#define BIT_RTC_CTRL_REG_MODE_12_24_M 0x08
+#define BIT_RTC_CTRL_REG_TEST_MODE_M 0x10
+#define BIT_RTC_CTRL_REG_SET_32_COUNTER_M 0x20
+#define BIT_RTC_CTRL_REG_GET_TIME_M 0x40
+#define BIT_RTC_CTRL_REG_RTC_V_OPT_M 0x80
+
+/* RTC_STATUS_REG bitfields */
+#define BIT_RTC_STATUS_REG_RUN_M 0x02
+#define BIT_RTC_STATUS_REG_1S_EVENT_M 0x04
+#define BIT_RTC_STATUS_REG_1M_EVENT_M 0x08
+#define BIT_RTC_STATUS_REG_1H_EVENT_M 0x10
+#define BIT_RTC_STATUS_REG_1D_EVENT_M 0x20
+#define BIT_RTC_STATUS_REG_ALARM_M 0x40
+#define BIT_RTC_STATUS_REG_POWER_UP_M 0x80
+
+/* RTC_INTERRUPTS_REG bitfields */
+#define BIT_RTC_INTERRUPTS_REG_EVERY_M 0x03
+#define BIT_RTC_INTERRUPTS_REG_IT_TIMER_M 0x04
+#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M 0x08
+
+/* DEVCTRL bitfields */
+#define BIT_RTC_PWDN 0x40
+
+/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */
+#define ALL_TIME_REGS 6
+
+/*
+ * Supports 1 byte read from TPS65910 RTC register.
+ */
+static int tps65910_rtc_read_u8(u8 *data, u8 reg)
+{
+ int ret;
+
+ ret = tps65910_i2c_read_u8(TPS65910_I2C_ID0, data, reg);
+
+ if (ret < 0)
+ pr_err("tps65910_rtc: Could not read TPS65910"
+ "register %X - error %d\n", reg, ret);
+ return ret;
+}
+
+/*
+ * Supports 1 byte write to TPS65910 RTC registers.
+ */
+static int tps65910_rtc_write_u8(u8 data, u8 reg)
+{
+ int ret;
+
+ ret = tps65910_i2c_write_u8(TPS65910_I2C_ID0, data, reg);
+ if (ret < 0)
+ pr_err("tps65910_rtc: Could not write TPS65910"
+ "register %X - error %d\n", reg, ret);
+ return ret;
+}
+
+/*
+ * Cache the value for timer/alarm interrupts register; this is
+ * only changed by callers holding rtc ops lock (or resume).
+ */
+static unsigned char rtc_irq_bits;
+
+/*
+ * Enable 1/second update and/or alarm interrupts.
+ */
+static int set_rtc_irq_bit(unsigned char bit)
+{
+ unsigned char val;
+ int ret;
+
+ val = rtc_irq_bits | bit;
+ val |= bit;
+ ret = tps65910_rtc_write_u8(val, TPS65910_REG_RTC_INTERRUPTS);
+ if (ret == 0)
+ rtc_irq_bits = val;
+
+ return ret;
+}
+
+/*
+ * Disable update and/or alarm interrupts.
+ */
+static int mask_rtc_irq_bit(unsigned char bit)
+{
+ unsigned char val;
+ int ret;
+
+ val = rtc_irq_bits & ~bit;
+ ret = tps65910_rtc_write_u8(val, TPS65910_REG_RTC_INTERRUPTS);
+ if (ret == 0)
+ rtc_irq_bits = val;
+
+ return ret;
+}
+
+static int tps65910_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
+{
+ int ret;
+
+ if (enabled)
+ ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ else
+ ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+
+ return ret;
+}
+
+static int tps65910_rtc_update_irq_enable(struct device *dev, unsigned enabled)
+{
+ int ret;
+
+ if (enabled)
+ ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+ else
+ ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+
+ return ret;
+}
+
+#if 1 /* Debugging periodic interrupts */
+/*
+ * We will just handle setting the frequency and make use the framework for
+ * reading the periodic interupts.
+ *
+ * @freq: Current periodic IRQ freq:
+ * bit 0: every second
+ * bit 1: every minute
+ * bit 2: every hour
+ * bit 3: every day
+ */
+
+static int tps65910_rtc_irq_set_freq(struct device *dev, int freq)
+{
+ struct rtc_device *rtc = dev_get_drvdata(dev);
+
+ if (freq < 0 || freq > 3)
+ return -EINVAL;
+
+ rtc->irq_freq = freq;
+ /* set rtc irq freq to user defined value */
+ set_rtc_irq_bit(freq);
+
+ return 0;
+}
+#endif
+
+/*
+ * Gets current TPS65910 RTC time and date parameters.
+ *
+ * The RTC's time/alarm representation is not what gmtime(3) requires
+ * Linux to use:
+ *
+ * - Months are 1..12 vs Linux 0-11
+ * - Years are 0..99 vs Linux 1900..N (we assume 21st century)
+ */
+static int tps65910_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char rtc_data[ALL_TIME_REGS + 1];
+ int ret;
+ u8 save_control;
+
+ tps65910_rtc_read_u8(&save_control, TPS65910_REG_RTC_CTRL);
+ ret = tps65910_rtc_read_u8(&save_control, TPS65910_REG_RTC_CTRL);
+ if (ret < 0)
+ return ret;
+
+ save_control &= ~BIT_RTC_CTRL_REG_RTC_V_OPT_M;
+
+ ret = tps65910_rtc_write_u8(save_control, TPS65910_REG_RTC_CTRL);
+ if (ret < 0)
+ return ret;
+
+ ret = tps65910_rtc_read_u8(&rtc_data[0], TPS65910_REG_SECONDS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_read_u8(&rtc_data[1], TPS65910_REG_MINUTES);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_read_u8(&rtc_data[2], TPS65910_REG_HOURS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_read_u8(&rtc_data[3], TPS65910_REG_DAYS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_read_u8(&rtc_data[4], TPS65910_REG_MONTHS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_read_u8(&rtc_data[5], TPS65910_REG_YEARS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[0]);
+ tm->tm_min = bcd2bin(rtc_data[1]);
+ tm->tm_hour = bcd2bin(rtc_data[2]);
+ tm->tm_mday = bcd2bin(rtc_data[3]);
+ tm->tm_mon = bcd2bin(rtc_data[4]) - 1;
+ tm->tm_year = bcd2bin(rtc_data[5]) + 100;
+
+ DBG("%s [%d]tm_wday=%d \n",__FUNCTION__,__LINE__,tm->tm_wday);
+ DBG("%s [%d]tm_sec=%d \n",__FUNCTION__,__LINE__,tm->tm_sec);
+ DBG("%s [%d]tm_min=%d \n",__FUNCTION__,__LINE__,tm->tm_min);
+ DBG("%s [%d]tm_hour=%d \n",__FUNCTION__,__LINE__,tm->tm_hour);
+ DBG("%s [%d]tm_mday=%d \n",__FUNCTION__,__LINE__,tm->tm_mday);
+ DBG("%s [%d]tm_mon=%d \n",__FUNCTION__,__LINE__,tm->tm_mon);
+ DBG("%s [%d]tm_year=%d \n",__FUNCTION__,__LINE__,tm->tm_year);
+
+ return ret;
+}
+
+static int tps65910_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char save_control;
+ unsigned char rtc_data[ALL_TIME_REGS + 1];
+ int ret;
+
+ DBG("%s [%d]tm_wday=%d \n",__FUNCTION__,__LINE__,tm->tm_wday);
+ DBG("%s [%d]tm_sec=%d \n",__FUNCTION__,__LINE__,tm->tm_sec);
+ DBG("%s [%d]tm_min=%d \n",__FUNCTION__,__LINE__,tm->tm_min);
+ DBG("%s [%d]tm_hour=%d \n",__FUNCTION__,__LINE__,tm->tm_hour);
+ DBG("%s [%d]tm_mday=%d \n",__FUNCTION__,__LINE__,tm->tm_mday);
+ DBG("%s [%d]tm_mon=%d \n",__FUNCTION__,__LINE__,tm->tm_mon);
+ DBG("%s [%d]tm_year=%d \n",__FUNCTION__,__LINE__,tm->tm_year);
+
+ rtc_data[1] = bin2bcd(tm->tm_sec);
+ rtc_data[2] = bin2bcd(tm->tm_min);
+ rtc_data[3] = bin2bcd(tm->tm_hour);
+ rtc_data[4] = bin2bcd(tm->tm_mday);
+ rtc_data[5] = bin2bcd(tm->tm_mon + 1);
+ rtc_data[6] = bin2bcd(tm->tm_year - 100);
+
+ /*Dummy read*/
+ ret = tps65910_rtc_read_u8(&save_control, TPS65910_REG_RTC_CTRL);
+
+ /* Stop RTC while updating the TC registers */
+ ret = tps65910_rtc_read_u8(&save_control, TPS65910_REG_RTC_CTRL);
+ if (ret < 0)
+ goto out;
+
+ save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
+
+ tps65910_rtc_write_u8(save_control, TPS65910_REG_RTC_CTRL);
+
+ /* update all the time registers in one shot */
+ ret = tps65910_rtc_write_u8(rtc_data[1], TPS65910_REG_SECONDS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_write_u8(rtc_data[2], TPS65910_REG_MINUTES);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_write_u8(rtc_data[3], TPS65910_REG_HOURS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_write_u8(rtc_data[4], TPS65910_REG_DAYS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_write_u8(rtc_data[5], TPS65910_REG_MONTHS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_write_u8(rtc_data[6], TPS65910_REG_YEARS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+
+ /*Dummy read*/
+ ret = tps65910_rtc_read_u8(&save_control, TPS65910_REG_RTC_CTRL);
+
+ ret = tps65910_rtc_read_u8(&save_control, TPS65910_REG_RTC_CTRL);
+ if (ret < 0)
+ goto out;
+ /* Start back RTC */
+ save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M;
+ ret = tps65910_rtc_write_u8(save_control, TPS65910_REG_RTC_CTRL);
+
+out:
+ return ret;
+}
+
+/*
+ * Gets current TPS65910 RTC alarm time.
+ */
+static int tps65910_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char rtc_data[ALL_TIME_REGS + 1];
+ int ret;
+
+ ret = tps65910_rtc_read_u8(&rtc_data[0], TPS65910_REG_ALARM_SECONDS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_read_u8(&rtc_data[1], TPS65910_REG_ALARM_MINUTES);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_read_u8(&rtc_data[2], TPS65910_REG_ALARM_HOURS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_read_u8(&rtc_data[3], TPS65910_REG_ALARM_DAYS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_read_u8(&rtc_data[4], TPS65910_REG_ALARM_MONTHS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_read_u8(&rtc_data[5], TPS65910_REG_ALARM_YEARS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_time error %d\n", ret);
+ return ret;
+ }
+
+ /* some of these fields may be wildcard/"match all" */
+ alm->time.tm_sec = bcd2bin(rtc_data[0]);
+ alm->time.tm_min = bcd2bin(rtc_data[1]);
+ alm->time.tm_hour = bcd2bin(rtc_data[2]);
+ alm->time.tm_mday = bcd2bin(rtc_data[3]);
+ alm->time.tm_mon = bcd2bin(rtc_data[4]) - 1;
+ alm->time.tm_year = bcd2bin(rtc_data[5]) + 100;
+
+ /* report cached alarm enable state */
+ if (rtc_irq_bits & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M)
+ alm->enabled = 1;
+
+ return ret;
+}
+
+static int tps65910_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char alarm_data[ALL_TIME_REGS + 1];
+ int ret;
+
+ ret = tps65910_rtc_alarm_irq_enable(dev, 0);
+ if (ret)
+ goto out;
+
+ alarm_data[1] = bin2bcd(alm->time.tm_sec);
+ alarm_data[2] = bin2bcd(alm->time.tm_min);
+ alarm_data[3] = bin2bcd(alm->time.tm_hour);
+ alarm_data[4] = bin2bcd(alm->time.tm_mday);
+ alarm_data[5] = bin2bcd(alm->time.tm_mon + 1);
+ alarm_data[6] = bin2bcd(alm->time.tm_year - 100);
+
+ /* update all the alarm registers in one shot */
+ ret = tps65910_rtc_write_u8(alarm_data[1], TPS65910_REG_ALARM_SECONDS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_write_u8(alarm_data[2], TPS65910_REG_ALARM_MINUTES);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_write_u8(alarm_data[3], TPS65910_REG_ALARM_HOURS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_write_u8(alarm_data[4], TPS65910_REG_ALARM_DAYS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_write_u8(alarm_data[5], TPS65910_REG_ALARM_MONTHS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+ ret = tps65910_rtc_write_u8(alarm_data[6], TPS65910_REG_ALARM_YEARS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_write_time error %d\n", ret);
+ return ret;
+ }
+
+ if (alm->enabled)
+ ret = tps65910_rtc_alarm_irq_enable(dev, 1);
+out:
+ return ret;
+}
+
+
+struct work_struct rtc_wq;
+unsigned long rtc_events;
+struct rtc_device *global_rtc;
+
+void rtc_work(void *data)
+{
+
+ int res;
+ u8 rd_reg;
+ unsigned long events = 0;
+
+ res = tps65910_rtc_read_u8(&rd_reg, TPS65910_REG_INT_STS);
+
+ if (res < 0)
+ goto out;
+ /*
+ * Figure out source of interrupt: ALARM or TIMER in RTC_STATUS_REG.
+ * only one (ALARM or RTC) interrupt source may be enabled
+ * at time, we also could check our results
+ * by reading RTS_INTERRUPTS_REGISTER[IT_TIMER,IT_ALARM]
+ */
+ if (rd_reg & TPS65910_RTC_ALARM_IT) {
+ res = tps65910_rtc_write_u8(rd_reg | TPS65910_RTC_ALARM_IT,
+ TPS65910_REG_INT_STS);
+ if (res < 0)
+ goto out;
+
+ /*Dummy read -- mandatory for status register*/
+ res = tps65910_rtc_read_u8(&rd_reg, TPS65910_REG_RTC_STATUS);
+ mdelay(100);
+ res = tps65910_rtc_read_u8(&rd_reg, TPS65910_REG_RTC_STATUS);
+ res = tps65910_rtc_write_u8(rd_reg, TPS65910_REG_RTC_STATUS);
+
+ rtc_events |= RTC_IRQF | RTC_AF;
+ } else if (rd_reg & TPS65910_RTC_PERIOD_IT) {
+ res = tps65910_rtc_write_u8(rd_reg | TPS65910_RTC_PERIOD_IT,
+ TPS65910_REG_INT_STS);
+ if (res < 0)
+ goto out;
+
+ /*Dummy read -- mandatory for status register*/
+ res = tps65910_rtc_read_u8(&rd_reg, TPS65910_REG_RTC_STATUS);
+ mdelay(100);
+ res = tps65910_rtc_read_u8(&rd_reg, TPS65910_REG_RTC_STATUS);
+ rd_reg &= 0xC3;
+ res = tps65910_rtc_write_u8(rd_reg, TPS65910_REG_RTC_STATUS);
+ rtc_events |= RTC_IRQF | RTC_UF;
+ }
+out:
+ /* Notify RTC core on event */
+ events = rtc_events;
+ rtc_update_irq(global_rtc, 1, events);
+}
+
+static struct rtc_class_ops tps65910_rtc_ops = {
+ .read_time = tps65910_rtc_read_time,
+ .set_time = tps65910_rtc_set_time,
+ .read_alarm = tps65910_rtc_read_alarm,
+ .set_alarm = tps65910_rtc_set_alarm,
+ .alarm_irq_enable = tps65910_rtc_alarm_irq_enable,
+ .update_irq_enable = tps65910_rtc_update_irq_enable,
+ .irq_set_freq = tps65910_rtc_irq_set_freq,
+};
+
+static int __devinit tps65910_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ int ret = 0;
+ u8 rd_reg;
+ struct rtc_time tm_def = { // 2011.1.1 12:00 Saturday
+ .tm_wday = 6,
+ .tm_year = 111,
+ .tm_mon = 0,
+ .tm_mday = 1,
+ .tm_hour = 12,
+ .tm_min = 0,
+ .tm_sec = 0,
+ };
+
+ rtc = rtc_device_register(pdev->name,
+ &pdev->dev, &tps65910_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ dev_err(&pdev->dev, "can't register TPS65910 RTC device,\
+ err %ld\n", PTR_ERR(rtc));
+ goto out0;
+
+ }
+ printk(KERN_INFO "TPS65910 RTC device successfully registered\n");
+
+ platform_set_drvdata(pdev, rtc);
+
+ /* Take rtc out of reset */
+ tps65910_rtc_read_u8(&rd_reg, TPS65910_REG_DEVCTRL);
+ rd_reg &= ~BIT_RTC_PWDN;
+ ret = tps65910_rtc_write_u8(rd_reg, TPS65910_REG_DEVCTRL);
+
+ /* Dummy read to ensure that the register gets updated.
+ * Please refer tps65910 TRM table:25 for details
+ */
+ tps65910_rtc_read_u8(&rd_reg, TPS65910_REG_RTC_STATUS);
+
+ ret = tps65910_rtc_read_u8(&rd_reg, TPS65910_REG_RTC_STATUS);
+ if (ret < 0) {
+ printk(KERN_ERR "TPS65910 RTC STATUS REG READ FAILED\n");
+ goto out1;
+ }
+
+ if (rd_reg & BIT_RTC_STATUS_REG_POWER_UP_M) {
+ dev_warn(&pdev->dev, "Power up reset detected.\n");
+ // cwz:if rtc power up reset, set default time.
+ printk(KERN_INFO "TPS65910 RTC set to default time\n");
+ tps65910_rtc_set_time(rtc, &tm_def);
+ }
+
+ if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M)
+ dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n");
+
+ /* Clear RTC Power up reset and pending alarm interrupts */
+ ret = tps65910_rtc_write_u8(rd_reg, TPS65910_REG_RTC_STATUS);
+ if (ret < 0)
+ goto out1;
+ ret = tps65910_rtc_read_u8(&rd_reg, TPS65910_REG_INT_STS);
+ if (ret < 0) {
+ printk(KERN_ERR "TPS65910 RTC STATUS REG READ FAILED\n");
+ goto out1;
+ }
+
+ if (rd_reg & 0x40) {
+ printk(KERN_INFO "pending alarm interrupt!!! clearing!!!");
+ tps65910_rtc_write_u8(rd_reg, TPS65910_REG_INT_STS);
+ }
+
+ global_rtc = rtc;
+
+ /* Link RTC IRQ handler to TPS65910 Core */
+ tps65910_add_irq_work(TPS65910_RTC_ALARM_IRQ, rtc_work);
+ tps65910_add_irq_work(TPS65910_RTC_PERIOD_IRQ, rtc_work);
+
+ /* Check RTC module status, Enable if it is off */
+ ret = tps65910_rtc_read_u8(&rd_reg, TPS65910_REG_RTC_CTRL);
+ if (ret < 0)
+ goto out1;
+
+ if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) {
+ dev_info(&pdev->dev, "Enabling TPS65910-RTC.\n");
+ // cwz:if rtc stop, set default time, then enable rtc
+ printk(KERN_INFO "TPS65910 RTC set to default time\n");
+ tps65910_rtc_set_time(rtc, &tm_def);
+ rd_reg |= BIT_RTC_CTRL_REG_STOP_RTC_M;
+ ret = tps65910_rtc_write_u8(rd_reg, TPS65910_REG_RTC_CTRL);
+ if (ret < 0)
+ goto out1;
+ }
+
+ /* init cached IRQ enable bits */
+ ret = tps65910_rtc_read_u8(&rtc_irq_bits, TPS65910_REG_RTC_INTERRUPTS);
+ if (ret < 0)
+ goto out1;
+
+ tps65910_rtc_write_u8(0x3F, TPS65910_REG_INT_MSK);
+ return ret;
+
+out1:
+ rtc_device_unregister(rtc);
+out0:
+ return ret;
+}
+
+/*
+ * Disable all TPS65910 RTC module interrupts.
+ * Sets status flag to free.
+ */
+static int __devexit tps65910_rtc_remove(struct platform_device *pdev)
+{
+ /* leave rtc running, but disable irqs */
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+
+
+ free_irq(irq, rtc);
+
+ rtc_device_unregister(rtc);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static void tps65910_rtc_shutdown(struct platform_device *pdev)
+{
+ /* mask timer interrupts, but leave alarm interrupts on to enable
+ * power-on when alarm is triggered
+ */
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+}
+
+#ifdef CONFIG_PM
+
+static unsigned char irqstat;
+
+static
+int tps65910_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ irqstat = rtc_irq_bits;
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+ return 0;
+}
+
+static int tps65910_rtc_resume(struct platform_device *pdev)
+{
+ set_rtc_irq_bit(irqstat);
+ return 0;
+}
+
+#else
+#define tps65910_rtc_suspend NULL
+#define tps65910_rtc_resume NULL
+#endif
+
+
+static struct platform_driver tps65910rtc_driver = {
+ .probe = tps65910_rtc_probe,
+ .remove = __devexit_p(tps65910_rtc_remove),
+ .shutdown = tps65910_rtc_shutdown,
+ .suspend = tps65910_rtc_suspend,
+ .resume = tps65910_rtc_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tps65910_rtc",
+ },
+};
+static int __init tps65910_rtc_init(void)
+{
+ return platform_driver_register(&tps65910rtc_driver);
+}
+module_init(tps65910_rtc_init);
+
+static void __exit tps65910_rtc_exit(void)
+{
+ platform_driver_unregister(&tps65910rtc_driver);
+}
+module_exit(tps65910_rtc_exit);
+
+MODULE_ALIAS("platform:tps65910_rtc");
+MODULE_AUTHOR("cwz <cwz@rockchips.com");
+MODULE_LICENSE("GPL");
--- /dev/null
+/* linux/i2c/tps65910.h
+ *
+ * TPS65910 Power Management Device Definitions.
+ *
+ * Based on include/linux/i2c/twl.h
+ *
+ * Copyright (C) 2010 Mistral Solutions Pvt Ltd <www.mistralsolutions.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_I2C_TPS65910_H
+#define __LINUX_I2C_TPS65910_H
+
+#define TPS65910_NUM_SLAVES 1
+/* I2C Slave Address 7-bit */
+#define TPS65910_I2C_ID0 0x2D /* general-purpose */
+#define TPS65910_I2C_ID1 0x12 /* Smart Reflex */
+
+/* TPS65910 to host IRQ */
+#define TPS65910_HOST_IRQ RK29_PIN6_PD3
+
+/* TPS65910 MAX GPIOs */
+#define TPS65910_GPIO_MAX 1
+
+/*
+ * ----------------------------------------------------------------------------
+ * Registers, all 8 bits
+ * ----------------------------------------------------------------------------
+ */
+#define TPS65910_REG_SECONDS 0x00
+#define TPS65910_REG_MINUTES 0x01
+#define TPS65910_REG_HOURS 0x02
+#define TPS65910_REG_DAYS 0x03
+#define TPS65910_REG_MONTHS 0x04
+#define TPS65910_REG_YEARS 0x05
+#define TPS65910_REG_WEEKS 0x06
+#define TPS65910_REG_ALARM_SECONDS 0x08
+#define TPS65910_REG_ALARM_MINUTES 0x09
+#define TPS65910_REG_ALARM_HOURS 0x0A
+#define TPS65910_REG_ALARM_DAYS 0x0B
+#define TPS65910_REG_ALARM_MONTHS 0x0C
+#define TPS65910_REG_ALARM_YEARS 0x0D
+
+#define TPS65910_REG_RTC_CTRL 0x10
+#define TPS65910_REG_RTC_STATUS 0x11
+#define TPS65910_REG_RTC_INTERRUPTS 0x12
+#define TPS65910_REG_RTC_COMP_LSB 0x13
+#define TPS65910_REG_RTC_COMP_MSB 0x14
+#define TPS65910_REG_RTC_RES_PROG 0x15
+#define TPS65910_REG_RTC_RESET_STATUS 0x16
+#define TPS65910_REG_BCK1 0x17
+#define TPS65910_REG_BCK2 0x18
+#define TPS65910_REG_BCK3 0x19
+#define TPS65910_REG_BCK4 0x1A
+#define TPS65910_REG_BCK5 0x1B
+#define TPS65910_REG_PUADEN 0x1C
+#define TPS65910_REG_REF 0x1D
+#define TPS65910_REG_VRTC 0x1E
+
+#define TPS65910_REG_VIO 0x20
+#define TPS65910_REG_VDD1 0x21
+#define TPS65910_REG_VDD1_OP 0x22
+#define TPS65910_REG_VDD1_SR 0x23
+#define TPS65910_REG_VDD2 0x24
+#define TPS65910_REG_VDD2_OP 0x25
+#define TPS65910_REG_VDD2_SR 0x26
+#define TPS65910_REG_VDD3 0x27
+
+#define TPS65910_REG_VDIG1 0x30
+#define TPS65910_REG_VDIG2 0x31
+#define TPS65910_REG_VAUX1 0x32
+#define TPS65910_REG_VAUX2 0x33
+#define TPS65910_REG_VAUX33 0x34
+#define TPS65910_REG_VMMC 0x35
+#define TPS65910_REG_VPLL 0x36
+#define TPS65910_REG_VDAC 0x37
+#define TPS65910_REG_THERM 0x38
+#define TPS65910_REG_BBCH 0x39
+
+#define TPS65910_REG_DCDCCTRL 0x3E
+#define TPS65910_REG_DEVCTRL 0x3F
+#define TPS65910_REG_DEVCTRL2 0x40
+#define TPS65910_REG_SLEEP_KEEP_LDO_ON 0x41
+#define TPS65910_REG_SLEEP_KEEP_RES_ON 0x42
+#define TPS65910_REG_SLEEP_SET_LDO_OFF 0x43
+#define TPS65910_REG_SLEEP_SET_RES_OFF 0x44
+#define TPS65910_REG_EN1_LDO_ASS 0x45
+#define TPS65910_REG_EN1_SMPS_ASS 0x46
+#define TPS65910_REG_EN2_LDO_ASS 0x47
+#define TPS65910_REG_EN2_SMPS_ASS 0x48
+#define TPS65910_REG_EN3_LDO_ASS 0x49
+#define TPS65910_REG_SPARE 0x4A
+
+#define TPS65910_REG_INT_STS 0x50
+#define TPS65910_REG_INT_MSK 0x51
+#define TPS65910_REG_INT_STS2 0x52
+#define TPS65910_REG_INT_MSK2 0x53
+#define TPS65910_REG_INT_STS3 0x54
+#define TPS65910_REG_INT_MSK3 0x55
+
+#define TPS65910_REG_GPIO0 0x60
+
+#define TPS65910_REG_JTAGVERNUM 0x80
+
+/* TPS65910 GPIO Specific flags */
+#define TPS65910_GPIO_INT_FALLING 0
+#define TPS65910_GPIO_INT_RISING 1
+
+#define TPS65910_DEBOUNCE_91_5_MS 0
+#define TPS65910_DEBOUNCE_150_MS 1
+
+#define TPS65910_GPIO_PUDIS (1 << 3)
+#define TPS65910_GPIO_CFG_OUTPUT (1 << 2)
+
+
+
+/* TPS65910 Interrupt events */
+
+/* RTC Driver */
+#define TPS65910_RTC_ALARM_IT 0x80
+#define TPS65910_RTC_PERIOD_IT 0x40
+
+/*Core Driver */
+#define TPS65910_HOT_DIE_IT 0x20
+#define TPS65910_PWRHOLD_IT 0x10
+#define TPS65910_PWRON_LP_IT 0x08
+#define TPS65910_PWRON_IT 0x04
+#define TPS65910_VMBHI_IT 0x02
+#define TPS65910_VMBGCH_IT 0x01
+
+/* GPIO driver */
+#define TPS65910_GPIO_F_IT 0x02
+#define TPS65910_GPIO_R_IT 0x01
+
+
+#define TPS65910_VRTC_OFFMASK (1<<3)
+
+/* Back-up battery charger control */
+#define TPS65910_BBCHEN 0x01
+
+/* Back-up battery charger voltage */
+#define TPS65910_BBSEL_3P0 0x00
+#define TPS65910_BBSEL_2P52 0x02
+#define TPS65910_BBSEL_3P15 0x04
+#define TPS65910_BBSEL_VBAT 0x06
+
+/* DEVCTRL_REG flags */
+#define TPS65910_RTC_PWDNN 0x40
+#define TPS65910_CK32K_CTRL 0x20
+#define TPS65910_SR_CTL_I2C_SEL 0x10
+#define TPS65910_DEV_OFF_RST 0x08
+#define TPS65910_DEV_ON 0x04
+#define TPS65910_DEV_SLP 0x02
+#define TPS65910_DEV_OFF 0x01
+
+/* DEVCTRL2_REG flags */
+#define TPS65910_DEV2_TSLOT_LENGTH 0x30
+#define TPS65910_DEV2_SLEEPSIG_POL 0x08
+#define TPS65910_DEV2_PWON_LP_OFF 0x04
+#define TPS65910_DEV2_PWON_LP_RST 0x02
+#define TPS65910_DEV2_IT_POL 0x01
+
+/* TPS65910 SMPS/LDO's */
+#define TPS65910_VIO 0
+#define TPS65910_VDD1 1
+#define TPS65910_VDD2 2
+#define TPS65910_VDD3 3
+/* LDOs */
+#define TPS65910_VDIG1 4
+#define TPS65910_VDIG2 5
+#define TPS65910_VAUX33 6
+#define TPS65910_VMMC 7
+#define TPS65910_VAUX1 8
+#define TPS65910_VAUX2 9
+#define TPS65910_VDAC 10
+#define TPS65910_VPLL 11
+/* Internal LDO */
+#define TPS65910_VRTC 12
+
+/* Number of step-down/up converters available */
+#define TPS65910_NUM_DCDC 4
+
+/* Number of LDO voltage regulators available */
+#define TPS65910_NUM_LDO 9
+
+/* Number of total regulators available */
+#define TPS65910_NUM_REGULATOR (TPS65910_NUM_DCDC + TPS65910_NUM_LDO)
+
+
+/* Regulator Supply state */
+#define SUPPLY_STATE_FLAG 0x03
+/* OFF States */
+#define TPS65910_REG_OFF_00 0x00
+#define TPS65910_REG_OFF_10 0x02
+/* OHP - on High Power */
+#define TPS65910_REG_OHP 0x01
+/* OLP - on Low Power */
+#define TPS65910_REG_OLP 0x03
+
+#define TPS65910_MAX_IRQS 10
+#define TPS65910_VMBDCH_IRQ 0
+#define TPS65910_VMBHI_IRQ 1
+#define TPS65910_PWRON_IRQ 2
+#define TPS65910_PWRON_LP_IRQ 3
+#define TPS65910_PWRHOLD_IRQ 4
+#define TPS65910_HOTDIE_IRQ 5
+#define TPS65910_RTC_ALARM_IRQ 6
+#define TPS65910_RTC_PERIOD_IRQ 7
+#define TPS65910_GPIO0_R_IRQ 8
+#define TPS65910_GPIO0_F_IRQ 9
+
+/* TPS65910 has 1 GPIO */
+struct tps65910_gpio {
+ u8 debounce;
+ u8 pullup_pulldown;
+ u8 gpio_config; /* Input or output */
+ u8 gpio_val; /* Output value */
+ int (*gpio_setup)(struct tps65910_gpio *pdata);
+ int (*gpio_taredown)(struct tps65910_gpio *pdata);
+};
+
+struct tps65910_platform_data {
+
+ unsigned irq_num; /* TPS65910 to Host IRQ Number */
+ struct tps65910_gpio *gpio;
+
+ /* plaform specific data to be initialised in board file */
+ struct regulator_init_data *vio;
+ struct regulator_init_data *vdd1;
+ struct regulator_init_data *vdd2;
+ struct regulator_init_data *vdd3;
+ struct regulator_init_data *vdig1;
+ struct regulator_init_data *vdig2;
+ struct regulator_init_data *vaux33;
+ struct regulator_init_data *vmmc;
+ struct regulator_init_data *vaux1;
+ struct regulator_init_data *vaux2;
+ struct regulator_init_data *vdac;
+ struct regulator_init_data *vpll;
+
+ void (*handlers[TPS65910_MAX_IRQS]) (void *data);
+ /* Configure TP65910 to board specific usage*/
+ int (*board_tps65910_config)(struct tps65910_platform_data *pdata);
+};
+
+int tps65910_enable_bbch(u8 voltage);
+int tps65910_disable_bbch(void);
+
+int tps65910_remove_irq_work(int irq);
+int tps65910_add_irq_work(int irq, void (*handler)(void *data));
+
+int tps65910_i2c_write_u8(u8 slave_addr, u8 val, u8 reg);
+int tps65910_i2c_read_u8(u8 slave_addr, u8 *val, u8 reg);
+
+#endif /* __LINUX_I2C_TPS65910_H */
+