Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6

* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (46 commits)
  mfd: Fix mismatch in twl4030 mutex lock-unlock
  mfd: twl6030-pwm.c needs MODULE_LICENSE
  mfd: Fix the omap-usb-host clock API usage on usbhs_disable()
  mfd: Acknowledge WM8994 IRQs before reporting
  mfd: Acknowlege all WM831x IRQs before we handle them
  mfd: Avoid two assignments if failures happen in tps65910_i2c_probe
  regulator: Storing tps65912 error codes in u8
  mfd: Don't leak init_data in tps65910_i2c_probe
  regulator: aat2870: Add AAT2870 regulator driver
  backlight: Add AAT2870 backlight driver
  mfd: Add AAT2870 mfd driver
  mfd: Remove dead code from max8997-irq
  mfd: Move TPS55910 Kconfig option
  mfd: Fix missing stmpe kerneldoc
  mfd: Fix off-by-one value range checking for tps65912_i2c_write
  mfd: Add devices for WM831x clocking module
  mfd: Remove comp{1,2}_threshold sysfs entries in tps65911_comparator_remove
  mfd: Don't ask about the TPS65912 core driver in Kconfig
  mfd: Fix off by one in WM831x IRQ code
  mfd: Add tps65921 support from twl-core
  ...
This commit is contained in:
Linus Torvalds 2011-07-31 14:31:44 -10:00
commit 3da3f872aa
44 changed files with 4127 additions and 461 deletions

View File

@ -280,6 +280,12 @@ config GPIO_TC3589X
This enables support for the GPIOs found on the TC3589X
I/O Expander.
config GPIO_TPS65912
tristate "TI TPS65912 GPIO"
depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
help
This driver supports TPS65912 gpio chip
config GPIO_TWL4030
tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
depends on TWL4030_CORE

View File

@ -48,6 +48,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_MACH_U300) += gpio-u300.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o

View File

@ -0,0 +1,156 @@
/*
* Copyright 2011 Texas Instruments Inc.
*
* Author: Margarita Olaya <magi@slimlogic.co.uk>
*
* 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 driver is based on wm8350 implementation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/mfd/tps65912.h>
struct tps65912_gpio_data {
struct tps65912 *tps65912;
struct gpio_chip gpio_chip;
};
static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset)
{
struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
int val;
val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset);
if (val & GPIO_STS_MASK)
return 1;
return 0;
}
static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset,
int value)
{
struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
if (value)
tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
GPIO_SET_MASK);
else
tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
GPIO_SET_MASK);
}
static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset,
int value)
{
struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
/* Set the initial value */
tps65912_gpio_set(gc, offset, value);
return tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
GPIO_CFG_MASK);
}
static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset)
{
struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
GPIO_CFG_MASK);
}
static struct gpio_chip template_chip = {
.label = "tps65912",
.owner = THIS_MODULE,
.direction_input = tps65912_gpio_input,
.direction_output = tps65912_gpio_output,
.get = tps65912_gpio_get,
.set = tps65912_gpio_set,
.can_sleep = 1,
.ngpio = 5,
.base = -1,
};
static int __devinit tps65912_gpio_probe(struct platform_device *pdev)
{
struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
struct tps65912_board *pdata = tps65912->dev->platform_data;
struct tps65912_gpio_data *tps65912_gpio;
int ret;
tps65912_gpio = kzalloc(sizeof(*tps65912_gpio), GFP_KERNEL);
if (tps65912_gpio == NULL)
return -ENOMEM;
tps65912_gpio->tps65912 = tps65912;
tps65912_gpio->gpio_chip = template_chip;
tps65912_gpio->gpio_chip.dev = &pdev->dev;
if (pdata && pdata->gpio_base)
tps65912_gpio->gpio_chip.base = pdata->gpio_base;
ret = gpiochip_add(&tps65912_gpio->gpio_chip);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
goto err;
}
platform_set_drvdata(pdev, tps65912_gpio);
return ret;
err:
kfree(tps65912_gpio);
return ret;
}
static int __devexit tps65912_gpio_remove(struct platform_device *pdev)
{
struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev);
int ret;
ret = gpiochip_remove(&tps65912_gpio->gpio_chip);
if (ret == 0)
kfree(tps65912_gpio);
return ret;
}
static struct platform_driver tps65912_gpio_driver = {
.driver = {
.name = "tps65912-gpio",
.owner = THIS_MODULE,
},
.probe = tps65912_gpio_probe,
.remove = __devexit_p(tps65912_gpio_remove),
};
static int __init tps65912_gpio_init(void)
{
return platform_driver_register(&tps65912_gpio_driver);
}
subsys_initcall(tps65912_gpio_init);
static void __exit tps65912_gpio_exit(void)
{
platform_driver_unregister(&tps65912_gpio_driver);
}
module_exit(tps65912_gpio_exit);
MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>");
MODULE_DESCRIPTION("GPIO interface for TPS65912 PMICs");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:tps65912-gpio");

View File

@ -171,6 +171,37 @@ config MFD_TPS6586X
This driver can also be built as a module. If so, the module
will be called tps6586x.
config MFD_TPS65910
bool "TPS65910 Power Management chip"
depends on I2C=y && GPIOLIB
select MFD_CORE
select GPIO_TPS65910
help
if you say yes here you get support for the TPS65910 series of
Power Management chips.
config MFD_TPS65912
bool
depends on GPIOLIB
config MFD_TPS65912_I2C
bool "TPS95612 Power Management chip with I2C"
select MFD_CORE
select MFD_TPS65912
depends on I2C=y && GPIOLIB
help
If you say yes here you get support for the TPS65912 series of
PM chips with I2C interface.
config MFD_TPS65912_SPI
bool "TPS65912 Power Management chip with SPI"
select MFD_CORE
select MFD_TPS65912
depends on SPI_MASTER && GPIOLIB
help
If you say yes here you get support for the TPS65912 series of
PM chips with SPI interface.
config MENELAUS
bool "Texas Instruments TWL92330/Menelaus PM chip"
depends on I2C=y && ARCH_OMAP2
@ -662,8 +693,9 @@ config MFD_JANZ_CMODIO
CAN and GPIO controllers.
config MFD_JZ4740_ADC
tristate "Support for the JZ4740 SoC ADC core"
bool "Support for the JZ4740 SoC ADC core"
select MFD_CORE
select GENERIC_IRQ_CHIP
depends on MACH_JZ4740
help
Say yes here if you want support for the ADC unit in the JZ4740 SoC.
@ -725,18 +757,19 @@ config MFD_PM8XXX_IRQ
This is required to use certain other PM 8xxx features, such as GPIO
and MPP.
config MFD_TPS65910
bool "TPS65910 Power Management chip"
depends on I2C=y && GPIOLIB
select MFD_CORE
select GPIO_TPS65910
help
if you say yes here you get support for the TPS65910 series of
Power Management chips.
config TPS65911_COMPARATOR
tristate
config MFD_AAT2870_CORE
bool "Support for the AnalogicTech AAT2870"
select MFD_CORE
depends on I2C=y && GPIOLIB
help
If you say yes here you get support for the AAT2870.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"

View File

@ -23,6 +23,7 @@ obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
wm831x-objs += wm831x-auxadc.o
obj-$(CONFIG_MFD_WM831X) += wm831x.o
obj-$(CONFIG_MFD_WM831X_I2C) += wm831x-i2c.o
obj-$(CONFIG_MFD_WM831X_SPI) += wm831x-spi.o
@ -35,6 +36,11 @@ obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o
obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
tps65912-objs := tps65912-core.o tps65912-irq.o
obj-$(CONFIG_MFD_TPS65912) += tps65912.o
obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
@ -94,5 +100,5 @@ obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o

535
drivers/mfd/aat2870-core.c Normal file
View File

@ -0,0 +1,535 @@
/*
* linux/drivers/mfd/aat2870-core.c
*
* Copyright (c) 2011, NVIDIA Corporation.
* Author: Jin Park <jinyoungp@nvidia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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 St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
#include <linux/mfd/aat2870.h>
#include <linux/regulator/machine.h>
static struct aat2870_register aat2870_regs[AAT2870_REG_NUM] = {
/* readable, writeable, value */
{ 0, 1, 0x00 }, /* 0x00 AAT2870_BL_CH_EN */
{ 0, 1, 0x16 }, /* 0x01 AAT2870_BLM */
{ 0, 1, 0x16 }, /* 0x02 AAT2870_BLS */
{ 0, 1, 0x56 }, /* 0x03 AAT2870_BL1 */
{ 0, 1, 0x56 }, /* 0x04 AAT2870_BL2 */
{ 0, 1, 0x56 }, /* 0x05 AAT2870_BL3 */
{ 0, 1, 0x56 }, /* 0x06 AAT2870_BL4 */
{ 0, 1, 0x56 }, /* 0x07 AAT2870_BL5 */
{ 0, 1, 0x56 }, /* 0x08 AAT2870_BL6 */
{ 0, 1, 0x56 }, /* 0x09 AAT2870_BL7 */
{ 0, 1, 0x56 }, /* 0x0A AAT2870_BL8 */
{ 0, 1, 0x00 }, /* 0x0B AAT2870_FLR */
{ 0, 1, 0x03 }, /* 0x0C AAT2870_FM */
{ 0, 1, 0x03 }, /* 0x0D AAT2870_FS */
{ 0, 1, 0x10 }, /* 0x0E AAT2870_ALS_CFG0 */
{ 0, 1, 0x06 }, /* 0x0F AAT2870_ALS_CFG1 */
{ 0, 1, 0x00 }, /* 0x10 AAT2870_ALS_CFG2 */
{ 1, 0, 0x00 }, /* 0x11 AAT2870_AMB */
{ 0, 1, 0x00 }, /* 0x12 AAT2870_ALS0 */
{ 0, 1, 0x00 }, /* 0x13 AAT2870_ALS1 */
{ 0, 1, 0x00 }, /* 0x14 AAT2870_ALS2 */
{ 0, 1, 0x00 }, /* 0x15 AAT2870_ALS3 */
{ 0, 1, 0x00 }, /* 0x16 AAT2870_ALS4 */
{ 0, 1, 0x00 }, /* 0x17 AAT2870_ALS5 */
{ 0, 1, 0x00 }, /* 0x18 AAT2870_ALS6 */
{ 0, 1, 0x00 }, /* 0x19 AAT2870_ALS7 */
{ 0, 1, 0x00 }, /* 0x1A AAT2870_ALS8 */
{ 0, 1, 0x00 }, /* 0x1B AAT2870_ALS9 */
{ 0, 1, 0x00 }, /* 0x1C AAT2870_ALSA */
{ 0, 1, 0x00 }, /* 0x1D AAT2870_ALSB */
{ 0, 1, 0x00 }, /* 0x1E AAT2870_ALSC */
{ 0, 1, 0x00 }, /* 0x1F AAT2870_ALSD */
{ 0, 1, 0x00 }, /* 0x20 AAT2870_ALSE */
{ 0, 1, 0x00 }, /* 0x21 AAT2870_ALSF */
{ 0, 1, 0x00 }, /* 0x22 AAT2870_SUB_SET */
{ 0, 1, 0x00 }, /* 0x23 AAT2870_SUB_CTRL */
{ 0, 1, 0x00 }, /* 0x24 AAT2870_LDO_AB */
{ 0, 1, 0x00 }, /* 0x25 AAT2870_LDO_CD */
{ 0, 1, 0x00 }, /* 0x26 AAT2870_LDO_EN */
};
static struct mfd_cell aat2870_devs[] = {
{
.name = "aat2870-backlight",
.id = AAT2870_ID_BL,
.pdata_size = sizeof(struct aat2870_bl_platform_data),
},
{
.name = "aat2870-regulator",
.id = AAT2870_ID_LDOA,
.pdata_size = sizeof(struct regulator_init_data),
},
{
.name = "aat2870-regulator",
.id = AAT2870_ID_LDOB,
.pdata_size = sizeof(struct regulator_init_data),
},
{
.name = "aat2870-regulator",
.id = AAT2870_ID_LDOC,
.pdata_size = sizeof(struct regulator_init_data),
},
{
.name = "aat2870-regulator",
.id = AAT2870_ID_LDOD,
.pdata_size = sizeof(struct regulator_init_data),
},
};
static int __aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
{
int ret;
if (addr >= AAT2870_REG_NUM) {
dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);
return -EINVAL;
}
if (!aat2870->reg_cache[addr].readable) {
*val = aat2870->reg_cache[addr].value;
goto out;
}
ret = i2c_master_send(aat2870->client, &addr, 1);
if (ret < 0)
return ret;
if (ret != 1)
return -EIO;
ret = i2c_master_recv(aat2870->client, val, 1);
if (ret < 0)
return ret;
if (ret != 1)
return -EIO;
out:
dev_dbg(aat2870->dev, "read: addr=0x%02x, val=0x%02x\n", addr, *val);
return 0;
}
static int __aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
{
u8 msg[2];
int ret;
if (addr >= AAT2870_REG_NUM) {
dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);
return -EINVAL;
}
if (!aat2870->reg_cache[addr].writeable) {
dev_err(aat2870->dev, "Address 0x%02x is not writeable\n",
addr);
return -EINVAL;
}
msg[0] = addr;
msg[1] = val;
ret = i2c_master_send(aat2870->client, msg, 2);
if (ret < 0)
return ret;
if (ret != 2)
return -EIO;
aat2870->reg_cache[addr].value = val;
dev_dbg(aat2870->dev, "write: addr=0x%02x, val=0x%02x\n", addr, val);
return 0;
}
static int aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
{
int ret;
mutex_lock(&aat2870->io_lock);
ret = __aat2870_read(aat2870, addr, val);
mutex_unlock(&aat2870->io_lock);
return ret;
}
static int aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
{
int ret;
mutex_lock(&aat2870->io_lock);
ret = __aat2870_write(aat2870, addr, val);
mutex_unlock(&aat2870->io_lock);
return ret;
}
static int aat2870_update(struct aat2870_data *aat2870, u8 addr, u8 mask,
u8 val)
{
int change;
u8 old_val, new_val;
int ret;
mutex_lock(&aat2870->io_lock);
ret = __aat2870_read(aat2870, addr, &old_val);
if (ret)
goto out_unlock;
new_val = (old_val & ~mask) | (val & mask);
change = old_val != new_val;
if (change)
ret = __aat2870_write(aat2870, addr, new_val);
out_unlock:
mutex_unlock(&aat2870->io_lock);
return ret;
}
static inline void aat2870_enable(struct aat2870_data *aat2870)
{
if (aat2870->en_pin >= 0)
gpio_set_value(aat2870->en_pin, 1);
aat2870->is_enable = 1;
}
static inline void aat2870_disable(struct aat2870_data *aat2870)
{
if (aat2870->en_pin >= 0)
gpio_set_value(aat2870->en_pin, 0);
aat2870->is_enable = 0;
}
#ifdef CONFIG_DEBUG_FS
static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf)
{
u8 addr, val;
ssize_t count = 0;
int ret;
count += sprintf(buf, "aat2870 registers\n");
for (addr = 0; addr < AAT2870_REG_NUM; addr++) {
count += sprintf(buf + count, "0x%02x: ", addr);
if (count >= PAGE_SIZE - 1)
break;
ret = aat2870->read(aat2870, addr, &val);
if (ret == 0)
count += snprintf(buf + count, PAGE_SIZE - count,
"0x%02x", val);
else
count += snprintf(buf + count, PAGE_SIZE - count,
"<read fail: %d>", ret);
if (count >= PAGE_SIZE - 1)
break;
count += snprintf(buf + count, PAGE_SIZE - count, "\n");
if (count >= PAGE_SIZE - 1)
break;
}
/* Truncate count; min() would cause a warning */
if (count >= PAGE_SIZE)
count = PAGE_SIZE - 1;
return count;
}
static int aat2870_reg_open_file(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static ssize_t aat2870_reg_read_file(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct aat2870_data *aat2870 = file->private_data;
char *buf;
ssize_t ret;
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = aat2870_dump_reg(aat2870, buf);
if (ret >= 0)
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
kfree(buf);
return ret;
}
static ssize_t aat2870_reg_write_file(struct file *file,
const char __user *user_buf, size_t count,
loff_t *ppos)
{
struct aat2870_data *aat2870 = file->private_data;
char buf[32];
int buf_size;
char *start = buf;
unsigned long addr, val;
int ret;
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size)) {
dev_err(aat2870->dev, "Failed to copy from user\n");
return -EFAULT;
}
buf[buf_size] = 0;
while (*start == ' ')
start++;
addr = simple_strtoul(start, &start, 16);
if (addr >= AAT2870_REG_NUM) {
dev_err(aat2870->dev, "Invalid address, 0x%lx\n", addr);
return -EINVAL;
}
while (*start == ' ')
start++;
if (strict_strtoul(start, 16, &val))
return -EINVAL;
ret = aat2870->write(aat2870, (u8)addr, (u8)val);
if (ret)
return ret;
return buf_size;
}
static const struct file_operations aat2870_reg_fops = {
.open = aat2870_reg_open_file,
.read = aat2870_reg_read_file,
.write = aat2870_reg_write_file,
};
static void aat2870_init_debugfs(struct aat2870_data *aat2870)
{
aat2870->dentry_root = debugfs_create_dir("aat2870", NULL);
if (!aat2870->dentry_root) {
dev_warn(aat2870->dev,
"Failed to create debugfs root directory\n");
return;
}
aat2870->dentry_reg = debugfs_create_file("regs", 0644,
aat2870->dentry_root,
aat2870, &aat2870_reg_fops);
if (!aat2870->dentry_reg)
dev_warn(aat2870->dev,
"Failed to create debugfs register file\n");
}
static void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
{
debugfs_remove_recursive(aat2870->dentry_root);
}
#else
static inline void aat2870_init_debugfs(struct aat2870_data *aat2870)
{
}
static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
{
}
#endif /* CONFIG_DEBUG_FS */
static int aat2870_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct aat2870_platform_data *pdata = client->dev.platform_data;
struct aat2870_data *aat2870;
int i, j;
int ret = 0;
aat2870 = kzalloc(sizeof(struct aat2870_data), GFP_KERNEL);
if (!aat2870) {
dev_err(&client->dev,
"Failed to allocate memory for aat2870\n");
ret = -ENOMEM;
goto out;
}
aat2870->dev = &client->dev;
dev_set_drvdata(aat2870->dev, aat2870);
aat2870->client = client;
i2c_set_clientdata(client, aat2870);
aat2870->reg_cache = aat2870_regs;
if (pdata->en_pin < 0)
aat2870->en_pin = -1;
else
aat2870->en_pin = pdata->en_pin;
aat2870->init = pdata->init;
aat2870->uninit = pdata->uninit;
aat2870->read = aat2870_read;
aat2870->write = aat2870_write;
aat2870->update = aat2870_update;
mutex_init(&aat2870->io_lock);
if (aat2870->init)
aat2870->init(aat2870);
if (aat2870->en_pin >= 0) {
ret = gpio_request(aat2870->en_pin, "aat2870-en");
if (ret < 0) {
dev_err(&client->dev,
"Failed to request GPIO %d\n", aat2870->en_pin);
goto out_kfree;
}
gpio_direction_output(aat2870->en_pin, 1);
}
aat2870_enable(aat2870);
for (i = 0; i < pdata->num_subdevs; i++) {
for (j = 0; j < ARRAY_SIZE(aat2870_devs); j++) {
if ((pdata->subdevs[i].id == aat2870_devs[j].id) &&
!strcmp(pdata->subdevs[i].name,
aat2870_devs[j].name)) {
aat2870_devs[j].platform_data =
pdata->subdevs[i].platform_data;
break;
}
}
}
ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs,
ARRAY_SIZE(aat2870_devs), NULL, 0);
if (ret != 0) {
dev_err(aat2870->dev, "Failed to add subdev: %d\n", ret);
goto out_disable;
}
aat2870_init_debugfs(aat2870);
return 0;
out_disable:
aat2870_disable(aat2870);
if (aat2870->en_pin >= 0)
gpio_free(aat2870->en_pin);
out_kfree:
kfree(aat2870);
out:
return ret;
}
static int aat2870_i2c_remove(struct i2c_client *client)
{
struct aat2870_data *aat2870 = i2c_get_clientdata(client);
aat2870_uninit_debugfs(aat2870);
mfd_remove_devices(aat2870->dev);
aat2870_disable(aat2870);
if (aat2870->en_pin >= 0)
gpio_free(aat2870->en_pin);
if (aat2870->uninit)
aat2870->uninit(aat2870);
kfree(aat2870);
return 0;
}
#ifdef CONFIG_PM
static int aat2870_i2c_suspend(struct i2c_client *client, pm_message_t state)
{
struct aat2870_data *aat2870 = i2c_get_clientdata(client);
aat2870_disable(aat2870);
return 0;
}
static int aat2870_i2c_resume(struct i2c_client *client)
{
struct aat2870_data *aat2870 = i2c_get_clientdata(client);
struct aat2870_register *reg = NULL;
int i;
aat2870_enable(aat2870);
/* restore registers */
for (i = 0; i < AAT2870_REG_NUM; i++) {
reg = &aat2870->reg_cache[i];
if (reg->writeable)
aat2870->write(aat2870, i, reg->value);
}
return 0;
}
#else
#define aat2870_i2c_suspend NULL
#define aat2870_i2c_resume NULL
#endif /* CONFIG_PM */
static struct i2c_device_id aat2870_i2c_id_table[] = {
{ "aat2870", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aat2870_i2c_id_table);
static struct i2c_driver aat2870_i2c_driver = {
.driver = {
.name = "aat2870",
.owner = THIS_MODULE,
},
.probe = aat2870_i2c_probe,
.remove = aat2870_i2c_remove,
.suspend = aat2870_i2c_suspend,
.resume = aat2870_i2c_resume,
.id_table = aat2870_i2c_id_table,
};
static int __init aat2870_init(void)
{
return i2c_add_driver(&aat2870_i2c_driver);
}
subsys_initcall(aat2870_init);
static void __exit aat2870_exit(void)
{
i2c_del_driver(&aat2870_i2c_driver);
}
module_exit(aat2870_exit);
MODULE_DESCRIPTION("Core support for the AnalogicTech AAT2870");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");

View File

@ -879,20 +879,13 @@ static ssize_t ab3550_bank_write(struct file *file,
size_t count, loff_t *ppos)
{
struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_bank;
int err;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_bank);
err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
if (err)
return -EINVAL;
return err;
if (user_bank >= AB3550_NUM_BANKS) {
dev_err(&ab->i2c_client[0]->dev,
@ -902,7 +895,7 @@ static ssize_t ab3550_bank_write(struct file *file,
ab->debug_bank = user_bank;
return buf_size;
return count;
}
static int ab3550_address_print(struct seq_file *s, void *p)
@ -923,27 +916,21 @@ static ssize_t ab3550_address_write(struct file *file,
size_t count, loff_t *ppos)
{
struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_address;
int err;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_address);
err = kstrtoul_from_user(user_buf, count, 0, &user_address);
if (err)
return -EINVAL;
return err;
if (user_address > 0xff) {
dev_err(&ab->i2c_client[0]->dev,
"debugfs error input > 0xff\n");
return -EINVAL;
}
ab->debug_address = user_address;
return buf_size;
return count;
}
static int ab3550_val_print(struct seq_file *s, void *p)
@ -971,21 +958,15 @@ static ssize_t ab3550_val_write(struct file *file,
size_t count, loff_t *ppos)
{
struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_val;
int err;
u8 regvalue;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_val);
err = kstrtoul_from_user(user_buf, count, 0, &user_val);
if (err)
return -EINVAL;
return err;
if (user_val > 0xff) {
dev_err(&ab->i2c_client[0]->dev,
"debugfs error input > 0xff\n");
@ -1002,7 +983,7 @@ static ssize_t ab3550_val_write(struct file *file,
if (err)
return -EINVAL;
return buf_size;
return count;
}
static const struct file_operations ab3550_bank_fops = {

View File

@ -363,7 +363,7 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
}
}
static struct resource ab8500_gpio_resources[] = {
static struct resource __devinitdata ab8500_gpio_resources[] = {
{
.name = "GPIO_INT6",
.start = AB8500_INT_GPIO6R,
@ -372,7 +372,7 @@ static struct resource ab8500_gpio_resources[] = {
}
};
static struct resource ab8500_gpadc_resources[] = {
static struct resource __devinitdata ab8500_gpadc_resources[] = {
{
.name = "HW_CONV_END",
.start = AB8500_INT_GP_HW_ADC_CONV_END,
@ -387,7 +387,7 @@ static struct resource ab8500_gpadc_resources[] = {
},
};
static struct resource ab8500_rtc_resources[] = {
static struct resource __devinitdata ab8500_rtc_resources[] = {
{
.name = "60S",
.start = AB8500_INT_RTC_60S,
@ -402,7 +402,7 @@ static struct resource ab8500_rtc_resources[] = {
},
};
static struct resource ab8500_poweronkey_db_resources[] = {
static struct resource __devinitdata ab8500_poweronkey_db_resources[] = {
{
.name = "ONKEY_DBF",
.start = AB8500_INT_PON_KEY1DB_F,
@ -417,19 +417,46 @@ static struct resource ab8500_poweronkey_db_resources[] = {
},
};
static struct resource ab8500_bm_resources[] = {
static struct resource __devinitdata ab8500_av_acc_detect_resources[] = {
{
.name = "MAIN_EXT_CH_NOT_OK",
.start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
.end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
.flags = IORESOURCE_IRQ,
.name = "ACC_DETECT_1DB_F",
.start = AB8500_INT_ACC_DETECT_1DB_F,
.end = AB8500_INT_ACC_DETECT_1DB_F,
.flags = IORESOURCE_IRQ,
},
{
.name = "BATT_OVV",
.start = AB8500_INT_BATT_OVV,
.end = AB8500_INT_BATT_OVV,
.flags = IORESOURCE_IRQ,
.name = "ACC_DETECT_1DB_R",
.start = AB8500_INT_ACC_DETECT_1DB_R,
.end = AB8500_INT_ACC_DETECT_1DB_R,
.flags = IORESOURCE_IRQ,
},
{
.name = "ACC_DETECT_21DB_F",
.start = AB8500_INT_ACC_DETECT_21DB_F,
.end = AB8500_INT_ACC_DETECT_21DB_F,
.flags = IORESOURCE_IRQ,
},
{
.name = "ACC_DETECT_21DB_R",
.start = AB8500_INT_ACC_DETECT_21DB_R,
.end = AB8500_INT_ACC_DETECT_21DB_R,
.flags = IORESOURCE_IRQ,
},
{
.name = "ACC_DETECT_22DB_F",
.start = AB8500_INT_ACC_DETECT_22DB_F,
.end = AB8500_INT_ACC_DETECT_22DB_F,
.flags = IORESOURCE_IRQ,
},
{
.name = "ACC_DETECT_22DB_R",
.start = AB8500_INT_ACC_DETECT_22DB_R,
.end = AB8500_INT_ACC_DETECT_22DB_R,
.flags = IORESOURCE_IRQ,
},
};
static struct resource __devinitdata ab8500_charger_resources[] = {
{
.name = "MAIN_CH_UNPLUG_DET",
.start = AB8500_INT_MAIN_CH_UNPLUG_DET,
@ -442,12 +469,6 @@ static struct resource ab8500_bm_resources[] = {
.end = AB8500_INT_MAIN_CH_PLUG_DET,
.flags = IORESOURCE_IRQ,
},
{
.name = "VBUS_DET_F",
.start = AB8500_INT_VBUS_DET_F,
.end = AB8500_INT_VBUS_DET_F,
.flags = IORESOURCE_IRQ,
},
{
.name = "VBUS_DET_R",
.start = AB8500_INT_VBUS_DET_R,
@ -455,15 +476,21 @@ static struct resource ab8500_bm_resources[] = {
.flags = IORESOURCE_IRQ,
},
{
.name = "BAT_CTRL_INDB",
.start = AB8500_INT_BAT_CTRL_INDB,
.end = AB8500_INT_BAT_CTRL_INDB,
.name = "VBUS_DET_F",
.start = AB8500_INT_VBUS_DET_F,
.end = AB8500_INT_VBUS_DET_F,
.flags = IORESOURCE_IRQ,
},
{
.name = "CH_WD_EXP",
.start = AB8500_INT_CH_WD_EXP,
.end = AB8500_INT_CH_WD_EXP,
.name = "USB_LINK_STATUS",
.start = AB8500_INT_USB_LINK_STATUS,
.end = AB8500_INT_USB_LINK_STATUS,
.flags = IORESOURCE_IRQ,
},
{
.name = "USB_CHARGE_DET_DONE",
.start = AB8500_INT_USB_CHG_DET_DONE,
.end = AB8500_INT_USB_CHG_DET_DONE,
.flags = IORESOURCE_IRQ,
},
{
@ -473,21 +500,60 @@ static struct resource ab8500_bm_resources[] = {
.flags = IORESOURCE_IRQ,
},
{
.name = "NCONV_ACCU",
.start = AB8500_INT_CCN_CONV_ACC,
.end = AB8500_INT_CCN_CONV_ACC,
.name = "USB_CH_TH_PROT_R",
.start = AB8500_INT_USB_CH_TH_PROT_R,
.end = AB8500_INT_USB_CH_TH_PROT_R,
.flags = IORESOURCE_IRQ,
},
{
.name = "LOW_BAT_F",
.start = AB8500_INT_LOW_BAT_F,
.end = AB8500_INT_LOW_BAT_F,
.name = "USB_CH_TH_PROT_F",
.start = AB8500_INT_USB_CH_TH_PROT_F,
.end = AB8500_INT_USB_CH_TH_PROT_F,
.flags = IORESOURCE_IRQ,
},
{
.name = "LOW_BAT_R",
.start = AB8500_INT_LOW_BAT_R,
.end = AB8500_INT_LOW_BAT_R,
.name = "MAIN_EXT_CH_NOT_OK",
.start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
.end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
.flags = IORESOURCE_IRQ,
},
{
.name = "MAIN_CH_TH_PROT_R",
.start = AB8500_INT_MAIN_CH_TH_PROT_R,
.end = AB8500_INT_MAIN_CH_TH_PROT_R,
.flags = IORESOURCE_IRQ,
},
{
.name = "MAIN_CH_TH_PROT_F",
.start = AB8500_INT_MAIN_CH_TH_PROT_F,
.end = AB8500_INT_MAIN_CH_TH_PROT_F,
.flags = IORESOURCE_IRQ,
},
{
.name = "USB_CHARGER_NOT_OKR",
.start = AB8500_INT_USB_CHARGER_NOT_OK,
.end = AB8500_INT_USB_CHARGER_NOT_OK,
.flags = IORESOURCE_IRQ,
},
{
.name = "USB_CHARGER_NOT_OKF",
.start = AB8500_INT_USB_CHARGER_NOT_OKF,
.end = AB8500_INT_USB_CHARGER_NOT_OKF,
.flags = IORESOURCE_IRQ,
},
{
.name = "CH_WD_EXP",
.start = AB8500_INT_CH_WD_EXP,
.end = AB8500_INT_CH_WD_EXP,
.flags = IORESOURCE_IRQ,
},
};
static struct resource __devinitdata ab8500_btemp_resources[] = {
{
.name = "BAT_CTRL_INDB",
.start = AB8500_INT_BAT_CTRL_INDB,
.end = AB8500_INT_BAT_CTRL_INDB,
.flags = IORESOURCE_IRQ,
},
{
@ -503,38 +569,55 @@ static struct resource ab8500_bm_resources[] = {
.flags = IORESOURCE_IRQ,
},
{
.name = "USB_CHARGER_NOT_OKR",
.start = AB8500_INT_USB_CHARGER_NOT_OK,
.end = AB8500_INT_USB_CHARGER_NOT_OK,
.name = "BTEMP_LOW_MEDIUM",
.start = AB8500_INT_BTEMP_LOW_MEDIUM,
.end = AB8500_INT_BTEMP_LOW_MEDIUM,
.flags = IORESOURCE_IRQ,
},
{
.name = "USB_CHARGE_DET_DONE",
.start = AB8500_INT_USB_CHG_DET_DONE,
.end = AB8500_INT_USB_CHG_DET_DONE,
.flags = IORESOURCE_IRQ,
},
{
.name = "USB_CH_TH_PROT_R",
.start = AB8500_INT_USB_CH_TH_PROT_R,
.end = AB8500_INT_USB_CH_TH_PROT_R,
.flags = IORESOURCE_IRQ,
},
{
.name = "MAIN_CH_TH_PROT_R",
.start = AB8500_INT_MAIN_CH_TH_PROT_R,
.end = AB8500_INT_MAIN_CH_TH_PROT_R,
.flags = IORESOURCE_IRQ,
},
{
.name = "USB_CHARGER_NOT_OKF",
.start = AB8500_INT_USB_CHARGER_NOT_OKF,
.end = AB8500_INT_USB_CHARGER_NOT_OKF,
.name = "BTEMP_MEDIUM_HIGH",
.start = AB8500_INT_BTEMP_MEDIUM_HIGH,
.end = AB8500_INT_BTEMP_MEDIUM_HIGH,
.flags = IORESOURCE_IRQ,
},
};
static struct resource ab8500_debug_resources[] = {
static struct resource __devinitdata ab8500_fg_resources[] = {
{
.name = "NCONV_ACCU",
.start = AB8500_INT_CCN_CONV_ACC,
.end = AB8500_INT_CCN_CONV_ACC,
.flags = IORESOURCE_IRQ,
},
{
.name = "BATT_OVV",
.start = AB8500_INT_BATT_OVV,
.end = AB8500_INT_BATT_OVV,
.flags = IORESOURCE_IRQ,
},
{
.name = "LOW_BAT_F",
.start = AB8500_INT_LOW_BAT_F,
.end = AB8500_INT_LOW_BAT_F,
.flags = IORESOURCE_IRQ,
},
{
.name = "LOW_BAT_R",
.start = AB8500_INT_LOW_BAT_R,
.end = AB8500_INT_LOW_BAT_R,
.flags = IORESOURCE_IRQ,
},
{
.name = "CC_INT_CALIB",
.start = AB8500_INT_CC_INT_CALIB,
.end = AB8500_INT_CC_INT_CALIB,
.flags = IORESOURCE_IRQ,
},
};
static struct resource __devinitdata ab8500_chargalg_resources[] = {};
static struct resource __devinitdata ab8500_debug_resources[] = {
{
.name = "IRQ_FIRST",
.start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
@ -549,7 +632,7 @@ static struct resource ab8500_debug_resources[] = {
},
};
static struct resource ab8500_usb_resources[] = {
static struct resource __devinitdata ab8500_usb_resources[] = {
{
.name = "ID_WAKEUP_R",
.start = AB8500_INT_ID_WAKEUP_R,
@ -580,9 +663,21 @@ static struct resource ab8500_usb_resources[] = {
.end = AB8500_INT_USB_LINK_STATUS,
.flags = IORESOURCE_IRQ,
},
{
.name = "USB_ADP_PROBE_PLUG",
.start = AB8500_INT_ADP_PROBE_PLUG,
.end = AB8500_INT_ADP_PROBE_PLUG,
.flags = IORESOURCE_IRQ,
},
{
.name = "USB_ADP_PROBE_UNPLUG",
.start = AB8500_INT_ADP_PROBE_UNPLUG,
.end = AB8500_INT_ADP_PROBE_UNPLUG,
.flags = IORESOURCE_IRQ,
},
};
static struct resource ab8500_temp_resources[] = {
static struct resource __devinitdata ab8500_temp_resources[] = {
{
.name = "AB8500_TEMP_WARM",
.start = AB8500_INT_TEMP_WARM,
@ -591,7 +686,7 @@ static struct resource ab8500_temp_resources[] = {
},
};
static struct mfd_cell ab8500_devs[] = {
static struct mfd_cell __devinitdata ab8500_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
@ -621,11 +716,33 @@ static struct mfd_cell ab8500_devs[] = {
.resources = ab8500_rtc_resources,
},
{
.name = "ab8500-bm",
.num_resources = ARRAY_SIZE(ab8500_bm_resources),
.resources = ab8500_bm_resources,
.name = "ab8500-charger",
.num_resources = ARRAY_SIZE(ab8500_charger_resources),
.resources = ab8500_charger_resources,
},
{
.name = "ab8500-btemp",
.num_resources = ARRAY_SIZE(ab8500_btemp_resources),
.resources = ab8500_btemp_resources,
},
{
.name = "ab8500-fg",
.num_resources = ARRAY_SIZE(ab8500_fg_resources),
.resources = ab8500_fg_resources,
},
{
.name = "ab8500-chargalg",
.num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
.resources = ab8500_chargalg_resources,
},
{
.name = "ab8500-acc-det",
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
.resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-codec",
},
{ .name = "ab8500-codec", },
{
.name = "ab8500-usb",
.num_resources = ARRAY_SIZE(ab8500_usb_resources),

View File

@ -419,20 +419,13 @@ static ssize_t ab8500_bank_write(struct file *file,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_bank;
int err;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_bank);
err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
if (err)
return -EINVAL;
return err;
if (user_bank >= AB8500_NUM_BANKS) {
dev_err(dev, "debugfs error input > number of banks\n");
@ -441,7 +434,7 @@ static ssize_t ab8500_bank_write(struct file *file,
debug_bank = user_bank;
return buf_size;
return count;
}
static int ab8500_address_print(struct seq_file *s, void *p)
@ -459,26 +452,20 @@ static ssize_t ab8500_address_write(struct file *file,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_address;
int err;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_address);
err = kstrtoul_from_user(user_buf, count, 0, &user_address);
if (err)
return -EINVAL;
return err;
if (user_address > 0xff) {
dev_err(dev, "debugfs error input > 0xff\n");
return -EINVAL;
}
debug_address = user_address;
return buf_size;
return count;
}
static int ab8500_val_print(struct seq_file *s, void *p)
@ -509,20 +496,14 @@ static ssize_t ab8500_val_write(struct file *file,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_val;
int err;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_val);
err = kstrtoul_from_user(user_buf, count, 0, &user_val);
if (err)
return -EINVAL;
return err;
if (user_val > 0xff) {
dev_err(dev, "debugfs error input > 0xff\n");
return -EINVAL;
@ -534,7 +515,7 @@ static ssize_t ab8500_val_write(struct file *file,
return -EINVAL;
}
return buf_size;
return count;
}
static const struct file_operations ab8500_bank_fops = {

View File

@ -56,7 +56,7 @@ struct jz4740_adc {
void __iomem *base;
int irq;
int irq_base;
struct irq_chip_generic *gc;
struct clk *clk;
atomic_t clk_ref;
@ -64,63 +64,17 @@ struct jz4740_adc {
spinlock_t lock;
};
static inline void jz4740_adc_irq_set_masked(struct jz4740_adc *adc, int irq,
bool masked)
{
unsigned long flags;
uint8_t val;
irq -= adc->irq_base;
spin_lock_irqsave(&adc->lock, flags);
val = readb(adc->base + JZ_REG_ADC_CTRL);
if (masked)
val |= BIT(irq);
else
val &= ~BIT(irq);
writeb(val, adc->base + JZ_REG_ADC_CTRL);
spin_unlock_irqrestore(&adc->lock, flags);
}
static void jz4740_adc_irq_mask(struct irq_data *data)
{
struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
jz4740_adc_irq_set_masked(adc, data->irq, true);
}
static void jz4740_adc_irq_unmask(struct irq_data *data)
{
struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
jz4740_adc_irq_set_masked(adc, data->irq, false);
}
static void jz4740_adc_irq_ack(struct irq_data *data)
{
struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
unsigned int irq = data->irq - adc->irq_base;
writeb(BIT(irq), adc->base + JZ_REG_ADC_STATUS);
}
static struct irq_chip jz4740_adc_irq_chip = {
.name = "jz4740-adc",
.irq_mask = jz4740_adc_irq_mask,
.irq_unmask = jz4740_adc_irq_unmask,
.irq_ack = jz4740_adc_irq_ack,
};
static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc)
{
struct jz4740_adc *adc = irq_desc_get_handler_data(desc);
struct irq_chip_generic *gc = irq_desc_get_handler_data(desc);
uint8_t status;
unsigned int i;
status = readb(adc->base + JZ_REG_ADC_STATUS);
status = readb(gc->reg_base + JZ_REG_ADC_STATUS);
for (i = 0; i < 5; ++i) {
if (status & BIT(i))
generic_handle_irq(adc->irq_base + i);
generic_handle_irq(gc->irq_base + i);
}
}
@ -249,10 +203,12 @@ const struct mfd_cell jz4740_adc_cells[] = {
static int __devinit jz4740_adc_probe(struct platform_device *pdev)
{
int ret;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
struct jz4740_adc *adc;
struct resource *mem_base;
int irq;
int ret;
int irq_base;
adc = kmalloc(sizeof(*adc), GFP_KERNEL);
if (!adc) {
@ -267,9 +223,9 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
goto err_free;
}
adc->irq_base = platform_get_irq(pdev, 1);
if (adc->irq_base < 0) {
ret = adc->irq_base;
irq_base = platform_get_irq(pdev, 1);
if (irq_base < 0) {
ret = irq_base;
dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret);
goto err_free;
}
@ -309,20 +265,28 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, adc);
for (irq = adc->irq_base; irq < adc->irq_base + 5; ++irq) {
irq_set_chip_data(irq, adc);
irq_set_chip_and_handler(irq, &jz4740_adc_irq_chip,
handle_level_irq);
}
gc = irq_alloc_generic_chip("INTC", 1, irq_base, adc->base,
handle_level_irq);
irq_set_handler_data(adc->irq, adc);
ct = gc->chip_types;
ct->regs.mask = JZ_REG_ADC_CTRL;
ct->regs.ack = JZ_REG_ADC_STATUS;
ct->chip.irq_mask = irq_gc_mask_set_bit;
ct->chip.irq_unmask = irq_gc_mask_clr_bit;
ct->chip.irq_ack = irq_gc_ack;
irq_setup_generic_chip(gc, IRQ_MSK(5), 0, 0, IRQ_NOPROBE | IRQ_LEVEL);
adc->gc = gc;
irq_set_handler_data(adc->irq, gc);
irq_set_chained_handler(adc->irq, jz4740_adc_irq_demux);
writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
ARRAY_SIZE(jz4740_adc_cells), mem_base, adc->irq_base);
ARRAY_SIZE(jz4740_adc_cells), mem_base, irq_base);
if (ret < 0)
goto err_clk_put;
@ -347,6 +311,8 @@ static int __devexit jz4740_adc_remove(struct platform_device *pdev)
mfd_remove_devices(&pdev->dev);
irq_remove_generic_chip(adc->gc, IRQ_MSK(5), IRQ_NOPROBE | IRQ_LEVEL, 0);
kfree(adc->gc);
irq_set_handler_data(adc->irq, NULL);
irq_set_chained_handler(adc->irq, NULL);

View File

@ -37,6 +37,9 @@
#define GPIOBASE 0x44
#define GPIO_IO_SIZE 64
#define WDTBASE 0x84
#define WDT_IO_SIZE 64
static struct resource smbus_sch_resource = {
.flags = IORESOURCE_IO,
};
@ -59,6 +62,18 @@ static struct mfd_cell lpc_sch_cells[] = {
},
};
static struct resource wdt_sch_resource = {
.flags = IORESOURCE_IO,
};
static struct mfd_cell tunnelcreek_cells[] = {
{
.name = "tunnelcreek_wdt",
.num_resources = 1,
.resources = &wdt_sch_resource,
},
};
static struct pci_device_id lpc_sch_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
@ -72,6 +87,7 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
unsigned int base_addr_cfg;
unsigned short base_addr;
int i;
int ret;
pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
if (!(base_addr_cfg & (1 << 31))) {
@ -104,8 +120,39 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
lpc_sch_cells[i].id = id->device;
return mfd_add_devices(&dev->dev, 0,
ret = mfd_add_devices(&dev->dev, 0,
lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
if (ret)
goto out_dev;
if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC) {
pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
if (!(base_addr_cfg & (1 << 31))) {
dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
ret = -ENODEV;
goto out_dev;
}
base_addr = (unsigned short)base_addr_cfg;
if (base_addr == 0) {
dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
ret = -ENODEV;
goto out_dev;
}
wdt_sch_resource.start = base_addr;
wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
tunnelcreek_cells[i].id = id->device;
ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
ARRAY_SIZE(tunnelcreek_cells), NULL, 0);
}
return ret;
out_dev:
mfd_remove_devices(&dev->dev);
return ret;
}
static void __devexit lpc_sch_remove(struct pci_dev *dev)

View File

@ -58,8 +58,6 @@ static struct i2c_client *get_i2c(struct max8997_dev *max8997,
default:
return ERR_PTR(-EINVAL);
}
return ERR_PTR(-EINVAL);
}
struct max8997_irq_data {

View File

@ -998,9 +998,9 @@ static void usbhs_disable(struct device *dev)
if (is_omap_usbhs_rev2(omap)) {
if (is_ehci_tll_mode(pdata->port_mode[0]))
clk_enable(omap->usbtll_p1_fck);
clk_disable(omap->usbtll_p1_fck);
if (is_ehci_tll_mode(pdata->port_mode[1]))
clk_enable(omap->usbtll_p2_fck);
clk_disable(omap->usbtll_p2_fck);
clk_disable(omap->utmi_p2_fck);
clk_disable(omap->utmi_p1_fck);
}

View File

@ -228,7 +228,7 @@ int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
EXPORT_SYMBOL_GPL(stmpe_block_write);
/**
* stmpe_set_altfunc: set the alternate function for STMPE pins
* stmpe_set_altfunc()- set the alternate function for STMPE pins
* @stmpe: Device to configure
* @pins: Bitmask of pins to affect
* @block: block to enable alternate functions for

View File

@ -42,6 +42,7 @@ struct stmpe_variant_block {
* @id_mask: bits valid in CHIPID register for comparison with id_val
* @num_gpios: number of GPIOS
* @af_bits: number of bits used to specify the alternate function
* @regs: variant specific registers.
* @blocks: list of blocks present on this device
* @num_blocks: number of blocks present on this device
* @num_irqs: number of internal IRQs available on this device

View File

@ -147,12 +147,11 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
if (init_data == NULL)
return -ENOMEM;
init_data->irq = pmic_plat_data->irq;
init_data->irq_base = pmic_plat_data->irq;
tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
if (tps65910 == NULL)
if (tps65910 == NULL) {
kfree(init_data);
return -ENOMEM;
}
i2c_set_clientdata(i2c, tps65910);
tps65910->dev = &i2c->dev;
@ -168,17 +167,22 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
if (ret < 0)
goto err;
init_data->irq = pmic_plat_data->irq;
init_data->irq_base = pmic_plat_data->irq;
tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
ret = tps65910_irq_init(tps65910, init_data->irq, init_data);
if (ret < 0)
goto err;
kfree(init_data);
return ret;
err:
mfd_remove_devices(tps65910->dev);
kfree(tps65910);
kfree(init_data);
return ret;
}
@ -187,6 +191,7 @@ static int tps65910_i2c_remove(struct i2c_client *i2c)
struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
mfd_remove_devices(tps65910->dev);
tps65910_irq_exit(tps65910);
kfree(tps65910);
return 0;

View File

@ -157,6 +157,8 @@ static __devexit int tps65911_comparator_remove(struct platform_device *pdev)
struct tps65910 *tps65910;
tps65910 = dev_get_drvdata(pdev->dev.parent);
device_remove_file(&pdev->dev, &dev_attr_comp2_threshold);
device_remove_file(&pdev->dev, &dev_attr_comp1_threshold);
return 0;
}

177
drivers/mfd/tps65912-core.c Normal file
View File

@ -0,0 +1,177 @@
/*
* tps65912-core.c -- TI TPS65912x
*
* Copyright 2011 Texas Instruments Inc.
*
* Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
*
* 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 driver is based on wm8350 implementation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps65912.h>
static struct mfd_cell tps65912s[] = {
{
.name = "tps65912-pmic",
},
};
int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
{
u8 data;
int err;
mutex_lock(&tps65912->io_mutex);
err = tps65912->read(tps65912, reg, 1, &data);
if (err) {
dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
goto out;
}
data |= mask;
err = tps65912->write(tps65912, reg, 1, &data);
if (err)
dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
out:
mutex_unlock(&tps65912->io_mutex);
return err;
}
EXPORT_SYMBOL_GPL(tps65912_set_bits);
int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
{
u8 data;
int err;
mutex_lock(&tps65912->io_mutex);
err = tps65912->read(tps65912, reg, 1, &data);
if (err) {
dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
goto out;
}
data &= ~mask;
err = tps65912->write(tps65912, reg, 1, &data);
if (err)
dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
out:
mutex_unlock(&tps65912->io_mutex);
return err;
}
EXPORT_SYMBOL_GPL(tps65912_clear_bits);
static inline int tps65912_read(struct tps65912 *tps65912, u8 reg)
{
u8 val;
int err;
err = tps65912->read(tps65912, reg, 1, &val);
if (err < 0)
return err;
return val;
}
static inline int tps65912_write(struct tps65912 *tps65912, u8 reg, u8 val)
{
return tps65912->write(tps65912, reg, 1, &val);
}
int tps65912_reg_read(struct tps65912 *tps65912, u8 reg)
{
int data;
mutex_lock(&tps65912->io_mutex);
data = tps65912_read(tps65912, reg);
if (data < 0)
dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
mutex_unlock(&tps65912->io_mutex);
return data;
}
EXPORT_SYMBOL_GPL(tps65912_reg_read);
int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val)
{
int err;
mutex_lock(&tps65912->io_mutex);
err = tps65912_write(tps65912, reg, val);
if (err < 0)
dev_err(tps65912->dev, "Write for reg 0x%x failed\n", reg);
mutex_unlock(&tps65912->io_mutex);
return err;
}
EXPORT_SYMBOL_GPL(tps65912_reg_write);
int tps65912_device_init(struct tps65912 *tps65912)
{
struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data;
struct tps65912_platform_data *init_data;
int ret, dcdc_avs, value;
init_data = kzalloc(sizeof(struct tps65912_platform_data), GFP_KERNEL);
if (init_data == NULL)
return -ENOMEM;
init_data->irq = pmic_plat_data->irq;
init_data->irq_base = pmic_plat_data->irq;
mutex_init(&tps65912->io_mutex);
dev_set_drvdata(tps65912->dev, tps65912);
dcdc_avs = (pmic_plat_data->is_dcdc1_avs << 0 |
pmic_plat_data->is_dcdc2_avs << 1 |
pmic_plat_data->is_dcdc3_avs << 2 |
pmic_plat_data->is_dcdc4_avs << 3);
if (dcdc_avs) {
tps65912->read(tps65912, TPS65912_I2C_SPI_CFG, 1, &value);
dcdc_avs |= value;
tps65912->write(tps65912, TPS65912_I2C_SPI_CFG, 1, &dcdc_avs);
}
ret = mfd_add_devices(tps65912->dev, -1,
tps65912s, ARRAY_SIZE(tps65912s),
NULL, 0);
if (ret < 0)
goto err;
ret = tps65912_irq_init(tps65912, init_data->irq, init_data);
if (ret < 0)
goto err;
return ret;
err:
kfree(init_data);
mfd_remove_devices(tps65912->dev);
kfree(tps65912);
return ret;
}
void tps65912_device_exit(struct tps65912 *tps65912)
{
mfd_remove_devices(tps65912->dev);
kfree(tps65912);
}
MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
MODULE_DESCRIPTION("TPS65912x chip family multi-function driver");
MODULE_LICENSE("GPL");

139
drivers/mfd/tps65912-i2c.c Normal file
View File

@ -0,0 +1,139 @@
/*
* tps65912-i2c.c -- I2C access for TI TPS65912x PMIC
*
* Copyright 2011 Texas Instruments Inc.
*
* Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
*
* 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 driver is based on wm8350 implementation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps65912.h>
static int tps65912_i2c_read(struct tps65912 *tps65912, u8 reg,
int bytes, void *dest)
{
struct i2c_client *i2c = tps65912->control_data;
struct i2c_msg xfer[2];
int ret;
/* Write register */
xfer[0].addr = i2c->addr;
xfer[0].flags = 0;
xfer[0].len = 1;
xfer[0].buf = &reg;
/* Read data */
xfer[1].addr = i2c->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = bytes;
xfer[1].buf = dest;
ret = i2c_transfer(i2c->adapter, xfer, 2);
if (ret == 2)
ret = 0;
else if (ret >= 0)
ret = -EIO;
return ret;
}
static int tps65912_i2c_write(struct tps65912 *tps65912, u8 reg,
int bytes, void *src)
{
struct i2c_client *i2c = tps65912->control_data;
/* we add 1 byte for device register */
u8 msg[TPS6591X_MAX_REGISTER + 1];
int ret;
if (bytes > TPS6591X_MAX_REGISTER)
return -EINVAL;
msg[0] = reg;
memcpy(&msg[1], src, bytes);
ret = i2c_master_send(i2c, msg, bytes + 1);
if (ret < 0)
return ret;
if (ret != bytes + 1)
return -EIO;
return 0;
}
static int tps65912_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct tps65912 *tps65912;
tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
if (tps65912 == NULL)
return -ENOMEM;
i2c_set_clientdata(i2c, tps65912);
tps65912->dev = &i2c->dev;
tps65912->control_data = i2c;
tps65912->read = tps65912_i2c_read;
tps65912->write = tps65912_i2c_write;
return tps65912_device_init(tps65912);
}
static int tps65912_i2c_remove(struct i2c_client *i2c)
{
struct tps65912 *tps65912 = i2c_get_clientdata(i2c);
tps65912_device_exit(tps65912);
return 0;
}
static const struct i2c_device_id tps65912_i2c_id[] = {
{"tps65912", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id);
static struct i2c_driver tps65912_i2c_driver = {
.driver = {
.name = "tps65912",
.owner = THIS_MODULE,
},
.probe = tps65912_i2c_probe,
.remove = tps65912_i2c_remove,
.id_table = tps65912_i2c_id,
};
static int __init tps65912_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&tps65912_i2c_driver);
if (ret != 0)
pr_err("Failed to register TPS65912 I2C driver: %d\n", ret);
return ret;
}
/* init early so consumer devices can complete system boot */
subsys_initcall(tps65912_i2c_init);
static void __exit tps65912_i2c_exit(void)
{
i2c_del_driver(&tps65912_i2c_driver);
}
module_exit(tps65912_i2c_exit);
MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
MODULE_LICENSE("GPL");

224
drivers/mfd/tps65912-irq.c Normal file
View File

@ -0,0 +1,224 @@
/*
* tps65912-irq.c -- TI TPS6591x
*
* Copyright 2011 Texas Instruments Inc.
*
* Author: Margarita Olaya <magi@slimlogic.co.uk>
*
* 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 driver is based on wm8350 implementation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/mfd/tps65912.h>
static inline int irq_to_tps65912_irq(struct tps65912 *tps65912,
int irq)
{
return irq - tps65912->irq_base;
}
/*
* This is a threaded IRQ handler so can access I2C/SPI. Since the
* IRQ handler explicitly clears the IRQ it handles the IRQ line
* will be reasserted and the physical IRQ will be handled again if
* another interrupt is asserted while we run - in the normal course
* of events this is a rare occurrence so we save I2C/SPI reads. We're
* also assuming that it's rare to get lots of interrupts firing
* simultaneously so try to minimise I/O.
*/
static irqreturn_t tps65912_irq(int irq, void *irq_data)
{
struct tps65912 *tps65912 = irq_data;
u32 irq_sts;
u32 irq_mask;
u8 reg;
int i;
tps65912->read(tps65912, TPS65912_INT_STS, 1, &reg);
irq_sts = reg;
tps65912->read(tps65912, TPS65912_INT_STS2, 1, &reg);
irq_sts |= reg << 8;
tps65912->read(tps65912, TPS65912_INT_STS3, 1, &reg);
irq_sts |= reg << 16;
tps65912->read(tps65912, TPS65912_INT_STS4, 1, &reg);
irq_sts |= reg << 24;
tps65912->read(tps65912, TPS65912_INT_MSK, 1, &reg);
irq_mask = reg;
tps65912->read(tps65912, TPS65912_INT_MSK2, 1, &reg);
irq_mask |= reg << 8;
tps65912->read(tps65912, TPS65912_INT_MSK3, 1, &reg);
irq_mask |= reg << 16;
tps65912->read(tps65912, TPS65912_INT_MSK4, 1, &reg);
irq_mask |= reg << 24;
irq_sts &= ~irq_mask;
if (!irq_sts)
return IRQ_NONE;
for (i = 0; i < tps65912->irq_num; i++) {
if (!(irq_sts & (1 << i)))
continue;
handle_nested_irq(tps65912->irq_base + i);
}
/* Write the STS register back to clear IRQs we handled */
reg = irq_sts & 0xFF;
irq_sts >>= 8;
if (reg)
tps65912->write(tps65912, TPS65912_INT_STS, 1, &reg);
reg = irq_sts & 0xFF;
irq_sts >>= 8;
if (reg)
tps65912->write(tps65912, TPS65912_INT_STS2, 1, &reg);
reg = irq_sts & 0xFF;
irq_sts >>= 8;
if (reg)
tps65912->write(tps65912, TPS65912_INT_STS3, 1, &reg);
reg = irq_sts & 0xFF;
if (reg)
tps65912->write(tps65912, TPS65912_INT_STS4, 1, &reg);
return IRQ_HANDLED;
}
static void tps65912_irq_lock(struct irq_data *data)
{
struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
mutex_lock(&tps65912->irq_lock);
}
static void tps65912_irq_sync_unlock(struct irq_data *data)
{
struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
u32 reg_mask;
u8 reg;
tps65912->read(tps65912, TPS65912_INT_MSK, 1, &reg);
reg_mask = reg;
tps65912->read(tps65912, TPS65912_INT_MSK2, 1, &reg);
reg_mask |= reg << 8;
tps65912->read(tps65912, TPS65912_INT_MSK3, 1, &reg);
reg_mask |= reg << 16;
tps65912->read(tps65912, TPS65912_INT_MSK4, 1, &reg);
reg_mask |= reg << 24;
if (tps65912->irq_mask != reg_mask) {
reg = tps65912->irq_mask & 0xFF;
tps65912->write(tps65912, TPS65912_INT_MSK, 1, &reg);
reg = tps65912->irq_mask >> 8 & 0xFF;
tps65912->write(tps65912, TPS65912_INT_MSK2, 1, &reg);
reg = tps65912->irq_mask >> 16 & 0xFF;
tps65912->write(tps65912, TPS65912_INT_MSK3, 1, &reg);
reg = tps65912->irq_mask >> 24 & 0xFF;
tps65912->write(tps65912, TPS65912_INT_MSK4, 1, &reg);
}
mutex_unlock(&tps65912->irq_lock);
}
static void tps65912_irq_enable(struct irq_data *data)
{
struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
tps65912->irq_mask &= ~(1 << irq_to_tps65912_irq(tps65912, data->irq));
}
static void tps65912_irq_disable(struct irq_data *data)
{
struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
tps65912->irq_mask |= (1 << irq_to_tps65912_irq(tps65912, data->irq));
}
static struct irq_chip tps65912_irq_chip = {
.name = "tps65912",
.irq_bus_lock = tps65912_irq_lock,
.irq_bus_sync_unlock = tps65912_irq_sync_unlock,
.irq_disable = tps65912_irq_disable,
.irq_enable = tps65912_irq_enable,
};
int tps65912_irq_init(struct tps65912 *tps65912, int irq,
struct tps65912_platform_data *pdata)
{
int ret, cur_irq;
int flags = IRQF_ONESHOT;
u8 reg;
if (!irq) {
dev_warn(tps65912->dev, "No interrupt support, no core IRQ\n");
return 0;
}
if (!pdata || !pdata->irq_base) {
dev_warn(tps65912->dev, "No interrupt support, no IRQ base\n");
return 0;
}
/* Clear unattended interrupts */
tps65912->read(tps65912, TPS65912_INT_STS, 1, &reg);
tps65912->write(tps65912, TPS65912_INT_STS, 1, &reg);
tps65912->read(tps65912, TPS65912_INT_STS2, 1, &reg);
tps65912->write(tps65912, TPS65912_INT_STS2, 1, &reg);
tps65912->read(tps65912, TPS65912_INT_STS3, 1, &reg);
tps65912->write(tps65912, TPS65912_INT_STS3, 1, &reg);
tps65912->read(tps65912, TPS65912_INT_STS4, 1, &reg);
tps65912->write(tps65912, TPS65912_INT_STS4, 1, &reg);
/* Mask top level interrupts */
tps65912->irq_mask = 0xFFFFFFFF;
mutex_init(&tps65912->irq_lock);
tps65912->chip_irq = irq;
tps65912->irq_base = pdata->irq_base;
tps65912->irq_num = TPS65912_NUM_IRQ;
/* Register with genirq */
for (cur_irq = tps65912->irq_base;
cur_irq < tps65912->irq_num + tps65912->irq_base;
cur_irq++) {
irq_set_chip_data(cur_irq, tps65912);
irq_set_chip_and_handler(cur_irq, &tps65912_irq_chip,
handle_edge_irq);
irq_set_nested_thread(cur_irq, 1);
/* ARM needs us to explicitly flag the IRQ as valid
* and will set them noprobe when we do so. */
#ifdef CONFIG_ARM
set_irq_flags(cur_irq, IRQF_VALID);
#else
irq_set_noprobe(cur_irq);
#endif
}
ret = request_threaded_irq(irq, NULL, tps65912_irq, flags,
"tps65912", tps65912);
irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
if (ret != 0)
dev_err(tps65912->dev, "Failed to request IRQ: %d\n", ret);
return ret;
}
int tps65912_irq_exit(struct tps65912 *tps65912)
{
free_irq(tps65912->chip_irq, tps65912);
return 0;
}

142
drivers/mfd/tps65912-spi.c Normal file
View File

@ -0,0 +1,142 @@
/*
* tps65912-spi.c -- SPI access for TI TPS65912x PMIC
*
* Copyright 2011 Texas Instruments Inc.
*
* Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
*
* 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 driver is based on wm8350 implementation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps65912.h>
static int tps65912_spi_write(struct tps65912 *tps65912, u8 addr,
int bytes, void *src)
{
struct spi_device *spi = tps65912->control_data;
u8 *data = (u8 *) src;
int ret;
/* bit 23 is the read/write bit */
unsigned long spi_data = 1 << 23 | addr << 15 | *data;
struct spi_transfer xfer;
struct spi_message msg;
u32 tx_buf, rx_buf;
tx_buf = spi_data;
rx_buf = 0;
xfer.tx_buf = &tx_buf;
xfer.rx_buf = NULL;
xfer.len = sizeof(unsigned long);
xfer.bits_per_word = 24;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
ret = spi_sync(spi, &msg);
return ret;
}
static int tps65912_spi_read(struct tps65912 *tps65912, u8 addr,
int bytes, void *dest)
{
struct spi_device *spi = tps65912->control_data;
/* bit 23 is the read/write bit */
unsigned long spi_data = 0 << 23 | addr << 15;
struct spi_transfer xfer;
struct spi_message msg;
int ret;
u8 *data = (u8 *) dest;
u32 tx_buf, rx_buf;
tx_buf = spi_data;
rx_buf = 0;
xfer.tx_buf = &tx_buf;
xfer.rx_buf = &rx_buf;
xfer.len = sizeof(unsigned long);
xfer.bits_per_word = 24;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
if (spi == NULL)
return 0;
ret = spi_sync(spi, &msg);
if (ret == 0)
*data = (u8) (rx_buf & 0xFF);
return ret;
}
static int __devinit tps65912_spi_probe(struct spi_device *spi)
{
struct tps65912 *tps65912;
tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
if (tps65912 == NULL)
return -ENOMEM;
tps65912->dev = &spi->dev;
tps65912->control_data = spi;
tps65912->read = tps65912_spi_read;
tps65912->write = tps65912_spi_write;
spi_set_drvdata(spi, tps65912);
return tps65912_device_init(tps65912);
}
static int __devexit tps65912_spi_remove(struct spi_device *spi)
{
struct tps65912 *tps65912 = spi_get_drvdata(spi);
tps65912_device_exit(tps65912);
return 0;
}
static struct spi_driver tps65912_spi_driver = {
.driver = {
.name = "tps65912",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = tps65912_spi_probe,
.remove = __devexit_p(tps65912_spi_remove),
};
static int __init tps65912_spi_init(void)
{
int ret;
ret = spi_register_driver(&tps65912_spi_driver);
if (ret != 0)
pr_err("Failed to register TPS65912 SPI driver: %d\n", ret);
return 0;
}
/* init early so consumer devices can complete system boot */
subsys_initcall(tps65912_spi_init);
static void __exit tps65912_spi_exit(void)
{
spi_unregister_driver(&tps65912_spi_driver);
}
module_exit(tps65912_spi_exit);
MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
MODULE_DESCRIPTION("SPI support for TPS65912 chip family mfd");
MODULE_LICENSE("GPL");

View File

@ -1283,6 +1283,8 @@ static const struct i2c_device_id twl_ids[] = {
{ "tps65950", 0 }, /* catalog version of twl5030 */
{ "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
{ "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
{ "tps65921", TPS_SUBSET }, /* fewer LDOs; no codec, no LED
and vibrator. Charger in USB module*/
{ "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
{ "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */
{ /* end of list */ },

View File

@ -530,13 +530,13 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
if (ret) {
dev_err(twl4030_madc->dev,
"unable to write sel register 0x%X\n", method->sel + 1);
return ret;
goto out;
}
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
if (ret) {
dev_err(twl4030_madc->dev,
"unable to write sel register 0x%X\n", method->sel + 1);
return ret;
goto out;
}
/* Select averaging for all channels if do_avg is set */
if (req->do_avg) {
@ -546,7 +546,7 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
dev_err(twl4030_madc->dev,
"unable to write avg register 0x%X\n",
method->avg + 1);
return ret;
goto out;
}
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
ch_lsb, method->avg);
@ -554,7 +554,7 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
dev_err(twl4030_madc->dev,
"unable to write sel reg 0x%X\n",
method->sel + 1);
return ret;
goto out;
}
}
if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {

View File

@ -161,3 +161,5 @@ void pwm_free(struct pwm_device *pwm)
kfree(pwm);
}
EXPORT_SYMBOL(pwm_free);
MODULE_LICENSE("GPL");

299
drivers/mfd/wm831x-auxadc.c Normal file
View File

@ -0,0 +1,299 @@
/*
* wm831x-auxadc.c -- AUXADC for Wolfson WM831x PMICs
*
* Copyright 2009-2011 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/irq.h>
#include <linux/mfd/wm831x/auxadc.h>
#include <linux/mfd/wm831x/otp.h>
#include <linux/mfd/wm831x/regulator.h>
struct wm831x_auxadc_req {
struct list_head list;
enum wm831x_auxadc input;
int val;
struct completion done;
};
static int wm831x_auxadc_read_irq(struct wm831x *wm831x,
enum wm831x_auxadc input)
{
struct wm831x_auxadc_req *req;
int ret;
bool ena = false;
req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return -ENOMEM;
init_completion(&req->done);
req->input = input;
req->val = -ETIMEDOUT;
mutex_lock(&wm831x->auxadc_lock);
/* Enqueue the request */
list_add(&req->list, &wm831x->auxadc_pending);
ena = !wm831x->auxadc_active;
if (ena) {
ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
WM831X_AUX_ENA, WM831X_AUX_ENA);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n",
ret);
goto out;
}
}
/* Enable the conversion if not already running */
if (!(wm831x->auxadc_active & (1 << input))) {
ret = wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
1 << input, 1 << input);
if (ret != 0) {
dev_err(wm831x->dev,
"Failed to set AUXADC source: %d\n", ret);
goto out;
}
wm831x->auxadc_active |= 1 << input;
}
/* We convert at the fastest rate possible */
if (ena) {
ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
WM831X_AUX_CVT_ENA |
WM831X_AUX_RATE_MASK,
WM831X_AUX_CVT_ENA |
WM831X_AUX_RATE_MASK);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to start AUXADC: %d\n",
ret);
goto out;
}
}
mutex_unlock(&wm831x->auxadc_lock);
/* Wait for an interrupt */
wait_for_completion_timeout(&req->done, msecs_to_jiffies(500));
mutex_lock(&wm831x->auxadc_lock);
list_del(&req->list);
ret = req->val;
out:
mutex_unlock(&wm831x->auxadc_lock);
kfree(req);
return ret;
}
static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
{
struct wm831x *wm831x = irq_data;
struct wm831x_auxadc_req *req;
int ret, input, val;
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
if (ret < 0) {
dev_err(wm831x->dev,
"Failed to read AUXADC data: %d\n", ret);
return IRQ_NONE;
}
input = ((ret & WM831X_AUX_DATA_SRC_MASK)
>> WM831X_AUX_DATA_SRC_SHIFT) - 1;
if (input == 14)
input = WM831X_AUX_CAL;
val = ret & WM831X_AUX_DATA_MASK;
mutex_lock(&wm831x->auxadc_lock);
/* Disable this conversion, we're about to complete all users */
wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
1 << input, 0);
wm831x->auxadc_active &= ~(1 << input);
/* Turn off the entire convertor if idle */
if (!wm831x->auxadc_active)
wm831x_reg_write(wm831x, WM831X_AUXADC_CONTROL, 0);
/* Wake up any threads waiting for this request */
list_for_each_entry(req, &wm831x->auxadc_pending, list) {
if (req->input == input) {
req->val = val;
complete(&req->done);
}
}
mutex_unlock(&wm831x->auxadc_lock);
return IRQ_HANDLED;
}
static int wm831x_auxadc_read_polled(struct wm831x *wm831x,
enum wm831x_auxadc input)
{
int ret, src, timeout;
mutex_lock(&wm831x->auxadc_lock);
ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
WM831X_AUX_ENA, WM831X_AUX_ENA);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
goto out;
}
/* We force a single source at present */
src = input;
ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
1 << src);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
goto out;
}
ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
goto disable;
}
/* If we're not using interrupts then poll the
* interrupt status register */
timeout = 5;
while (timeout) {
msleep(1);
ret = wm831x_reg_read(wm831x,
WM831X_INTERRUPT_STATUS_1);
if (ret < 0) {
dev_err(wm831x->dev,
"ISR 1 read failed: %d\n", ret);
goto disable;
}
/* Did it complete? */
if (ret & WM831X_AUXADC_DATA_EINT) {
wm831x_reg_write(wm831x,
WM831X_INTERRUPT_STATUS_1,
WM831X_AUXADC_DATA_EINT);
break;
} else {
dev_err(wm831x->dev,
"AUXADC conversion timeout\n");
ret = -EBUSY;
goto disable;
}
}
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
if (ret < 0) {
dev_err(wm831x->dev,
"Failed to read AUXADC data: %d\n", ret);
goto disable;
}
src = ((ret & WM831X_AUX_DATA_SRC_MASK)
>> WM831X_AUX_DATA_SRC_SHIFT) - 1;
if (src == 14)
src = WM831X_AUX_CAL;
if (src != input) {
dev_err(wm831x->dev, "Data from source %d not %d\n",
src, input);
ret = -EINVAL;
} else {
ret &= WM831X_AUX_DATA_MASK;
}
disable:
wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
out:
mutex_unlock(&wm831x->auxadc_lock);
return ret;
}
/**
* wm831x_auxadc_read: Read a value from the WM831x AUXADC
*
* @wm831x: Device to read from.
* @input: AUXADC input to read.
*/
int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
{
return wm831x->auxadc_read(wm831x, input);
}
EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
/**
* wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
*
* @wm831x: Device to read from.
* @input: AUXADC input to read.
*/
int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
{
int ret;
ret = wm831x_auxadc_read(wm831x, input);
if (ret < 0)
return ret;
ret *= 1465;
return ret;
}
EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
void wm831x_auxadc_init(struct wm831x *wm831x)
{
int ret;
mutex_init(&wm831x->auxadc_lock);
INIT_LIST_HEAD(&wm831x->auxadc_pending);
if (wm831x->irq && wm831x->irq_base) {
wm831x->auxadc_read = wm831x_auxadc_read_irq;
ret = request_threaded_irq(wm831x->irq_base +
WM831X_IRQ_AUXADC_DATA,
NULL, wm831x_auxadc_irq, 0,
"auxadc", wm831x);
if (ret < 0) {
dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
ret);
wm831x->auxadc_read = NULL;
}
}
if (!wm831x->auxadc_read)
wm831x->auxadc_read = wm831x_auxadc_read_polled;
}

View File

@ -295,7 +295,7 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
goto out;
r &= ~mask;
r |= val;
r |= val & mask;
ret = wm831x_write(wm831x, reg, 2, &r);
@ -306,146 +306,6 @@ out:
}
EXPORT_SYMBOL_GPL(wm831x_set_bits);
/**
* wm831x_auxadc_read: Read a value from the WM831x AUXADC
*
* @wm831x: Device to read from.
* @input: AUXADC input to read.
*/
int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
{
int ret, src, irq_masked, timeout;
/* Are we using the interrupt? */
irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK);
irq_masked &= WM831X_AUXADC_DATA_EINT;
mutex_lock(&wm831x->auxadc_lock);
ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
WM831X_AUX_ENA, WM831X_AUX_ENA);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
goto out;
}
/* We force a single source at present */
src = input;
ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
1 << src);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
goto out;
}
/* Clear any notification from a very late arriving interrupt */
try_wait_for_completion(&wm831x->auxadc_done);
ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
goto disable;
}
if (irq_masked) {
/* If we're not using interrupts then poll the
* interrupt status register */
timeout = 5;
while (timeout) {
msleep(1);
ret = wm831x_reg_read(wm831x,
WM831X_INTERRUPT_STATUS_1);
if (ret < 0) {
dev_err(wm831x->dev,
"ISR 1 read failed: %d\n", ret);
goto disable;
}
/* Did it complete? */
if (ret & WM831X_AUXADC_DATA_EINT) {
wm831x_reg_write(wm831x,
WM831X_INTERRUPT_STATUS_1,
WM831X_AUXADC_DATA_EINT);
break;
} else {
dev_err(wm831x->dev,
"AUXADC conversion timeout\n");
ret = -EBUSY;
goto disable;
}
}
} else {
/* If we are using interrupts then wait for the
* interrupt to complete. Use an extremely long
* timeout to handle situations with heavy load where
* the notification of the interrupt may be delayed by
* threaded IRQ handling. */
if (!wait_for_completion_timeout(&wm831x->auxadc_done,
msecs_to_jiffies(500))) {
dev_err(wm831x->dev, "Timed out waiting for AUXADC\n");
ret = -EBUSY;
goto disable;
}
}
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read AUXADC data: %d\n", ret);
} else {
src = ((ret & WM831X_AUX_DATA_SRC_MASK)
>> WM831X_AUX_DATA_SRC_SHIFT) - 1;
if (src == 14)
src = WM831X_AUX_CAL;
if (src != input) {
dev_err(wm831x->dev, "Data from source %d not %d\n",
src, input);
ret = -EINVAL;
} else {
ret &= WM831X_AUX_DATA_MASK;
}
}
disable:
wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
out:
mutex_unlock(&wm831x->auxadc_lock);
return ret;
}
EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
{
struct wm831x *wm831x = irq_data;
complete(&wm831x->auxadc_done);
return IRQ_HANDLED;
}
/**
* wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
*
* @wm831x: Device to read from.
* @input: AUXADC input to read.
*/
int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
{
int ret;
ret = wm831x_auxadc_read(wm831x, input);
if (ret < 0)
return ret;
ret *= 1465;
return ret;
}
EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
static struct resource wm831x_dcdc1_resources[] = {
{
.start = WM831X_DC1_CONTROL_1,
@ -871,6 +731,9 @@ static struct mfd_cell wm8310_devs[] = {
.num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
.resources = wm831x_dcdc4_resources,
},
{
.name = "wm831x-clk",
},
{
.name = "wm831x-epe",
.id = 1,
@ -975,11 +838,6 @@ static struct mfd_cell wm8310_devs[] = {
.num_resources = ARRAY_SIZE(wm831x_power_resources),
.resources = wm831x_power_resources,
},
{
.name = "wm831x-rtc",
.num_resources = ARRAY_SIZE(wm831x_rtc_resources),
.resources = wm831x_rtc_resources,
},
{
.name = "wm831x-status",
.id = 1,
@ -1027,6 +885,9 @@ static struct mfd_cell wm8311_devs[] = {
.num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
.resources = wm831x_dcdc4_resources,
},
{
.name = "wm831x-clk",
},
{
.name = "wm831x-epe",
.id = 1,
@ -1107,11 +968,6 @@ static struct mfd_cell wm8311_devs[] = {
.num_resources = ARRAY_SIZE(wm831x_power_resources),
.resources = wm831x_power_resources,
},
{
.name = "wm831x-rtc",
.num_resources = ARRAY_SIZE(wm831x_rtc_resources),
.resources = wm831x_rtc_resources,
},
{
.name = "wm831x-status",
.id = 1,
@ -1124,11 +980,6 @@ static struct mfd_cell wm8311_devs[] = {
.num_resources = ARRAY_SIZE(wm831x_status2_resources),
.resources = wm831x_status2_resources,
},
{
.name = "wm831x-touch",
.num_resources = ARRAY_SIZE(wm831x_touch_resources),
.resources = wm831x_touch_resources,
},
{
.name = "wm831x-watchdog",
.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
@ -1164,6 +1015,9 @@ static struct mfd_cell wm8312_devs[] = {
.num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
.resources = wm831x_dcdc4_resources,
},
{
.name = "wm831x-clk",
},
{
.name = "wm831x-epe",
.id = 1,
@ -1268,11 +1122,6 @@ static struct mfd_cell wm8312_devs[] = {
.num_resources = ARRAY_SIZE(wm831x_power_resources),
.resources = wm831x_power_resources,
},
{
.name = "wm831x-rtc",
.num_resources = ARRAY_SIZE(wm831x_rtc_resources),
.resources = wm831x_rtc_resources,
},
{
.name = "wm831x-status",
.id = 1,
@ -1285,11 +1134,6 @@ static struct mfd_cell wm8312_devs[] = {
.num_resources = ARRAY_SIZE(wm831x_status2_resources),
.resources = wm831x_status2_resources,
},
{
.name = "wm831x-touch",
.num_resources = ARRAY_SIZE(wm831x_touch_resources),
.resources = wm831x_touch_resources,
},
{
.name = "wm831x-watchdog",
.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
@ -1325,6 +1169,9 @@ static struct mfd_cell wm8320_devs[] = {
.num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources),
.resources = wm8320_dcdc4_buck_resources,
},
{
.name = "wm831x-clk",
},
{
.name = "wm831x-gpio",
.num_resources = ARRAY_SIZE(wm831x_gpio_resources),
@ -1404,11 +1251,6 @@ static struct mfd_cell wm8320_devs[] = {
.num_resources = ARRAY_SIZE(wm831x_on_resources),
.resources = wm831x_on_resources,
},
{
.name = "wm831x-rtc",
.num_resources = ARRAY_SIZE(wm831x_rtc_resources),
.resources = wm831x_rtc_resources,
},
{
.name = "wm831x-status",
.id = 1,
@ -1428,6 +1270,22 @@ static struct mfd_cell wm8320_devs[] = {
},
};
static struct mfd_cell touch_devs[] = {
{
.name = "wm831x-touch",
.num_resources = ARRAY_SIZE(wm831x_touch_resources),
.resources = wm831x_touch_resources,
},
};
static struct mfd_cell rtc_devs[] = {
{
.name = "wm831x-rtc",
.num_resources = ARRAY_SIZE(wm831x_rtc_resources),
.resources = wm831x_rtc_resources,
},
};
static struct mfd_cell backlight_devs[] = {
{
.name = "wm831x-backlight",
@ -1440,14 +1298,12 @@ static struct mfd_cell backlight_devs[] = {
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
{
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
int rev;
int rev, wm831x_num;
enum wm831x_parent parent;
int ret, i;
mutex_init(&wm831x->io_lock);
mutex_init(&wm831x->key_lock);
mutex_init(&wm831x->auxadc_lock);
init_completion(&wm831x->auxadc_done);
dev_set_drvdata(wm831x->dev, wm831x);
ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
@ -1592,45 +1448,51 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
}
}
/* Multiply by 10 as we have many subdevices of the same type */
if (pdata && pdata->wm831x_num)
wm831x_num = pdata->wm831x_num * 10;
else
wm831x_num = -1;
ret = wm831x_irq_init(wm831x, irq);
if (ret != 0)
goto err;
if (wm831x->irq_base) {
ret = request_threaded_irq(wm831x->irq_base +
WM831X_IRQ_AUXADC_DATA,
NULL, wm831x_auxadc_irq, 0,
"auxadc", wm831x);
if (ret < 0)
dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
ret);
}
wm831x_auxadc_init(wm831x);
/* The core device is up, instantiate the subdevices. */
switch (parent) {
case WM8310:
ret = mfd_add_devices(wm831x->dev, -1,
ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8310_devs, ARRAY_SIZE(wm8310_devs),
NULL, wm831x->irq_base);
break;
case WM8311:
ret = mfd_add_devices(wm831x->dev, -1,
ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8311_devs, ARRAY_SIZE(wm8311_devs),
NULL, wm831x->irq_base);
if (!pdata || !pdata->disable_touch)
mfd_add_devices(wm831x->dev, wm831x_num,
touch_devs, ARRAY_SIZE(touch_devs),
NULL, wm831x->irq_base);
break;
case WM8312:
ret = mfd_add_devices(wm831x->dev, -1,
ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8312_devs, ARRAY_SIZE(wm8312_devs),
NULL, wm831x->irq_base);
if (!pdata || !pdata->disable_touch)
mfd_add_devices(wm831x->dev, wm831x_num,
touch_devs, ARRAY_SIZE(touch_devs),
NULL, wm831x->irq_base);
break;
case WM8320:
case WM8321:
case WM8325:
case WM8326:
ret = mfd_add_devices(wm831x->dev, -1,
ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8320_devs, ARRAY_SIZE(wm8320_devs),
NULL, wm831x->irq_base);
break;
@ -1645,9 +1507,30 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
goto err_irq;
}
/* The RTC can only be used if the 32.768kHz crystal is
* enabled; this can't be controlled by software at runtime.
*/
ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read clock status: %d\n", ret);
goto err_irq;
}
if (ret & WM831X_XTAL_ENA) {
ret = mfd_add_devices(wm831x->dev, wm831x_num,
rtc_devs, ARRAY_SIZE(rtc_devs),
NULL, wm831x->irq_base);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
goto err_irq;
}
} else {
dev_info(wm831x->dev, "32.768kHz clock disabled, no RTC\n");
}
if (pdata && pdata->backlight) {
/* Treat errors as non-critical */
ret = mfd_add_devices(wm831x->dev, -1, backlight_devs,
ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
ARRAY_SIZE(backlight_devs), NULL,
wm831x->irq_base);
if (ret < 0)

View File

@ -348,6 +348,15 @@ static void wm831x_irq_sync_unlock(struct irq_data *data)
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
int i;
for (i = 0; i < ARRAY_SIZE(wm831x->gpio_update); i++) {
if (wm831x->gpio_update[i]) {
wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + i,
WM831X_GPN_INT_MODE | WM831X_GPN_POL,
wm831x->gpio_update[i]);
wm831x->gpio_update[i] = 0;
}
}
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
/* If there's been a change in the mask write it back
* to the hardware. */
@ -387,7 +396,7 @@ static void wm831x_irq_disable(struct irq_data *data)
static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
{
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
int val, irq;
int irq;
irq = data->irq - wm831x->irq_base;
@ -399,22 +408,30 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
return -EINVAL;
}
/* Rebase the IRQ into the GPIO range so we've got a sensible array
* index.
*/
irq -= WM831X_IRQ_GPIO_1;
/* We set the high bit to flag that we need an update; don't
* do the update here as we can be called with the bus lock
* held.
*/
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
val = WM831X_GPN_INT_MODE;
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE;
break;
case IRQ_TYPE_EDGE_RISING:
val = WM831X_GPN_POL;
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
break;
case IRQ_TYPE_EDGE_FALLING:
val = 0;
wm831x->gpio_update[irq] = 0x10000;
break;
default:
return -EINVAL;
}
return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq,
WM831X_GPN_INT_MODE | WM831X_GPN_POL, val);
return 0;
}
static struct irq_chip wm831x_irq_chip = {
@ -432,7 +449,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
{
struct wm831x *wm831x = data;
unsigned int i;
int primary;
int primary, status_addr;
int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
int read[WM831X_NUM_IRQ_REGS] = { 0 };
int *status;
@ -467,8 +484,9 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
/* Hopefully there should only be one register to read
* each time otherwise we ought to do a block read. */
if (!read[offset]) {
*status = wm831x_reg_read(wm831x,
irq_data_to_status_reg(&wm831x_irqs[i]));
status_addr = irq_data_to_status_reg(&wm831x_irqs[i]);
*status = wm831x_reg_read(wm831x, status_addr);
if (*status < 0) {
dev_err(wm831x->dev,
"Failed to read IRQ status: %d\n",
@ -477,26 +495,21 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
}
read[offset] = 1;
/* Ignore any bits that we don't think are masked */
*status &= ~wm831x->irq_masks_cur[offset];
/* Acknowledge now so we don't miss
* notifications while we handle.
*/
wm831x_reg_write(wm831x, status_addr, *status);
}
/* Report it if it isn't masked, or forget the status. */
if ((*status & ~wm831x->irq_masks_cur[offset])
& wm831x_irqs[i].mask)
if (*status & wm831x_irqs[i].mask)
handle_nested_irq(wm831x->irq_base + i);
else
*status &= ~wm831x_irqs[i].mask;
}
out:
/* Touchscreen interrupts are handled specially in the driver */
status_regs[0] &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT);
for (i = 0; i < ARRAY_SIZE(status_regs); i++) {
if (status_regs[i])
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i,
status_regs[i]);
}
return IRQ_HANDLED;
}
@ -515,13 +528,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
0xffff);
}
if (!pdata || !pdata->irq_base) {
dev_err(wm831x->dev,
"No interrupt base specified, no interrupts\n");
/* Try to dynamically allocate IRQs if no base is specified */
if (!pdata || !pdata->irq_base)
wm831x->irq_base = -1;
else
wm831x->irq_base = pdata->irq_base;
wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0,
WM831X_NUM_IRQS, 0);
if (wm831x->irq_base < 0) {
dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
wm831x->irq_base);
wm831x->irq_base = 0;
return 0;
}
if (pdata->irq_cmos)
if (pdata && pdata->irq_cmos)
i = 0;
else
i = WM831X_IRQ_OD;
@ -541,7 +563,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
}
wm831x->irq = irq;
wm831x->irq_base = pdata->irq_base;
/* Register them with genirq */
for (cur_irq = wm831x->irq_base;

View File

@ -473,17 +473,13 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq,
{
int ret, cur_irq, i;
int flags = IRQF_ONESHOT;
int irq_base = -1;
if (!irq) {
dev_warn(wm8350->dev, "No interrupt support, no core IRQ\n");
return 0;
}
if (!pdata || !pdata->irq_base) {
dev_warn(wm8350->dev, "No interrupt support, no IRQ base\n");
return 0;
}
/* Mask top level interrupts */
wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
@ -502,7 +498,17 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq,
wm8350->chip_irq = irq;
wm8350->irq_base = pdata->irq_base;
if (pdata->irq_high) {
if (pdata && pdata->irq_base > 0)
irq_base = pdata->irq_base;
wm8350->irq_base = irq_alloc_descs(irq_base, 0, ARRAY_SIZE(wm8350_irqs), 0);
if (wm8350->irq_base < 0) {
dev_warn(wm8350->dev, "Allocating irqs failed with %d\n",
wm8350->irq_base);
return 0;
}
if (pdata && pdata->irq_high) {
flags |= IRQF_TRIGGER_HIGH;
wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,

View File

@ -316,7 +316,7 @@ static int wm8994_suspend(struct device *dev)
static int wm8994_resume(struct device *dev)
{
struct wm8994 *wm8994 = dev_get_drvdata(dev);
int ret;
int ret, i;
/* We may have lied to the PM core about suspending */
if (!wm8994->suspended)
@ -329,10 +329,16 @@ static int wm8994_resume(struct device *dev)
return ret;
}
ret = wm8994_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK,
WM8994_NUM_IRQ_REGS * 2, &wm8994->irq_masks_cur);
if (ret < 0)
dev_err(dev, "Failed to restore interrupt masks: %d\n", ret);
/* Write register at a time as we use the cache on the CPU so store
* it in native endian.
*/
for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) {
ret = wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK
+ i, wm8994->irq_masks_cur[i]);
if (ret < 0)
dev_err(dev, "Failed to restore interrupt masks: %d\n",
ret);
}
ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
&wm8994->ldo_regs);
@ -403,7 +409,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
break;
default:
BUG();
return -EINVAL;
goto err;
}
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
@ -425,7 +431,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
break;
default:
BUG();
return -EINVAL;
goto err;
}
ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
@ -476,13 +482,18 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
goto err_enable;
}
switch (ret) {
case 0:
case 1:
if (wm8994->type == WM8994)
switch (wm8994->type) {
case WM8994:
switch (ret) {
case 0:
case 1:
dev_warn(wm8994->dev,
"revision %c not fully supported\n",
'A' + ret);
break;
default:
break;
}
break;
default:
break;

View File

@ -231,12 +231,6 @@ static irqreturn_t wm8994_irq_thread(int irq, void *data)
status[i] &= ~wm8994->irq_masks_cur[i];
}
/* Report */
for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
handle_nested_irq(wm8994->irq_base + i);
}
/* Ack any unmasked IRQs */
for (i = 0; i < ARRAY_SIZE(status); i++) {
if (status[i])
@ -244,6 +238,12 @@ static irqreturn_t wm8994_irq_thread(int irq, void *data)
status[i]);
}
/* Report */
for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
handle_nested_irq(wm8994->irq_base + i);
}
return IRQ_HANDLED;
}

View File

@ -249,6 +249,12 @@ config REGULATOR_TPS6507X
three step-down converters and two general-purpose LDO voltage regulators.
It supports TI's software based Class-2 SmartReflex implementation.
config REGULATOR_TPS65912
tristate "TI TPS65912 Power regulator"
depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
help
This driver supports TPS65912 voltage regulator chip.
config REGULATOR_88PM8607
bool "Marvell 88PM8607 Power regulators"
depends on MFD_88PM860X=y
@ -304,5 +310,12 @@ config REGULATOR_TPS65910
help
This driver supports TPS65910 voltage regulator chips.
config REGULATOR_AAT2870
tristate "AnalogicTech AAT2870 Regulators"
depends on MFD_AAT2870_CORE
help
If you have a AnalogicTech AAT2870 say Y to enable the
regulator driver.
endif

View File

@ -38,10 +38,12 @@ obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o
obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG

View File

@ -0,0 +1,232 @@
/*
* linux/drivers/regulator/aat2870-regulator.c
*
* Copyright (c) 2011, NVIDIA Corporation.
* Author: Jin Park <jinyoungp@nvidia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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 St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/aat2870.h>
struct aat2870_regulator {
struct platform_device *pdev;
struct regulator_desc desc;
const int *voltages; /* uV */
int min_uV;
int max_uV;
u8 enable_addr;
u8 enable_shift;
u8 enable_mask;
u8 voltage_addr;
u8 voltage_shift;
u8 voltage_mask;
};
static int aat2870_ldo_list_voltage(struct regulator_dev *rdev,
unsigned selector)
{
struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
return ri->voltages[selector];
}
static int aat2870_ldo_set_voltage_sel(struct regulator_dev *rdev,
unsigned selector)
{
struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
return aat2870->update(aat2870, ri->voltage_addr, ri->voltage_mask,
(selector << ri->voltage_shift) & ri->voltage_mask);
}
static int aat2870_ldo_get_voltage_sel(struct regulator_dev *rdev)
{
struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
u8 val;
int ret;
ret = aat2870->read(aat2870, ri->voltage_addr, &val);
if (ret)
return ret;
return (val & ri->voltage_mask) >> ri->voltage_shift;
}
static int aat2870_ldo_enable(struct regulator_dev *rdev)
{
struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask,
ri->enable_mask);
}
static int aat2870_ldo_disable(struct regulator_dev *rdev)
{
struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, 0);
}
static int aat2870_ldo_is_enabled(struct regulator_dev *rdev)
{
struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
u8 val;
int ret;
ret = aat2870->read(aat2870, ri->enable_addr, &val);
if (ret)
return ret;
return val & ri->enable_mask ? 1 : 0;
}
static struct regulator_ops aat2870_ldo_ops = {
.list_voltage = aat2870_ldo_list_voltage,
.set_voltage_sel = aat2870_ldo_set_voltage_sel,
.get_voltage_sel = aat2870_ldo_get_voltage_sel,
.enable = aat2870_ldo_enable,
.disable = aat2870_ldo_disable,
.is_enabled = aat2870_ldo_is_enabled,
};
static const int aat2870_ldo_voltages[] = {
1200000, 1300000, 1500000, 1600000,
1800000, 2000000, 2200000, 2500000,
2600000, 2700000, 2800000, 2900000,
3000000, 3100000, 3200000, 3300000,
};
#define AAT2870_LDO(ids) \
{ \
.desc = { \
.name = #ids, \
.id = AAT2870_ID_##ids, \
.n_voltages = ARRAY_SIZE(aat2870_ldo_voltages), \
.ops = &aat2870_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
}, \
.voltages = aat2870_ldo_voltages, \
.min_uV = 1200000, \
.max_uV = 3300000, \
}
static struct aat2870_regulator aat2870_regulators[] = {
AAT2870_LDO(LDOA),
AAT2870_LDO(LDOB),
AAT2870_LDO(LDOC),
AAT2870_LDO(LDOD),
};
static struct aat2870_regulator *aat2870_get_regulator(int id)
{
struct aat2870_regulator *ri = NULL;
int i;
for (i = 0; i < ARRAY_SIZE(aat2870_regulators); i++) {
ri = &aat2870_regulators[i];
if (ri->desc.id == id)
break;
}
if (!ri)
return NULL;
ri->enable_addr = AAT2870_LDO_EN;
ri->enable_shift = id - AAT2870_ID_LDOA;
ri->enable_mask = 0x1 << ri->enable_shift;
ri->voltage_addr = (id - AAT2870_ID_LDOA) / 2 ?
AAT2870_LDO_CD : AAT2870_LDO_AB;
ri->voltage_shift = (id - AAT2870_ID_LDOA) % 2 ? 0 : 4;
ri->voltage_mask = 0xF << ri->voltage_shift;
return ri;
}
static int aat2870_regulator_probe(struct platform_device *pdev)
{
struct aat2870_regulator *ri;
struct regulator_dev *rdev;
ri = aat2870_get_regulator(pdev->id);
if (!ri) {
dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id);
return -EINVAL;
}
ri->pdev = pdev;
rdev = regulator_register(&ri->desc, &pdev->dev,
pdev->dev.platform_data, ri);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "Failed to register regulator %s\n",
ri->desc.name);
return PTR_ERR(rdev);
}
platform_set_drvdata(pdev, rdev);
return 0;
}
static int __devexit aat2870_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
regulator_unregister(rdev);
return 0;
}
static struct platform_driver aat2870_regulator_driver = {
.driver = {
.name = "aat2870-regulator",
.owner = THIS_MODULE,
},
.probe = aat2870_regulator_probe,
.remove = __devexit_p(aat2870_regulator_remove),
};
static int __init aat2870_regulator_init(void)
{
return platform_driver_register(&aat2870_regulator_driver);
}
subsys_initcall(aat2870_regulator_init);
static void __exit aat2870_regulator_exit(void)
{
platform_driver_unregister(&aat2870_regulator_driver);
}
module_exit(aat2870_regulator_exit);
MODULE_DESCRIPTION("AnalogicTech AAT2870 Regulator");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");

View File

@ -0,0 +1,800 @@
/*
* tps65912.c -- TI tps65912
*
* Copyright 2011 Texas Instruments Inc.
*
* Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
*
* 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 driver is based on wm8350 implementation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/mfd/tps65912.h>
/* DCDC's */
#define TPS65912_REG_DCDC1 0
#define TPS65912_REG_DCDC2 1
#define TPS65912_REG_DCDC3 2
#define TPS65912_REG_DCDC4 3
/* LDOs */
#define TPS65912_REG_LDO1 4
#define TPS65912_REG_LDO2 5
#define TPS65912_REG_LDO3 6
#define TPS65912_REG_LDO4 7
#define TPS65912_REG_LDO5 8
#define TPS65912_REG_LDO6 9
#define TPS65912_REG_LDO7 10
#define TPS65912_REG_LDO8 11
#define TPS65912_REG_LDO9 12
#define TPS65912_REG_LDO10 13
#define TPS65912_MAX_REG_ID TPS65912_REG_LDO_10
/* Number of step-down converters available */
#define TPS65912_NUM_DCDC 4
/* Number of LDO voltage regulators available */
#define TPS65912_NUM_LDO 10
/* Number of total regulators available */
#define TPS65912_NUM_REGULATOR (TPS65912_NUM_DCDC + TPS65912_NUM_LDO)
#define TPS65912_REG_ENABLED 0x80
#define OP_SELREG_MASK 0x40
#define OP_SELREG_SHIFT 6
struct tps_info {
const char *name;
};
static struct tps_info tps65912_regs[] = {
{
.name = "DCDC1",
},
{
.name = "DCDC2",
},
{
.name = "DCDC3",
},
{
.name = "DCDC4",
},
{
.name = "LDO1",
},
{
.name = "LDO2",
},
{
.name = "LDO3",
},
{
.name = "LDO4",
},
{
.name = "LDO5",
},
{
.name = "LDO6",
},
{
.name = "LDO7",
},
{
.name = "LDO8",
},
{
.name = "LDO9",
},
{
.name = "LDO10",
},
};
struct tps65912_reg {
struct regulator_desc desc[TPS65912_NUM_REGULATOR];
struct tps65912 *mfd;
struct regulator_dev *rdev[TPS65912_NUM_REGULATOR];
struct tps_info *info[TPS65912_NUM_REGULATOR];
/* for read/write access */
struct mutex io_lock;
int mode;
int (*get_ctrl_reg)(int);
int dcdc1_range;
int dcdc2_range;
int dcdc3_range;
int dcdc4_range;
int pwm_mode_reg;
int eco_reg;
};
static int tps65912_get_range(struct tps65912_reg *pmic, int id)
{
struct tps65912 *mfd = pmic->mfd;
if (id > TPS65912_REG_DCDC4)
return 0;
switch (id) {
case TPS65912_REG_DCDC1:
pmic->dcdc1_range = tps65912_reg_read(mfd,
TPS65912_DCDC1_LIMIT);
if (pmic->dcdc1_range < 0)
return pmic->dcdc1_range;
pmic->dcdc1_range = (pmic->dcdc1_range &
DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
return pmic->dcdc1_range;
case TPS65912_REG_DCDC2:
pmic->dcdc2_range = tps65912_reg_read(mfd,
TPS65912_DCDC2_LIMIT);
if (pmic->dcdc2_range < 0)
return pmic->dcdc2_range;
pmic->dcdc2_range = (pmic->dcdc2_range &
DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
return pmic->dcdc2_range;
case TPS65912_REG_DCDC3:
pmic->dcdc3_range = tps65912_reg_read(mfd,
TPS65912_DCDC3_LIMIT);
if (pmic->dcdc3_range < 0)
return pmic->dcdc3_range;
pmic->dcdc3_range = (pmic->dcdc3_range &
DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
return pmic->dcdc3_range;
case TPS65912_REG_DCDC4:
pmic->dcdc4_range = tps65912_reg_read(mfd,
TPS65912_DCDC4_LIMIT);
if (pmic->dcdc4_range < 0)
return pmic->dcdc4_range;
pmic->dcdc4_range = (pmic->dcdc4_range &
DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
return pmic->dcdc4_range;
default:
return 0;
}
}
static unsigned long tps65912_vsel_to_uv_range0(u8 vsel)
{
unsigned long uv;
uv = ((vsel * 12500) + 500000);
return uv;
}
static unsigned long tps65912_vsel_to_uv_range1(u8 vsel)
{
unsigned long uv;
uv = ((vsel * 12500) + 700000);
return uv;
}
static unsigned long tps65912_vsel_to_uv_range2(u8 vsel)
{
unsigned long uv;
uv = ((vsel * 25000) + 500000);
return uv;
}
static unsigned long tps65912_vsel_to_uv_range3(u8 vsel)
{
unsigned long uv;
if (vsel == 0x3f)
uv = 3800000;
else
uv = ((vsel * 50000) + 500000);
return uv;
}
static unsigned long tps65912_vsel_to_uv_ldo(u8 vsel)
{
unsigned long uv = 0;
if (vsel <= 32)
uv = ((vsel * 25000) + 800000);
else if (vsel > 32 && vsel <= 60)
uv = (((vsel - 32) * 50000) + 1600000);
else if (vsel > 60)
uv = (((vsel - 60) * 100000) + 3000000);
return uv;
}
static int tps65912_get_ctrl_register(int id)
{
switch (id) {
case TPS65912_REG_DCDC1:
return TPS65912_DCDC1_AVS;
case TPS65912_REG_DCDC2:
return TPS65912_DCDC2_AVS;
case TPS65912_REG_DCDC3:
return TPS65912_DCDC3_AVS;
case TPS65912_REG_DCDC4:
return TPS65912_DCDC4_AVS;
case TPS65912_REG_LDO1:
return TPS65912_LDO1_AVS;
case TPS65912_REG_LDO2:
return TPS65912_LDO2_AVS;
case TPS65912_REG_LDO3:
return TPS65912_LDO3_AVS;
case TPS65912_REG_LDO4:
return TPS65912_LDO4_AVS;
case TPS65912_REG_LDO5:
return TPS65912_LDO5;
case TPS65912_REG_LDO6:
return TPS65912_LDO6;
case TPS65912_REG_LDO7:
return TPS65912_LDO7;
case TPS65912_REG_LDO8:
return TPS65912_LDO8;
case TPS65912_REG_LDO9:
return TPS65912_LDO9;
case TPS65912_REG_LDO10:
return TPS65912_LDO10;
default:
return -EINVAL;
}
}
static int tps65912_get_dcdc_sel_register(struct tps65912_reg *pmic, int id)
{
struct tps65912 *mfd = pmic->mfd;
int opvsel = 0, sr = 0;
u8 reg = 0;
if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_DCDC4)
return -EINVAL;
switch (id) {
case TPS65912_REG_DCDC1:
opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP);
sr = ((opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT);
if (sr)
reg = TPS65912_DCDC1_AVS;
else
reg = TPS65912_DCDC1_OP;
break;
case TPS65912_REG_DCDC2:
opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP);
sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
if (sr)
reg = TPS65912_DCDC2_AVS;
else
reg = TPS65912_DCDC2_OP;
break;
case TPS65912_REG_DCDC3:
opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP);
sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
if (sr)
reg = TPS65912_DCDC3_AVS;
else
reg = TPS65912_DCDC3_OP;
break;
case TPS65912_REG_DCDC4:
opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP);
sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
if (sr)
reg = TPS65912_DCDC4_AVS;
else
reg = TPS65912_DCDC4_OP;
break;
}
return reg;
}
static int tps65912_get_ldo_sel_register(struct tps65912_reg *pmic, int id)
{
struct tps65912 *mfd = pmic->mfd;
int opvsel = 0, sr = 0;
u8 reg = 0;
if (id < TPS65912_REG_LDO1 || id > TPS65912_REG_LDO10)
return -EINVAL;
switch (id) {
case TPS65912_REG_LDO1:
opvsel = tps65912_reg_read(mfd, TPS65912_LDO1_OP);
sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
if (sr)
reg = TPS65912_LDO1_AVS;
else
reg = TPS65912_LDO1_OP;
break;
case TPS65912_REG_LDO2:
opvsel = tps65912_reg_read(mfd, TPS65912_LDO2_OP);
sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
if (sr)
reg = TPS65912_LDO2_AVS;
else
reg = TPS65912_LDO2_OP;
break;
case TPS65912_REG_LDO3:
opvsel = tps65912_reg_read(mfd, TPS65912_LDO3_OP);
sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
if (sr)
reg = TPS65912_LDO3_AVS;
else
reg = TPS65912_LDO3_OP;
break;
case TPS65912_REG_LDO4:
opvsel = tps65912_reg_read(mfd, TPS65912_LDO4_OP);
sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
if (sr)
reg = TPS65912_LDO4_AVS;
else
reg = TPS65912_LDO4_OP;
break;
case TPS65912_REG_LDO5:
reg = TPS65912_LDO5;
break;
case TPS65912_REG_LDO6:
reg = TPS65912_LDO6;
break;
case TPS65912_REG_LDO7:
reg = TPS65912_LDO7;
break;
case TPS65912_REG_LDO8:
reg = TPS65912_LDO8;
break;
case TPS65912_REG_LDO9:
reg = TPS65912_LDO9;
break;
case TPS65912_REG_LDO10:
reg = TPS65912_LDO10;
break;
}
return reg;
}
static int tps65912_get_mode_regiters(struct tps65912_reg *pmic, int id)
{
switch (id) {
case TPS65912_REG_DCDC1:
pmic->pwm_mode_reg = TPS65912_DCDC1_CTRL;
pmic->eco_reg = TPS65912_DCDC1_AVS;
break;
case TPS65912_REG_DCDC2:
pmic->pwm_mode_reg = TPS65912_DCDC2_CTRL;
pmic->eco_reg = TPS65912_DCDC2_AVS;
break;
case TPS65912_REG_DCDC3:
pmic->pwm_mode_reg = TPS65912_DCDC3_CTRL;
pmic->eco_reg = TPS65912_DCDC3_AVS;
break;
case TPS65912_REG_DCDC4:
pmic->pwm_mode_reg = TPS65912_DCDC4_CTRL;
pmic->eco_reg = TPS65912_DCDC4_AVS;
break;
default:
return -EINVAL;
}
return 0;
}
static int tps65912_reg_is_enabled(struct regulator_dev *dev)
{
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
struct tps65912 *mfd = pmic->mfd;
int reg, value, id = rdev_get_id(dev);
if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10)
return -EINVAL;
reg = pmic->get_ctrl_reg(id);
if (reg < 0)
return reg;
value = tps65912_reg_read(mfd, reg);
if (value < 0)
return value;
return value & TPS65912_REG_ENABLED;
}
static int tps65912_reg_enable(struct regulator_dev *dev)
{
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
struct tps65912 *mfd = pmic->mfd;
int id = rdev_get_id(dev);
int reg;
if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10)
return -EINVAL;
reg = pmic->get_ctrl_reg(id);
if (reg < 0)
return reg;
return tps65912_set_bits(mfd, reg, TPS65912_REG_ENABLED);
}
static int tps65912_reg_disable(struct regulator_dev *dev)
{
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
struct tps65912 *mfd = pmic->mfd;
int id = rdev_get_id(dev), reg;
reg = pmic->get_ctrl_reg(id);
if (reg < 0)
return reg;
return tps65912_clear_bits(mfd, reg, TPS65912_REG_ENABLED);
}
static int tps65912_set_mode(struct regulator_dev *dev, unsigned int mode)
{
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
struct tps65912 *mfd = pmic->mfd;
int pwm_mode, eco, id = rdev_get_id(dev);
tps65912_get_mode_regiters(pmic, id);
pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg);
eco = tps65912_reg_read(mfd, pmic->eco_reg);
pwm_mode &= DCDCCTRL_DCDC_MODE_MASK;
eco &= DCDC_AVS_ECO_MASK;
switch (mode) {
case REGULATOR_MODE_FAST:
/* Verify if mode alredy set */
if (pwm_mode && !eco)
break;
tps65912_set_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK);
tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK);
break;
case REGULATOR_MODE_NORMAL:
case REGULATOR_MODE_IDLE:
if (!pwm_mode && !eco)
break;
tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK);
tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK);
break;
case REGULATOR_MODE_STANDBY:
if (!pwm_mode && eco)
break;
tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK);
tps65912_set_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK);
break;
default:
return -EINVAL;
}
return 0;
}
static unsigned int tps65912_get_mode(struct regulator_dev *dev)
{
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
struct tps65912 *mfd = pmic->mfd;
int pwm_mode, eco, mode = 0, id = rdev_get_id(dev);
tps65912_get_mode_regiters(pmic, id);
pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg);
eco = tps65912_reg_read(mfd, pmic->eco_reg);
pwm_mode &= DCDCCTRL_DCDC_MODE_MASK;
eco &= DCDC_AVS_ECO_MASK;
if (pwm_mode && !eco)
mode = REGULATOR_MODE_FAST;
else if (!pwm_mode && !eco)
mode = REGULATOR_MODE_NORMAL;
else if (!pwm_mode && eco)
mode = REGULATOR_MODE_STANDBY;
return mode;
}
static int tps65912_get_voltage_dcdc(struct regulator_dev *dev)
{
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
struct tps65912 *mfd = pmic->mfd;
int id = rdev_get_id(dev), voltage = 0, range;
int opvsel = 0, avsel = 0, sr, vsel;
switch (id) {
case TPS65912_REG_DCDC1:
opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP);
avsel = tps65912_reg_read(mfd, TPS65912_DCDC1_AVS);
range = pmic->dcdc1_range;
break;
case TPS65912_REG_DCDC2:
opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP);
avsel = tps65912_reg_read(mfd, TPS65912_DCDC2_AVS);
range = pmic->dcdc2_range;
break;
case TPS65912_REG_DCDC3:
opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP);
avsel = tps65912_reg_read(mfd, TPS65912_DCDC3_AVS);
range = pmic->dcdc3_range;
break;
case TPS65912_REG_DCDC4:
opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP);
avsel = tps65912_reg_read(mfd, TPS65912_DCDC4_AVS);
range = pmic->dcdc4_range;
break;
default:
return -EINVAL;
}
sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
if (sr)
vsel = avsel;
else
vsel = opvsel;
vsel &= 0x3F;
switch (range) {
case 0:
/* 0.5 - 1.2875V in 12.5mV steps */
voltage = tps65912_vsel_to_uv_range0(vsel);
break;
case 1:
/* 0.7 - 1.4875V in 12.5mV steps */
voltage = tps65912_vsel_to_uv_range1(vsel);
break;
case 2:
/* 0.5 - 2.075V in 25mV steps */
voltage = tps65912_vsel_to_uv_range2(vsel);
break;
case 3:
/* 0.5 - 3.8V in 50mV steps */
voltage = tps65912_vsel_to_uv_range3(vsel);
break;
}
return voltage;
}
static int tps65912_set_voltage_dcdc(struct regulator_dev *dev,
unsigned selector)
{
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
struct tps65912 *mfd = pmic->mfd;
int id = rdev_get_id(dev);
int value;
u8 reg;
reg = tps65912_get_dcdc_sel_register(pmic, id);
value = tps65912_reg_read(mfd, reg);
value &= 0xC0;
return tps65912_reg_write(mfd, reg, selector | value);
}
static int tps65912_get_voltage_ldo(struct regulator_dev *dev)
{
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
struct tps65912 *mfd = pmic->mfd;
int id = rdev_get_id(dev);
int vsel = 0;
u8 reg;
reg = tps65912_get_ldo_sel_register(pmic, id);
vsel = tps65912_reg_read(mfd, reg);
vsel &= 0x3F;
return tps65912_vsel_to_uv_ldo(vsel);
}
static int tps65912_set_voltage_ldo(struct regulator_dev *dev,
unsigned selector)
{
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
struct tps65912 *mfd = pmic->mfd;
int id = rdev_get_id(dev), reg, value;
reg = tps65912_get_ldo_sel_register(pmic, id);
value = tps65912_reg_read(mfd, reg);
value &= 0xC0;
return tps65912_reg_write(mfd, reg, selector | value);
}
static int tps65912_list_voltage_dcdc(struct regulator_dev *dev,
unsigned selector)
{
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
int range, voltage = 0, id = rdev_get_id(dev);
switch (id) {
case TPS65912_REG_DCDC1:
range = pmic->dcdc1_range;
break;
case TPS65912_REG_DCDC2:
range = pmic->dcdc2_range;
break;
case TPS65912_REG_DCDC3:
range = pmic->dcdc3_range;
break;
case TPS65912_REG_DCDC4:
range = pmic->dcdc4_range;
break;
default:
return -EINVAL;
}
switch (range) {
case 0:
/* 0.5 - 1.2875V in 12.5mV steps */
voltage = tps65912_vsel_to_uv_range0(selector);
break;
case 1:
/* 0.7 - 1.4875V in 12.5mV steps */
voltage = tps65912_vsel_to_uv_range1(selector);
break;
case 2:
/* 0.5 - 2.075V in 25mV steps */
voltage = tps65912_vsel_to_uv_range2(selector);
break;
case 3:
/* 0.5 - 3.8V in 50mV steps */
voltage = tps65912_vsel_to_uv_range3(selector);
break;
}
return voltage;
}
static int tps65912_list_voltage_ldo(struct regulator_dev *dev,
unsigned selector)
{
int ldo = rdev_get_id(dev);
if (ldo < TPS65912_REG_LDO1 || ldo > TPS65912_REG_LDO10)
return -EINVAL;
return tps65912_vsel_to_uv_ldo(selector);
}
/* Operations permitted on DCDCx */
static struct regulator_ops tps65912_ops_dcdc = {
.is_enabled = tps65912_reg_is_enabled,
.enable = tps65912_reg_enable,
.disable = tps65912_reg_disable,
.set_mode = tps65912_set_mode,
.get_mode = tps65912_get_mode,
.get_voltage = tps65912_get_voltage_dcdc,
.set_voltage_sel = tps65912_set_voltage_dcdc,
.list_voltage = tps65912_list_voltage_dcdc,
};
/* Operations permitted on LDOx */
static struct regulator_ops tps65912_ops_ldo = {
.is_enabled = tps65912_reg_is_enabled,
.enable = tps65912_reg_enable,
.disable = tps65912_reg_disable,
.get_voltage = tps65912_get_voltage_ldo,
.set_voltage_sel = tps65912_set_voltage_ldo,
.list_voltage = tps65912_list_voltage_ldo,
};
static __devinit int tps65912_probe(struct platform_device *pdev)
{
struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
struct tps_info *info;
struct regulator_init_data *reg_data;
struct regulator_dev *rdev;
struct tps65912_reg *pmic;
struct tps65912_board *pmic_plat_data;
int i, err;
pmic_plat_data = dev_get_platdata(tps65912->dev);
if (!pmic_plat_data)
return -EINVAL;
reg_data = pmic_plat_data->tps65912_pmic_init_data;
pmic = kzalloc(sizeof(*pmic), GFP_KERNEL);
if (!pmic)
return -ENOMEM;
mutex_init(&pmic->io_lock);
pmic->mfd = tps65912;
platform_set_drvdata(pdev, pmic);
pmic->get_ctrl_reg = &tps65912_get_ctrl_register;
info = tps65912_regs;
for (i = 0; i < TPS65912_NUM_REGULATOR; i++, info++, reg_data++) {
int range = 0;
/* Register the regulators */
pmic->info[i] = info;
pmic->desc[i].name = info->name;
pmic->desc[i].id = i;
pmic->desc[i].n_voltages = 64;
pmic->desc[i].ops = (i > TPS65912_REG_DCDC4 ?
&tps65912_ops_ldo : &tps65912_ops_dcdc);
pmic->desc[i].type = REGULATOR_VOLTAGE;
pmic->desc[i].owner = THIS_MODULE;
range = tps65912_get_range(pmic, i);
rdev = regulator_register(&pmic->desc[i],
tps65912->dev, reg_data, pmic);
if (IS_ERR(rdev)) {
dev_err(tps65912->dev,
"failed to register %s regulator\n",
pdev->name);
err = PTR_ERR(rdev);
goto err;
}
/* Save regulator for cleanup */
pmic->rdev[i] = rdev;
}
return 0;
err:
while (--i >= 0)
regulator_unregister(pmic->rdev[i]);
kfree(pmic);
return err;
}
static int __devexit tps65912_remove(struct platform_device *pdev)
{
struct tps65912_reg *tps65912_reg = platform_get_drvdata(pdev);
int i;
for (i = 0; i < TPS65912_NUM_REGULATOR; i++)
regulator_unregister(tps65912_reg->rdev[i]);
kfree(tps65912_reg);
return 0;
}
static struct platform_driver tps65912_driver = {
.driver = {
.name = "tps65912-pmic",
.owner = THIS_MODULE,
},
.probe = tps65912_probe,
.remove = __devexit_p(tps65912_remove),
};
/**
* tps65912_init
*
* Module init function
*/
static int __init tps65912_init(void)
{
return platform_driver_register(&tps65912_driver);
}
subsys_initcall(tps65912_init);
/**
* tps65912_cleanup
*
* Module exit function
*/
static void __exit tps65912_cleanup(void)
{
platform_driver_unregister(&tps65912_driver);
}
module_exit(tps65912_cleanup);
MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>");
MODULE_DESCRIPTION("TPS65912 voltage regulator driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:tps65912-pmic");

View File

@ -335,6 +335,13 @@ config BACKLIGHT_PCF50633
If you have a backlight driven by a NXP PCF50633 MFD, say Y here to
enable its driver.
config BACKLIGHT_AAT2870
bool "AnalogicTech AAT2870 Backlight"
depends on BACKLIGHT_CLASS_DEVICE && MFD_AAT2870_CORE
help
If you have a AnalogicTech AAT2870 say Y to enable the
backlight driver.
endif # BACKLIGHT_CLASS_DEVICE
endif # BACKLIGHT_LCD_SUPPORT

View File

@ -38,4 +38,5 @@ obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o
obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o
obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o

View File

@ -0,0 +1,246 @@
/*
* linux/drivers/video/backlight/aat2870_bl.c
*
* Copyright (c) 2011, NVIDIA Corporation.
* Author: Jin Park <jinyoungp@nvidia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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 St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/mfd/aat2870.h>
struct aat2870_bl_driver_data {
struct platform_device *pdev;
struct backlight_device *bd;
int channels;
int max_current;
int brightness; /* current brightness */
};
static inline int aat2870_brightness(struct aat2870_bl_driver_data *aat2870_bl,
int brightness)
{
struct backlight_device *bd = aat2870_bl->bd;
int val;
val = brightness * aat2870_bl->max_current;
val /= bd->props.max_brightness;
return val;
}
static inline int aat2870_bl_enable(struct aat2870_bl_driver_data *aat2870_bl)
{
struct aat2870_data *aat2870
= dev_get_drvdata(aat2870_bl->pdev->dev.parent);
return aat2870->write(aat2870, AAT2870_BL_CH_EN,
(u8)aat2870_bl->channels);
}
static inline int aat2870_bl_disable(struct aat2870_bl_driver_data *aat2870_bl)
{
struct aat2870_data *aat2870
= dev_get_drvdata(aat2870_bl->pdev->dev.parent);
return aat2870->write(aat2870, AAT2870_BL_CH_EN, 0x0);
}
static int aat2870_bl_get_brightness(struct backlight_device *bd)
{
return bd->props.brightness;
}
static int aat2870_bl_update_status(struct backlight_device *bd)
{
struct aat2870_bl_driver_data *aat2870_bl = dev_get_drvdata(&bd->dev);
struct aat2870_data *aat2870 =
dev_get_drvdata(aat2870_bl->pdev->dev.parent);
int brightness = bd->props.brightness;
int ret;
if ((brightness < 0) || (bd->props.max_brightness < brightness)) {
dev_err(&bd->dev, "invalid brightness, %d\n", brightness);
return -EINVAL;
}
dev_dbg(&bd->dev, "brightness=%d, power=%d, state=%d\n",
bd->props.brightness, bd->props.power, bd->props.state);
if ((bd->props.power != FB_BLANK_UNBLANK) ||
(bd->props.state & BL_CORE_FBBLANK) ||
(bd->props.state & BL_CORE_SUSPENDED))
brightness = 0;
ret = aat2870->write(aat2870, AAT2870_BLM,
(u8)aat2870_brightness(aat2870_bl, brightness));
if (ret < 0)
return ret;
if (brightness == 0) {
ret = aat2870_bl_disable(aat2870_bl);
if (ret < 0)
return ret;
} else if (aat2870_bl->brightness == 0) {
ret = aat2870_bl_enable(aat2870_bl);
if (ret < 0)
return ret;
}
aat2870_bl->brightness = brightness;
return 0;
}
static int aat2870_bl_check_fb(struct backlight_device *bd, struct fb_info *fi)
{
return 1;
}
static const struct backlight_ops aat2870_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.get_brightness = aat2870_bl_get_brightness,
.update_status = aat2870_bl_update_status,
.check_fb = aat2870_bl_check_fb,
};
static int aat2870_bl_probe(struct platform_device *pdev)
{
struct aat2870_bl_platform_data *pdata = pdev->dev.platform_data;
struct aat2870_bl_driver_data *aat2870_bl;
struct backlight_device *bd;
struct backlight_properties props;
int ret = 0;
if (!pdata) {
dev_err(&pdev->dev, "No platform data\n");
ret = -ENXIO;
goto out;
}
if (pdev->id != AAT2870_ID_BL) {
dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id);
ret = -EINVAL;
goto out;
}
aat2870_bl = kzalloc(sizeof(struct aat2870_bl_driver_data), GFP_KERNEL);
if (!aat2870_bl) {
dev_err(&pdev->dev,
"Failed to allocate memory for aat2870 backlight\n");
ret = -ENOMEM;
goto out;
}
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
bd = backlight_device_register("aat2870-backlight", &pdev->dev,
aat2870_bl, &aat2870_bl_ops, &props);
if (!bd) {
dev_err(&pdev->dev,
"Failed allocate memory for backlight device\n");
ret = -ENOMEM;
goto out_kfree;
}
aat2870_bl->pdev = pdev;
platform_set_drvdata(pdev, aat2870_bl);
aat2870_bl->bd = bd;
if (pdata->channels > 0)
aat2870_bl->channels = pdata->channels;
else
aat2870_bl->channels = AAT2870_BL_CH_ALL;
if (pdata->max_brightness > 0)
aat2870_bl->max_current = pdata->max_current;
else
aat2870_bl->max_current = AAT2870_CURRENT_27_9;
if (pdata->max_brightness > 0)
bd->props.max_brightness = pdata->max_brightness;
else
bd->props.max_brightness = 255;
aat2870_bl->brightness = 0;
bd->props.power = FB_BLANK_UNBLANK;
bd->props.brightness = bd->props.max_brightness;
ret = aat2870_bl_update_status(bd);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to initialize\n");
goto out_bl_dev_unregister;
}
return 0;
out_bl_dev_unregister:
backlight_device_unregister(bd);
out_kfree:
kfree(aat2870_bl);
out:
return ret;
}
static int aat2870_bl_remove(struct platform_device *pdev)
{
struct aat2870_bl_driver_data *aat2870_bl = platform_get_drvdata(pdev);
struct backlight_device *bd = aat2870_bl->bd;
bd->props.power = FB_BLANK_POWERDOWN;
bd->props.brightness = 0;
backlight_update_status(bd);
backlight_device_unregister(bd);
kfree(aat2870_bl);
return 0;
}
static struct platform_driver aat2870_bl_driver = {
.driver = {
.name = "aat2870-backlight",
.owner = THIS_MODULE,
},
.probe = aat2870_bl_probe,
.remove = aat2870_bl_remove,
};
static int __init aat2870_bl_init(void)
{
return platform_driver_register(&aat2870_bl_driver);
}
subsys_initcall(aat2870_bl_init);
static void __exit aat2870_bl_exit(void)
{
platform_driver_unregister(&aat2870_bl_driver);
}
module_exit(aat2870_bl_exit);
MODULE_DESCRIPTION("AnalogicTech AAT2870 Backlight");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");

181
include/linux/mfd/aat2870.h Normal file
View File

@ -0,0 +1,181 @@
/*
* linux/include/linux/mfd/aat2870.h
*
* Copyright (c) 2011, NVIDIA Corporation.
* Author: Jin Park <jinyoungp@nvidia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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 St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef __LINUX_MFD_AAT2870_H
#define __LINUX_MFD_AAT2870_H
#include <linux/debugfs.h>
#include <linux/i2c.h>
/* Register offsets */
#define AAT2870_BL_CH_EN 0x00
#define AAT2870_BLM 0x01
#define AAT2870_BLS 0x02
#define AAT2870_BL1 0x03
#define AAT2870_BL2 0x04
#define AAT2870_BL3 0x05
#define AAT2870_BL4 0x06
#define AAT2870_BL5 0x07
#define AAT2870_BL6 0x08
#define AAT2870_BL7 0x09
#define AAT2870_BL8 0x0A
#define AAT2870_FLR 0x0B
#define AAT2870_FM 0x0C
#define AAT2870_FS 0x0D
#define AAT2870_ALS_CFG0 0x0E
#define AAT2870_ALS_CFG1 0x0F
#define AAT2870_ALS_CFG2 0x10
#define AAT2870_AMB 0x11
#define AAT2870_ALS0 0x12
#define AAT2870_ALS1 0x13
#define AAT2870_ALS2 0x14
#define AAT2870_ALS3 0x15
#define AAT2870_ALS4 0x16
#define AAT2870_ALS5 0x17
#define AAT2870_ALS6 0x18
#define AAT2870_ALS7 0x19
#define AAT2870_ALS8 0x1A
#define AAT2870_ALS9 0x1B
#define AAT2870_ALSA 0x1C
#define AAT2870_ALSB 0x1D
#define AAT2870_ALSC 0x1E
#define AAT2870_ALSD 0x1F
#define AAT2870_ALSE 0x20
#define AAT2870_ALSF 0x21
#define AAT2870_SUB_SET 0x22
#define AAT2870_SUB_CTRL 0x23
#define AAT2870_LDO_AB 0x24
#define AAT2870_LDO_CD 0x25
#define AAT2870_LDO_EN 0x26
#define AAT2870_REG_NUM 0x27
/* Device IDs */
enum aat2870_id {
AAT2870_ID_BL,
AAT2870_ID_LDOA,
AAT2870_ID_LDOB,
AAT2870_ID_LDOC,
AAT2870_ID_LDOD
};
/* Backlight channels */
#define AAT2870_BL_CH1 0x01
#define AAT2870_BL_CH2 0x02
#define AAT2870_BL_CH3 0x04
#define AAT2870_BL_CH4 0x08
#define AAT2870_BL_CH5 0x10
#define AAT2870_BL_CH6 0x20
#define AAT2870_BL_CH7 0x40
#define AAT2870_BL_CH8 0x80
#define AAT2870_BL_CH_ALL 0xFF
/* Backlight current magnitude (mA) */
enum aat2870_current {
AAT2870_CURRENT_0_45,
AAT2870_CURRENT_0_90,
AAT2870_CURRENT_1_80,
AAT2870_CURRENT_2_70,
AAT2870_CURRENT_3_60,
AAT2870_CURRENT_4_50,
AAT2870_CURRENT_5_40,
AAT2870_CURRENT_6_30,
AAT2870_CURRENT_7_20,
AAT2870_CURRENT_8_10,
AAT2870_CURRENT_9_00,
AAT2870_CURRENT_9_90,
AAT2870_CURRENT_10_8,
AAT2870_CURRENT_11_7,
AAT2870_CURRENT_12_6,
AAT2870_CURRENT_13_5,
AAT2870_CURRENT_14_4,
AAT2870_CURRENT_15_3,
AAT2870_CURRENT_16_2,
AAT2870_CURRENT_17_1,
AAT2870_CURRENT_18_0,
AAT2870_CURRENT_18_9,
AAT2870_CURRENT_19_8,
AAT2870_CURRENT_20_7,
AAT2870_CURRENT_21_6,
AAT2870_CURRENT_22_5,
AAT2870_CURRENT_23_4,
AAT2870_CURRENT_24_3,
AAT2870_CURRENT_25_2,
AAT2870_CURRENT_26_1,
AAT2870_CURRENT_27_0,
AAT2870_CURRENT_27_9
};
struct aat2870_register {
bool readable;
bool writeable;
u8 value;
};
struct aat2870_data {
struct device *dev;
struct i2c_client *client;
struct mutex io_lock;
struct aat2870_register *reg_cache; /* register cache */
int en_pin; /* enable GPIO pin (if < 0, ignore this value) */
bool is_enable;
/* init and uninit for platform specified */
int (*init)(struct aat2870_data *aat2870);
void (*uninit)(struct aat2870_data *aat2870);
/* i2c io funcntions */
int (*read)(struct aat2870_data *aat2870, u8 addr, u8 *val);
int (*write)(struct aat2870_data *aat2870, u8 addr, u8 val);
int (*update)(struct aat2870_data *aat2870, u8 addr, u8 mask, u8 val);
/* for debugfs */
struct dentry *dentry_root;
struct dentry *dentry_reg;
};
struct aat2870_subdev_info {
int id;
const char *name;
void *platform_data;
};
struct aat2870_platform_data {
int en_pin; /* enable GPIO pin (if < 0, ignore this value) */
struct aat2870_subdev_info *subdevs;
int num_subdevs;
/* init and uninit for platform specified */
int (*init)(struct aat2870_data *aat2870);
void (*uninit)(struct aat2870_data *aat2870);
};
struct aat2870_bl_platform_data {
/* backlight channels, default is AAT2870_BL_CH_ALL */
int channels;
/* backlight current magnitude, default is AAT2870_CURRENT_27_9 */
int max_current;
/* maximum brightness, default is 255 */
int max_brightness;
};
#endif /* __LINUX_MFD_AAT2870_H */

View File

@ -28,6 +28,7 @@
#define AB8500_INTERRUPT 0xE
#define AB8500_RTC 0xF
#define AB8500_MISC 0x10
#define AB8500_DEVELOPMENT 0x11
#define AB8500_DEBUG 0x12
#define AB8500_PROD_TEST 0x13
#define AB8500_OTP_EMUL 0x15
@ -74,13 +75,6 @@
#define AB8500_INT_ACC_DETECT_21DB_F 37
#define AB8500_INT_ACC_DETECT_21DB_R 38
#define AB8500_INT_GP_SW_ADC_CONV_END 39
#define AB8500_INT_ACC_DETECT_1DB_F 33
#define AB8500_INT_ACC_DETECT_1DB_R 34
#define AB8500_INT_ACC_DETECT_22DB_F 35
#define AB8500_INT_ACC_DETECT_22DB_R 36
#define AB8500_INT_ACC_DETECT_21DB_F 37
#define AB8500_INT_ACC_DETECT_21DB_R 38
#define AB8500_INT_GP_SW_ADC_CONV_END 39
#define AB8500_INT_GPIO6R 40
#define AB8500_INT_GPIO7R 41
#define AB8500_INT_GPIO8R 42

View File

@ -57,6 +57,7 @@ struct stmpe_variant_info;
* @irq_lock: IRQ bus lock
* @dev: device, mostly for dev_dbg()
* @i2c: i2c client
* @partnum: part number
* @variant: the detected STMPE model number
* @regs: list of addresses of registers which are at different addresses on
* different variants. Indexed by one of STMPE_IDX_*.
@ -121,6 +122,8 @@ struct stmpe_keypad_platform_data {
* @norequest_mask: bitmask specifying which GPIOs should _not_ be
* requestable due to different usage (e.g. touch, keypad)
* STMPE_GPIO_NOREQ_* macros can be used here.
* @setup: board specific setup callback.
* @remove: board specific remove callback
*/
struct stmpe_gpio_platform_data {
int gpio_base;

View File

@ -791,6 +791,7 @@ int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask);
void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base);
int tps65910_irq_init(struct tps65910 *tps65910, int irq,
struct tps65910_platform_data *pdata);
int tps65910_irq_exit(struct tps65910 *tps65910);
static inline int tps65910_chip_id(struct tps65910 *tps65910)
{

View File

@ -0,0 +1,327 @@
/*
* tps65912.h -- TI TPS6591x
*
* Copyright 2011 Texas Instruments Inc.
*
* Author: Margarita Olaya <magi@slimlogic.co.uk>
*
* 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.
*
*/
#ifndef __LINUX_MFD_TPS65912_H
#define __LINUX_MFD_TPS65912_H
/* TPS regulator type list */
#define REGULATOR_LDO 0
#define REGULATOR_DCDC 1
/*
* List of registers for TPS65912
*/
#define TPS65912_DCDC1_CTRL 0x00
#define TPS65912_DCDC2_CTRL 0x01
#define TPS65912_DCDC3_CTRL 0x02
#define TPS65912_DCDC4_CTRL 0x03
#define TPS65912_DCDC1_OP 0x04
#define TPS65912_DCDC1_AVS 0x05
#define TPS65912_DCDC1_LIMIT 0x06
#define TPS65912_DCDC2_OP 0x07
#define TPS65912_DCDC2_AVS 0x08
#define TPS65912_DCDC2_LIMIT 0x09
#define TPS65912_DCDC3_OP 0x0A
#define TPS65912_DCDC3_AVS 0x0B
#define TPS65912_DCDC3_LIMIT 0x0C
#define TPS65912_DCDC4_OP 0x0D
#define TPS65912_DCDC4_AVS 0x0E
#define TPS65912_DCDC4_LIMIT 0x0F
#define TPS65912_LDO1_OP 0x10
#define TPS65912_LDO1_AVS 0x11
#define TPS65912_LDO1_LIMIT 0x12
#define TPS65912_LDO2_OP 0x13
#define TPS65912_LDO2_AVS 0x14
#define TPS65912_LDO2_LIMIT 0x15
#define TPS65912_LDO3_OP 0x16
#define TPS65912_LDO3_AVS 0x17
#define TPS65912_LDO3_LIMIT 0x18
#define TPS65912_LDO4_OP 0x19
#define TPS65912_LDO4_AVS 0x1A
#define TPS65912_LDO4_LIMIT 0x1B
#define TPS65912_LDO5 0x1C
#define TPS65912_LDO6 0x1D
#define TPS65912_LDO7 0x1E
#define TPS65912_LDO8 0x1F
#define TPS65912_LDO9 0x20
#define TPS65912_LDO10 0x21
#define TPS65912_THRM 0x22
#define TPS65912_CLK32OUT 0x23
#define TPS65912_DEVCTRL 0x24
#define TPS65912_DEVCTRL2 0x25
#define TPS65912_I2C_SPI_CFG 0x26
#define TPS65912_KEEP_ON 0x27
#define TPS65912_KEEP_ON2 0x28
#define TPS65912_SET_OFF1 0x29
#define TPS65912_SET_OFF2 0x2A
#define TPS65912_DEF_VOLT 0x2B
#define TPS65912_DEF_VOLT_MAPPING 0x2C
#define TPS65912_DISCHARGE 0x2D
#define TPS65912_DISCHARGE2 0x2E
#define TPS65912_EN1_SET1 0x2F
#define TPS65912_EN1_SET2 0x30
#define TPS65912_EN2_SET1 0x31
#define TPS65912_EN2_SET2 0x32
#define TPS65912_EN3_SET1 0x33
#define TPS65912_EN3_SET2 0x34
#define TPS65912_EN4_SET1 0x35
#define TPS65912_EN4_SET2 0x36
#define TPS65912_PGOOD 0x37
#define TPS65912_PGOOD2 0x38
#define TPS65912_INT_STS 0x39
#define TPS65912_INT_MSK 0x3A
#define TPS65912_INT_STS2 0x3B
#define TPS65912_INT_MSK2 0x3C
#define TPS65912_INT_STS3 0x3D
#define TPS65912_INT_MSK3 0x3E
#define TPS65912_INT_STS4 0x3F
#define TPS65912_INT_MSK4 0x40
#define TPS65912_GPIO1 0x41
#define TPS65912_GPIO2 0x42
#define TPS65912_GPIO3 0x43
#define TPS65912_GPIO4 0x44
#define TPS65912_GPIO5 0x45
#define TPS65912_VMON 0x46
#define TPS65912_LEDA_CTRL1 0x47
#define TPS65912_LEDA_CTRL2 0x48
#define TPS65912_LEDA_CTRL3 0x49
#define TPS65912_LEDA_CTRL4 0x4A
#define TPS65912_LEDA_CTRL5 0x4B
#define TPS65912_LEDA_CTRL6 0x4C
#define TPS65912_LEDA_CTRL7 0x4D
#define TPS65912_LEDA_CTRL8 0x4E
#define TPS65912_LEDB_CTRL1 0x4F
#define TPS65912_LEDB_CTRL2 0x50
#define TPS65912_LEDB_CTRL3 0x51
#define TPS65912_LEDB_CTRL4 0x52
#define TPS65912_LEDB_CTRL5 0x53
#define TPS65912_LEDB_CTRL6 0x54
#define TPS65912_LEDB_CTRL7 0x55
#define TPS65912_LEDB_CTRL8 0x56
#define TPS65912_LEDC_CTRL1 0x57
#define TPS65912_LEDC_CTRL2 0x58
#define TPS65912_LEDC_CTRL3 0x59
#define TPS65912_LEDC_CTRL4 0x5A
#define TPS65912_LEDC_CTRL5 0x5B
#define TPS65912_LEDC_CTRL6 0x5C
#define TPS65912_LEDC_CTRL7 0x5D
#define TPS65912_LEDC_CTRL8 0x5E
#define TPS65912_LED_RAMP_UP_TIME 0x5F
#define TPS65912_LED_RAMP_DOWN_TIME 0x60
#define TPS65912_LED_SEQ_EN 0x61
#define TPS65912_LOADSWITCH 0x62
#define TPS65912_SPARE 0x63
#define TPS65912_VERNUM 0x64
#define TPS6591X_MAX_REGISTER 0x64
/* IRQ Definitions */
#define TPS65912_IRQ_PWRHOLD_F 0
#define TPS65912_IRQ_VMON 1
#define TPS65912_IRQ_PWRON 2
#define TPS65912_IRQ_PWRON_LP 3
#define TPS65912_IRQ_PWRHOLD_R 4
#define TPS65912_IRQ_HOTDIE 5
#define TPS65912_IRQ_GPIO1_R 6
#define TPS65912_IRQ_GPIO1_F 7
#define TPS65912_IRQ_GPIO2_R 8
#define TPS65912_IRQ_GPIO2_F 9
#define TPS65912_IRQ_GPIO3_R 10
#define TPS65912_IRQ_GPIO3_F 11
#define TPS65912_IRQ_GPIO4_R 12
#define TPS65912_IRQ_GPIO4_F 13
#define TPS65912_IRQ_GPIO5_R 14
#define TPS65912_IRQ_GPIO5_F 15
#define TPS65912_IRQ_PGOOD_DCDC1 16
#define TPS65912_IRQ_PGOOD_DCDC2 17
#define TPS65912_IRQ_PGOOD_DCDC3 18
#define TPS65912_IRQ_PGOOD_DCDC4 19
#define TPS65912_IRQ_PGOOD_LDO1 20
#define TPS65912_IRQ_PGOOD_LDO2 21
#define TPS65912_IRQ_PGOOD_LDO3 22
#define TPS65912_IRQ_PGOOD_LDO4 23
#define TPS65912_IRQ_PGOOD_LDO5 24
#define TPS65912_IRQ_PGOOD_LDO6 25
#define TPS65912_IRQ_PGOOD_LDO7 26
#define TPS65912_IRQ_PGOOD_LD08 27
#define TPS65912_IRQ_PGOOD_LDO9 28
#define TPS65912_IRQ_PGOOD_LDO10 29
#define TPS65912_NUM_IRQ 30
/* GPIO 1 and 2 Register Definitions */
#define GPIO_SLEEP_MASK 0x80
#define GPIO_SLEEP_SHIFT 7
#define GPIO_DEB_MASK 0x10
#define GPIO_DEB_SHIFT 4
#define GPIO_CFG_MASK 0x04
#define GPIO_CFG_SHIFT 2
#define GPIO_STS_MASK 0x02
#define GPIO_STS_SHIFT 1
#define GPIO_SET_MASK 0x01
#define GPIO_SET_SHIFT 0
/* GPIO 3 Register Definitions */
#define GPIO3_SLEEP_MASK 0x80
#define GPIO3_SLEEP_SHIFT 7
#define GPIO3_SEL_MASK 0x40
#define GPIO3_SEL_SHIFT 6
#define GPIO3_ODEN_MASK 0x20
#define GPIO3_ODEN_SHIFT 5
#define GPIO3_DEB_MASK 0x10
#define GPIO3_DEB_SHIFT 4
#define GPIO3_PDEN_MASK 0x08
#define GPIO3_PDEN_SHIFT 3
#define GPIO3_CFG_MASK 0x04
#define GPIO3_CFG_SHIFT 2
#define GPIO3_STS_MASK 0x02
#define GPIO3_STS_SHIFT 1
#define GPIO3_SET_MASK 0x01
#define GPIO3_SET_SHIFT 0
/* GPIO 4 Register Definitions */
#define GPIO4_SLEEP_MASK 0x80
#define GPIO4_SLEEP_SHIFT 7
#define GPIO4_SEL_MASK 0x40
#define GPIO4_SEL_SHIFT 6
#define GPIO4_ODEN_MASK 0x20
#define GPIO4_ODEN_SHIFT 5
#define GPIO4_DEB_MASK 0x10
#define GPIO4_DEB_SHIFT 4
#define GPIO4_PDEN_MASK 0x08
#define GPIO4_PDEN_SHIFT 3
#define GPIO4_CFG_MASK 0x04
#define GPIO4_CFG_SHIFT 2
#define GPIO4_STS_MASK 0x02
#define GPIO4_STS_SHIFT 1
#define GPIO4_SET_MASK 0x01
#define GPIO4_SET_SHIFT 0
/* Register THERM (0x80) register.RegisterDescription */
#define THERM_THERM_HD_MASK 0x20
#define THERM_THERM_HD_SHIFT 5
#define THERM_THERM_TS_MASK 0x10
#define THERM_THERM_TS_SHIFT 4
#define THERM_THERM_HDSEL_MASK 0x0C
#define THERM_THERM_HDSEL_SHIFT 2
#define THERM_RSVD1_MASK 0x02
#define THERM_RSVD1_SHIFT 1
#define THERM_THERM_STATE_MASK 0x01
#define THERM_THERM_STATE_SHIFT 0
/* Register DCDCCTRL1 register.RegisterDescription */
#define DCDCCTRL_VCON_ENABLE_MASK 0x80
#define DCDCCTRL_VCON_ENABLE_SHIFT 7
#define DCDCCTRL_VCON_RANGE1_MASK 0x40
#define DCDCCTRL_VCON_RANGE1_SHIFT 6
#define DCDCCTRL_VCON_RANGE0_MASK 0x20
#define DCDCCTRL_VCON_RANGE0_SHIFT 5
#define DCDCCTRL_TSTEP2_MASK 0x10
#define DCDCCTRL_TSTEP2_SHIFT 4
#define DCDCCTRL_TSTEP1_MASK 0x08
#define DCDCCTRL_TSTEP1_SHIFT 3
#define DCDCCTRL_TSTEP0_MASK 0x04
#define DCDCCTRL_TSTEP0_SHIFT 2
#define DCDCCTRL_DCDC1_MODE_MASK 0x02
#define DCDCCTRL_DCDC1_MODE_SHIFT 1
/* Register DCDCCTRL2 and DCDCCTRL3 register.RegisterDescription */
#define DCDCCTRL_TSTEP2_MASK 0x10
#define DCDCCTRL_TSTEP2_SHIFT 4
#define DCDCCTRL_TSTEP1_MASK 0x08
#define DCDCCTRL_TSTEP1_SHIFT 3
#define DCDCCTRL_TSTEP0_MASK 0x04
#define DCDCCTRL_TSTEP0_SHIFT 2
#define DCDCCTRL_DCDC_MODE_MASK 0x02
#define DCDCCTRL_DCDC_MODE_SHIFT 1
#define DCDCCTRL_RSVD0_MASK 0x01
#define DCDCCTRL_RSVD0_SHIFT 0
/* Register DCDCCTRL4 register.RegisterDescription */
#define DCDCCTRL_RAMP_TIME_MASK 0x01
#define DCDCCTRL_RAMP_TIME_SHIFT 0
/* Register DCDCx_AVS */
#define DCDC_AVS_ENABLE_MASK 0x80
#define DCDC_AVS_ENABLE_SHIFT 7
#define DCDC_AVS_ECO_MASK 0x40
#define DCDC_AVS_ECO_SHIFT 6
/* Register DCDCx_LIMIT */
#define DCDC_LIMIT_RANGE_MASK 0xC0
#define DCDC_LIMIT_RANGE_SHIFT 6
#define DCDC_LIMIT_MAX_SEL_MASK 0x3F
#define DCDC_LIMIT_MAX_SEL_SHIFT 0
/**
* struct tps65912_board
* Board platform dat may be used to initialize regulators.
*/
struct tps65912_board {
int is_dcdc1_avs;
int is_dcdc2_avs;
int is_dcdc3_avs;
int is_dcdc4_avs;
int irq;
int irq_base;
int gpio_base;
struct regulator_init_data *tps65912_pmic_init_data;
};
/**
* struct tps65912 - tps65912 sub-driver chip access routines
*/
struct tps65912 {
struct device *dev;
/* for read/write acces */
struct mutex io_mutex;
/* For device IO interfaces: I2C or SPI */
void *control_data;
int (*read)(struct tps65912 *tps65912, u8 reg, int size, void *dest);
int (*write)(struct tps65912 *tps65912, u8 reg, int size, void *src);
/* Client devices */
struct tps65912_pmic *pmic;
/* GPIO Handling */
struct gpio_chip gpio;
/* IRQ Handling */
struct mutex irq_lock;
int chip_irq;
int irq_base;
int irq_num;
u32 irq_mask;
};
struct tps65912_platform_data {
int irq;
int irq_base;
};
unsigned int tps_chip(void);
int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask);
int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask);
int tps65912_reg_read(struct tps65912 *tps65912, u8 reg);
int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val);
int tps65912_device_init(struct tps65912 *tps65912);
void tps65912_device_exit(struct tps65912 *tps65912);
int tps65912_irq_init(struct tps65912 *tps65912, int irq,
struct tps65912_platform_data *pdata);
#endif /* __LINUX_MFD_TPS65912_H */

View File

@ -17,6 +17,7 @@
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/list.h>
/*
* Register values.
@ -234,9 +235,111 @@
#define WM831X_ON_PIN_TO_SHIFT 0 /* ON_PIN_TO - [1:0] */
#define WM831X_ON_PIN_TO_WIDTH 2 /* ON_PIN_TO - [1:0] */
/*
* R16528 (0x4090) - Clock Control 1
*/
#define WM831X_CLKOUT_ENA 0x8000 /* CLKOUT_ENA */
#define WM831X_CLKOUT_ENA_MASK 0x8000 /* CLKOUT_ENA */
#define WM831X_CLKOUT_ENA_SHIFT 15 /* CLKOUT_ENA */
#define WM831X_CLKOUT_ENA_WIDTH 1 /* CLKOUT_ENA */
#define WM831X_CLKOUT_OD 0x2000 /* CLKOUT_OD */
#define WM831X_CLKOUT_OD_MASK 0x2000 /* CLKOUT_OD */
#define WM831X_CLKOUT_OD_SHIFT 13 /* CLKOUT_OD */
#define WM831X_CLKOUT_OD_WIDTH 1 /* CLKOUT_OD */
#define WM831X_CLKOUT_SLOT_MASK 0x0700 /* CLKOUT_SLOT - [10:8] */
#define WM831X_CLKOUT_SLOT_SHIFT 8 /* CLKOUT_SLOT - [10:8] */
#define WM831X_CLKOUT_SLOT_WIDTH 3 /* CLKOUT_SLOT - [10:8] */
#define WM831X_CLKOUT_SLPSLOT_MASK 0x0070 /* CLKOUT_SLPSLOT - [6:4] */
#define WM831X_CLKOUT_SLPSLOT_SHIFT 4 /* CLKOUT_SLPSLOT - [6:4] */
#define WM831X_CLKOUT_SLPSLOT_WIDTH 3 /* CLKOUT_SLPSLOT - [6:4] */
#define WM831X_CLKOUT_SRC 0x0001 /* CLKOUT_SRC */
#define WM831X_CLKOUT_SRC_MASK 0x0001 /* CLKOUT_SRC */
#define WM831X_CLKOUT_SRC_SHIFT 0 /* CLKOUT_SRC */
#define WM831X_CLKOUT_SRC_WIDTH 1 /* CLKOUT_SRC */
/*
* R16529 (0x4091) - Clock Control 2
*/
#define WM831X_XTAL_INH 0x8000 /* XTAL_INH */
#define WM831X_XTAL_INH_MASK 0x8000 /* XTAL_INH */
#define WM831X_XTAL_INH_SHIFT 15 /* XTAL_INH */
#define WM831X_XTAL_INH_WIDTH 1 /* XTAL_INH */
#define WM831X_XTAL_ENA 0x2000 /* XTAL_ENA */
#define WM831X_XTAL_ENA_MASK 0x2000 /* XTAL_ENA */
#define WM831X_XTAL_ENA_SHIFT 13 /* XTAL_ENA */
#define WM831X_XTAL_ENA_WIDTH 1 /* XTAL_ENA */
#define WM831X_XTAL_BKUPENA 0x1000 /* XTAL_BKUPENA */
#define WM831X_XTAL_BKUPENA_MASK 0x1000 /* XTAL_BKUPENA */
#define WM831X_XTAL_BKUPENA_SHIFT 12 /* XTAL_BKUPENA */
#define WM831X_XTAL_BKUPENA_WIDTH 1 /* XTAL_BKUPENA */
#define WM831X_FLL_AUTO 0x0080 /* FLL_AUTO */
#define WM831X_FLL_AUTO_MASK 0x0080 /* FLL_AUTO */
#define WM831X_FLL_AUTO_SHIFT 7 /* FLL_AUTO */
#define WM831X_FLL_AUTO_WIDTH 1 /* FLL_AUTO */
#define WM831X_FLL_AUTO_FREQ_MASK 0x0007 /* FLL_AUTO_FREQ - [2:0] */
#define WM831X_FLL_AUTO_FREQ_SHIFT 0 /* FLL_AUTO_FREQ - [2:0] */
#define WM831X_FLL_AUTO_FREQ_WIDTH 3 /* FLL_AUTO_FREQ - [2:0] */
/*
* R16530 (0x4092) - FLL Control 1
*/
#define WM831X_FLL_FRAC 0x0004 /* FLL_FRAC */
#define WM831X_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */
#define WM831X_FLL_FRAC_SHIFT 2 /* FLL_FRAC */
#define WM831X_FLL_FRAC_WIDTH 1 /* FLL_FRAC */
#define WM831X_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
#define WM831X_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
#define WM831X_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
#define WM831X_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
#define WM831X_FLL_ENA 0x0001 /* FLL_ENA */
#define WM831X_FLL_ENA_MASK 0x0001 /* FLL_ENA */
#define WM831X_FLL_ENA_SHIFT 0 /* FLL_ENA */
#define WM831X_FLL_ENA_WIDTH 1 /* FLL_ENA */
/*
* R16531 (0x4093) - FLL Control 2
*/
#define WM831X_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */
#define WM831X_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */
#define WM831X_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */
#define WM831X_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */
#define WM831X_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */
#define WM831X_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */
#define WM831X_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
#define WM831X_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
#define WM831X_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
/*
* R16532 (0x4094) - FLL Control 3
*/
#define WM831X_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */
#define WM831X_FLL_K_SHIFT 0 /* FLL_K - [15:0] */
#define WM831X_FLL_K_WIDTH 16 /* FLL_K - [15:0] */
/*
* R16533 (0x4095) - FLL Control 4
*/
#define WM831X_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
#define WM831X_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
#define WM831X_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
#define WM831X_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */
#define WM831X_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */
#define WM831X_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */
/*
* R16534 (0x4096) - FLL Control 5
*/
#define WM831X_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */
#define WM831X_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */
#define WM831X_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */
#define WM831X_FLL_CLK_SRC_MASK 0x0003 /* FLL_CLK_SRC - [1:0] */
#define WM831X_FLL_CLK_SRC_SHIFT 0 /* FLL_CLK_SRC - [1:0] */
#define WM831X_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [1:0] */
struct regulator_dev;
#define WM831X_NUM_IRQ_REGS 5
#define WM831X_NUM_GPIO_REGS 16
enum wm831x_parent {
WM8310 = 0x8310,
@ -248,6 +351,12 @@ enum wm831x_parent {
WM8326 = 0x8326,
};
struct wm831x;
enum wm831x_auxadc;
typedef int (*wm831x_auxadc_read_fn)(struct wm831x *wm831x,
enum wm831x_auxadc input);
struct wm831x {
struct mutex io_lock;
@ -261,7 +370,7 @@ struct wm831x {
int irq; /* Our chip IRQ */
struct mutex irq_lock;
unsigned int irq_base;
int irq_base;
int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */
int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
@ -272,8 +381,13 @@ struct wm831x {
int num_gpio;
/* Used by the interrupt controller code to post writes */
int gpio_update[WM831X_NUM_GPIO_REGS];
struct mutex auxadc_lock;
struct completion auxadc_done;
struct list_head auxadc_pending;
u16 auxadc_active;
wm831x_auxadc_read_fn auxadc_read;
/* The WM831x has a security key blocking access to certain
* registers. The mutex is taken by the accessors for locking
@ -300,5 +414,6 @@ void wm831x_device_exit(struct wm831x *wm831x);
int wm831x_device_suspend(struct wm831x *wm831x);
int wm831x_irq_init(struct wm831x *wm831x, int irq);
void wm831x_irq_exit(struct wm831x *wm831x);
void wm831x_auxadc_init(struct wm831x *wm831x);
#endif

View File

@ -120,6 +120,9 @@ struct wm831x_pdata {
/** Put the /IRQ line into CMOS mode */
bool irq_cmos;
/** Disable the touchscreen */
bool disable_touch;
int irq_base;
int gpio_base;
int gpio_defaults[WM831X_GPIO_NUM];