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: (40 commits) mfd: Fix incorrect kfree(i2c) in wm8994-core i2c_driver probe mfd: Fix incorrect kfree(i2c) in wm831x-core i2c_driver probe mfd: Fix incorrect kfree(i2c) in tps6507x i2c_driver probe mfd: Add TPS6586x driver mfd: Use macros instead of some constant magic numbers for menelaus mfd: Fix menelaus mmc slot 2 misconfiguration mfd: Missing slab.h includes mfd: Fix wrong wm8350-core kfree in error path mfd: Fix wm8994_device_init() return value mfd: Avoid calling platform_device_put() twice in ucb1400 probe error path mfd: Annotate tc6387xb probe/remove routines with __devinit/__devexit mfd: Fix tc6387xb resource reclaim mfd: Fix wrong goto labels for tc6393xb error handling mfd: Get rid of now unused mc13783 private header hwmon: Don't access struct mc13783 directly from mc13783-adc mfd: New mc13783 function exposing flags mfd: Check jz4740-adc kmalloc() result mfd: Fix jz4740-adc resource reclaim in probe error path mfd: Add WM8321 support mfd: Add stmpe auto sleep feature ...
This commit is contained in:
commit
e83ddb3354
|
@ -206,6 +206,13 @@ config GPIO_SX150X
|
||||||
8 bits: sx1508q
|
8 bits: sx1508q
|
||||||
16 bits: sx1509q
|
16 bits: sx1509q
|
||||||
|
|
||||||
|
config GPIO_STMPE
|
||||||
|
bool "STMPE GPIOs"
|
||||||
|
depends on MFD_STMPE
|
||||||
|
help
|
||||||
|
This enables support for the GPIOs found on the STMPE I/O
|
||||||
|
Expanders.
|
||||||
|
|
||||||
config GPIO_TC35892
|
config GPIO_TC35892
|
||||||
bool "TC35892 GPIOs"
|
bool "TC35892 GPIOs"
|
||||||
depends on MFD_TC35892
|
depends on MFD_TC35892
|
||||||
|
|
|
@ -20,6 +20,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
|
||||||
obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
|
obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
|
||||||
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
|
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
|
||||||
obj-$(CONFIG_GPIO_PL061) += pl061.o
|
obj-$(CONFIG_GPIO_PL061) += pl061.o
|
||||||
|
obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o
|
||||||
obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o
|
obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o
|
||||||
obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
|
obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
|
||||||
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
|
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
|
||||||
|
|
|
@ -0,0 +1,399 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) ST-Ericsson SA 2010
|
||||||
|
*
|
||||||
|
* License Terms: GNU General Public License, version 2
|
||||||
|
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/mfd/stmpe.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These registers are modified under the irq bus lock and cached to avoid
|
||||||
|
* unnecessary writes in bus_sync_unlock.
|
||||||
|
*/
|
||||||
|
enum { REG_RE, REG_FE, REG_IE };
|
||||||
|
|
||||||
|
#define CACHE_NR_REGS 3
|
||||||
|
#define CACHE_NR_BANKS (STMPE_NR_GPIOS / 8)
|
||||||
|
|
||||||
|
struct stmpe_gpio {
|
||||||
|
struct gpio_chip chip;
|
||||||
|
struct stmpe *stmpe;
|
||||||
|
struct device *dev;
|
||||||
|
struct mutex irq_lock;
|
||||||
|
|
||||||
|
int irq_base;
|
||||||
|
|
||||||
|
/* Caches of interrupt control registers for bus_lock */
|
||||||
|
u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
|
||||||
|
u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct stmpe_gpio *to_stmpe_gpio(struct gpio_chip *chip)
|
||||||
|
{
|
||||||
|
return container_of(chip, struct stmpe_gpio, chip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
|
||||||
|
struct stmpe *stmpe = stmpe_gpio->stmpe;
|
||||||
|
u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB] - (offset / 8);
|
||||||
|
u8 mask = 1 << (offset % 8);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = stmpe_reg_read(stmpe, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return ret & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
|
||||||
|
struct stmpe *stmpe = stmpe_gpio->stmpe;
|
||||||
|
int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB;
|
||||||
|
u8 reg = stmpe->regs[which] - (offset / 8);
|
||||||
|
u8 mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
stmpe_reg_write(stmpe, reg, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe_gpio_direction_output(struct gpio_chip *chip,
|
||||||
|
unsigned offset, int val)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
|
||||||
|
struct stmpe *stmpe = stmpe_gpio->stmpe;
|
||||||
|
u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
|
||||||
|
u8 mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
stmpe_gpio_set(chip, offset, val);
|
||||||
|
|
||||||
|
return stmpe_set_bits(stmpe, reg, mask, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe_gpio_direction_input(struct gpio_chip *chip,
|
||||||
|
unsigned offset)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
|
||||||
|
struct stmpe *stmpe = stmpe_gpio->stmpe;
|
||||||
|
u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
|
||||||
|
u8 mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
return stmpe_set_bits(stmpe, reg, mask, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
|
||||||
|
|
||||||
|
return stmpe_gpio->irq_base + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
|
||||||
|
struct stmpe *stmpe = stmpe_gpio->stmpe;
|
||||||
|
|
||||||
|
return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct gpio_chip template_chip = {
|
||||||
|
.label = "stmpe",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.direction_input = stmpe_gpio_direction_input,
|
||||||
|
.get = stmpe_gpio_get,
|
||||||
|
.direction_output = stmpe_gpio_direction_output,
|
||||||
|
.set = stmpe_gpio_set,
|
||||||
|
.to_irq = stmpe_gpio_to_irq,
|
||||||
|
.request = stmpe_gpio_request,
|
||||||
|
.can_sleep = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stmpe_gpio_irq_set_type(unsigned int irq, unsigned int type)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
|
||||||
|
int offset = irq - stmpe_gpio->irq_base;
|
||||||
|
int regoffset = offset / 8;
|
||||||
|
int mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (type == IRQ_TYPE_EDGE_RISING)
|
||||||
|
stmpe_gpio->regs[REG_RE][regoffset] |= mask;
|
||||||
|
else
|
||||||
|
stmpe_gpio->regs[REG_RE][regoffset] &= ~mask;
|
||||||
|
|
||||||
|
if (type == IRQ_TYPE_EDGE_FALLING)
|
||||||
|
stmpe_gpio->regs[REG_FE][regoffset] |= mask;
|
||||||
|
else
|
||||||
|
stmpe_gpio->regs[REG_FE][regoffset] &= ~mask;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_gpio_irq_lock(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
|
||||||
|
|
||||||
|
mutex_lock(&stmpe_gpio->irq_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_gpio_irq_sync_unlock(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
|
||||||
|
struct stmpe *stmpe = stmpe_gpio->stmpe;
|
||||||
|
int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
|
||||||
|
static const u8 regmap[] = {
|
||||||
|
[REG_RE] = STMPE_IDX_GPRER_LSB,
|
||||||
|
[REG_FE] = STMPE_IDX_GPFER_LSB,
|
||||||
|
[REG_IE] = STMPE_IDX_IEGPIOR_LSB,
|
||||||
|
};
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < CACHE_NR_REGS; i++) {
|
||||||
|
for (j = 0; j < num_banks; j++) {
|
||||||
|
u8 old = stmpe_gpio->oldregs[i][j];
|
||||||
|
u8 new = stmpe_gpio->regs[i][j];
|
||||||
|
|
||||||
|
if (new == old)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
stmpe_gpio->oldregs[i][j] = new;
|
||||||
|
stmpe_reg_write(stmpe, stmpe->regs[regmap[i]] - j, new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&stmpe_gpio->irq_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_gpio_irq_mask(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
|
||||||
|
int offset = irq - stmpe_gpio->irq_base;
|
||||||
|
int regoffset = offset / 8;
|
||||||
|
int mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
stmpe_gpio->regs[REG_IE][regoffset] &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_gpio_irq_unmask(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
|
||||||
|
int offset = irq - stmpe_gpio->irq_base;
|
||||||
|
int regoffset = offset / 8;
|
||||||
|
int mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
stmpe_gpio->regs[REG_IE][regoffset] |= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip stmpe_gpio_irq_chip = {
|
||||||
|
.name = "stmpe-gpio",
|
||||||
|
.bus_lock = stmpe_gpio_irq_lock,
|
||||||
|
.bus_sync_unlock = stmpe_gpio_irq_sync_unlock,
|
||||||
|
.mask = stmpe_gpio_irq_mask,
|
||||||
|
.unmask = stmpe_gpio_irq_unmask,
|
||||||
|
.set_type = stmpe_gpio_irq_set_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = dev;
|
||||||
|
struct stmpe *stmpe = stmpe_gpio->stmpe;
|
||||||
|
u8 statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB];
|
||||||
|
int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
|
||||||
|
u8 status[num_banks];
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ret = stmpe_block_read(stmpe, statmsbreg, num_banks, status);
|
||||||
|
if (ret < 0)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
for (i = 0; i < num_banks; i++) {
|
||||||
|
int bank = num_banks - i - 1;
|
||||||
|
unsigned int enabled = stmpe_gpio->regs[REG_IE][bank];
|
||||||
|
unsigned int stat = status[i];
|
||||||
|
|
||||||
|
stat &= enabled;
|
||||||
|
if (!stat)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (stat) {
|
||||||
|
int bit = __ffs(stat);
|
||||||
|
int line = bank * 8 + bit;
|
||||||
|
|
||||||
|
handle_nested_irq(stmpe_gpio->irq_base + line);
|
||||||
|
stat &= ~(1 << bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
|
||||||
|
stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] + i,
|
||||||
|
status[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio)
|
||||||
|
{
|
||||||
|
int base = stmpe_gpio->irq_base;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
|
||||||
|
set_irq_chip_data(irq, stmpe_gpio);
|
||||||
|
set_irq_chip_and_handler(irq, &stmpe_gpio_irq_chip,
|
||||||
|
handle_simple_irq);
|
||||||
|
set_irq_nested_thread(irq, 1);
|
||||||
|
#ifdef CONFIG_ARM
|
||||||
|
set_irq_flags(irq, IRQF_VALID);
|
||||||
|
#else
|
||||||
|
set_irq_noprobe(irq);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio)
|
||||||
|
{
|
||||||
|
int base = stmpe_gpio->irq_base;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
|
||||||
|
#ifdef CONFIG_ARM
|
||||||
|
set_irq_flags(irq, 0);
|
||||||
|
#endif
|
||||||
|
set_irq_chip_and_handler(irq, NULL, NULL);
|
||||||
|
set_irq_chip_data(irq, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
struct stmpe_gpio_platform_data *pdata;
|
||||||
|
struct stmpe_gpio *stmpe_gpio;
|
||||||
|
int ret;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
pdata = stmpe->pdata->gpio;
|
||||||
|
if (!pdata)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
|
||||||
|
stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL);
|
||||||
|
if (!stmpe_gpio)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(&stmpe_gpio->irq_lock);
|
||||||
|
|
||||||
|
stmpe_gpio->dev = &pdev->dev;
|
||||||
|
stmpe_gpio->stmpe = stmpe;
|
||||||
|
|
||||||
|
stmpe_gpio->chip = template_chip;
|
||||||
|
stmpe_gpio->chip.ngpio = stmpe->num_gpios;
|
||||||
|
stmpe_gpio->chip.dev = &pdev->dev;
|
||||||
|
stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1;
|
||||||
|
|
||||||
|
stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
|
||||||
|
|
||||||
|
ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = stmpe_gpio_irq_init(stmpe_gpio);
|
||||||
|
if (ret)
|
||||||
|
goto out_free;
|
||||||
|
|
||||||
|
ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT,
|
||||||
|
"stmpe-gpio", stmpe_gpio);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
|
||||||
|
goto out_removeirq;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpiochip_add(&stmpe_gpio->chip);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
|
||||||
|
goto out_freeirq;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdata && pdata->setup)
|
||||||
|
pdata->setup(stmpe, stmpe_gpio->chip.base);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, stmpe_gpio);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_freeirq:
|
||||||
|
free_irq(irq, stmpe_gpio);
|
||||||
|
out_removeirq:
|
||||||
|
stmpe_gpio_irq_remove(stmpe_gpio);
|
||||||
|
out_free:
|
||||||
|
kfree(stmpe_gpio);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit stmpe_gpio_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct stmpe_gpio *stmpe_gpio = platform_get_drvdata(pdev);
|
||||||
|
struct stmpe *stmpe = stmpe_gpio->stmpe;
|
||||||
|
struct stmpe_gpio_platform_data *pdata = stmpe->pdata->gpio;
|
||||||
|
int irq = platform_get_irq(pdev, 0);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (pdata && pdata->remove)
|
||||||
|
pdata->remove(stmpe, stmpe_gpio->chip.base);
|
||||||
|
|
||||||
|
ret = gpiochip_remove(&stmpe_gpio->chip);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(stmpe_gpio->dev,
|
||||||
|
"unable to remove gpiochip: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
|
||||||
|
|
||||||
|
free_irq(irq, stmpe_gpio);
|
||||||
|
stmpe_gpio_irq_remove(stmpe_gpio);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
kfree(stmpe_gpio);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver stmpe_gpio_driver = {
|
||||||
|
.driver.name = "stmpe-gpio",
|
||||||
|
.driver.owner = THIS_MODULE,
|
||||||
|
.probe = stmpe_gpio_probe,
|
||||||
|
.remove = __devexit_p(stmpe_gpio_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init stmpe_gpio_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&stmpe_gpio_driver);
|
||||||
|
}
|
||||||
|
subsys_initcall(stmpe_gpio_init);
|
||||||
|
|
||||||
|
static void __exit stmpe_gpio_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&stmpe_gpio_driver);
|
||||||
|
}
|
||||||
|
module_exit(stmpe_gpio_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_DESCRIPTION("STMPExxxx GPIO driver");
|
||||||
|
MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
|
|
@ -108,6 +108,37 @@ static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||||
return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset;
|
return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
|
||||||
|
unsigned debounce)
|
||||||
|
{
|
||||||
|
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
|
||||||
|
struct wm831x *wm831x = wm831x_gpio->wm831x;
|
||||||
|
int reg = WM831X_GPIO1_CONTROL + offset;
|
||||||
|
int ret, fn;
|
||||||
|
|
||||||
|
ret = wm831x_reg_read(wm831x, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (ret & WM831X_GPN_FN_MASK) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Not in GPIO mode */
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debounce >= 32 && debounce <= 64)
|
||||||
|
fn = 0;
|
||||||
|
else if (debounce >= 4000 && debounce <= 8000)
|
||||||
|
fn = 1;
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||||
{
|
{
|
||||||
|
@ -208,6 +239,7 @@ static struct gpio_chip template_chip = {
|
||||||
.direction_output = wm831x_gpio_direction_out,
|
.direction_output = wm831x_gpio_direction_out,
|
||||||
.set = wm831x_gpio_set,
|
.set = wm831x_gpio_set,
|
||||||
.to_irq = wm831x_gpio_to_irq,
|
.to_irq = wm831x_gpio_to_irq,
|
||||||
|
.set_debounce = wm831x_gpio_set_debounce,
|
||||||
.dbg_show = wm831x_gpio_dbg_show,
|
.dbg_show = wm831x_gpio_dbg_show,
|
||||||
.can_sleep = 1,
|
.can_sleep = 1,
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/mfd/mc13783-private.h>
|
#include <linux/mfd/mc13783.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/hwmon-sysfs.h>
|
#include <linux/hwmon-sysfs.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -144,6 +144,14 @@ static const struct attribute_group mc13783_group_ts = {
|
||||||
.attrs = mc13783_attr_ts,
|
.attrs = mc13783_attr_ts,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int mc13783_adc_use_touchscreen(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
|
||||||
|
unsigned flags = mc13783_get_flags(priv->mc13783);
|
||||||
|
|
||||||
|
return flags & MC13783_USE_TOUCHSCREEN;
|
||||||
|
}
|
||||||
|
|
||||||
static int __init mc13783_adc_probe(struct platform_device *pdev)
|
static int __init mc13783_adc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct mc13783_adc_priv *priv;
|
struct mc13783_adc_priv *priv;
|
||||||
|
@ -162,10 +170,11 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_err_create1;
|
goto out_err_create1;
|
||||||
|
|
||||||
if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
|
if (!mc13783_adc_use_touchscreen(pdev)) {
|
||||||
ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts);
|
ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_err_create2;
|
goto out_err_create2;
|
||||||
|
}
|
||||||
|
|
||||||
priv->hwmon_dev = hwmon_device_register(&pdev->dev);
|
priv->hwmon_dev = hwmon_device_register(&pdev->dev);
|
||||||
if (IS_ERR(priv->hwmon_dev)) {
|
if (IS_ERR(priv->hwmon_dev)) {
|
||||||
|
@ -180,7 +189,7 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
out_err_register:
|
out_err_register:
|
||||||
|
|
||||||
if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
|
if (!mc13783_adc_use_touchscreen(pdev))
|
||||||
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
|
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
|
||||||
out_err_create2:
|
out_err_create2:
|
||||||
|
|
||||||
|
@ -199,7 +208,7 @@ static int __devexit mc13783_adc_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
hwmon_device_unregister(priv->hwmon_dev);
|
hwmon_device_unregister(priv->hwmon_dev);
|
||||||
|
|
||||||
if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
|
if (!mc13783_adc_use_touchscreen(pdev))
|
||||||
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
|
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
|
||||||
|
|
||||||
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group);
|
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group);
|
||||||
|
|
|
@ -395,6 +395,16 @@ config KEYBOARD_SH_KEYSC
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called sh_keysc.
|
module will be called sh_keysc.
|
||||||
|
|
||||||
|
config KEYBOARD_STMPE
|
||||||
|
tristate "STMPE keypad support"
|
||||||
|
depends on MFD_STMPE
|
||||||
|
help
|
||||||
|
Say Y here if you want to use the keypad controller on STMPE I/O
|
||||||
|
expanders.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called stmpe-keypad.
|
||||||
|
|
||||||
config KEYBOARD_DAVINCI
|
config KEYBOARD_DAVINCI
|
||||||
tristate "TI DaVinci Key Scan"
|
tristate "TI DaVinci Key Scan"
|
||||||
depends on ARCH_DAVINCI_DM365
|
depends on ARCH_DAVINCI_DM365
|
||||||
|
|
|
@ -35,6 +35,7 @@ obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
||||||
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
|
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
|
||||||
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
|
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
|
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
|
||||||
|
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
|
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
|
||||||
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
|
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
|
||||||
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
|
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
|
||||||
|
|
|
@ -0,0 +1,386 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) ST-Ericsson SA 2010
|
||||||
|
*
|
||||||
|
* License Terms: GNU General Public License, version 2
|
||||||
|
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/input/matrix_keypad.h>
|
||||||
|
#include <linux/mfd/stmpe.h>
|
||||||
|
|
||||||
|
/* These are at the same addresses in all STMPE variants */
|
||||||
|
#define STMPE_KPC_COL 0x60
|
||||||
|
#define STMPE_KPC_ROW_MSB 0x61
|
||||||
|
#define STMPE_KPC_ROW_LSB 0x62
|
||||||
|
#define STMPE_KPC_CTRL_MSB 0x63
|
||||||
|
#define STMPE_KPC_CTRL_LSB 0x64
|
||||||
|
#define STMPE_KPC_COMBI_KEY_0 0x65
|
||||||
|
#define STMPE_KPC_COMBI_KEY_1 0x66
|
||||||
|
#define STMPE_KPC_COMBI_KEY_2 0x67
|
||||||
|
#define STMPE_KPC_DATA_BYTE0 0x68
|
||||||
|
#define STMPE_KPC_DATA_BYTE1 0x69
|
||||||
|
#define STMPE_KPC_DATA_BYTE2 0x6a
|
||||||
|
#define STMPE_KPC_DATA_BYTE3 0x6b
|
||||||
|
#define STMPE_KPC_DATA_BYTE4 0x6c
|
||||||
|
|
||||||
|
#define STMPE_KPC_CTRL_LSB_SCAN (0x1 << 0)
|
||||||
|
#define STMPE_KPC_CTRL_LSB_DEBOUNCE (0x7f << 1)
|
||||||
|
#define STMPE_KPC_CTRL_MSB_SCAN_COUNT (0xf << 4)
|
||||||
|
|
||||||
|
#define STMPE_KPC_ROW_MSB_ROWS 0xff
|
||||||
|
|
||||||
|
#define STMPE_KPC_DATA_UP (0x1 << 7)
|
||||||
|
#define STMPE_KPC_DATA_ROW (0xf << 3)
|
||||||
|
#define STMPE_KPC_DATA_COL (0x7 << 0)
|
||||||
|
#define STMPE_KPC_DATA_NOKEY_MASK 0x78
|
||||||
|
|
||||||
|
#define STMPE_KEYPAD_MAX_DEBOUNCE 127
|
||||||
|
#define STMPE_KEYPAD_MAX_SCAN_COUNT 15
|
||||||
|
|
||||||
|
#define STMPE_KEYPAD_MAX_ROWS 8
|
||||||
|
#define STMPE_KEYPAD_MAX_COLS 8
|
||||||
|
#define STMPE_KEYPAD_ROW_SHIFT 3
|
||||||
|
#define STMPE_KEYPAD_KEYMAP_SIZE \
|
||||||
|
(STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stmpe_keypad_variant - model-specific attributes
|
||||||
|
* @auto_increment: whether the KPC_DATA_BYTE register address
|
||||||
|
* auto-increments on multiple read
|
||||||
|
* @num_data: number of data bytes
|
||||||
|
* @num_normal_data: number of normal keys' data bytes
|
||||||
|
* @max_cols: maximum number of columns supported
|
||||||
|
* @max_rows: maximum number of rows supported
|
||||||
|
* @col_gpios: bitmask of gpios which can be used for columns
|
||||||
|
* @row_gpios: bitmask of gpios which can be used for rows
|
||||||
|
*/
|
||||||
|
struct stmpe_keypad_variant {
|
||||||
|
bool auto_increment;
|
||||||
|
int num_data;
|
||||||
|
int num_normal_data;
|
||||||
|
int max_cols;
|
||||||
|
int max_rows;
|
||||||
|
unsigned int col_gpios;
|
||||||
|
unsigned int row_gpios;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
|
||||||
|
[STMPE1601] = {
|
||||||
|
.auto_increment = true,
|
||||||
|
.num_data = 5,
|
||||||
|
.num_normal_data = 3,
|
||||||
|
.max_cols = 8,
|
||||||
|
.max_rows = 8,
|
||||||
|
.col_gpios = 0x000ff, /* GPIO 0 - 7 */
|
||||||
|
.row_gpios = 0x0ff00, /* GPIO 8 - 15 */
|
||||||
|
},
|
||||||
|
[STMPE2401] = {
|
||||||
|
.auto_increment = false,
|
||||||
|
.num_data = 3,
|
||||||
|
.num_normal_data = 2,
|
||||||
|
.max_cols = 8,
|
||||||
|
.max_rows = 12,
|
||||||
|
.col_gpios = 0x0000ff, /* GPIO 0 - 7*/
|
||||||
|
.row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */
|
||||||
|
},
|
||||||
|
[STMPE2403] = {
|
||||||
|
.auto_increment = true,
|
||||||
|
.num_data = 5,
|
||||||
|
.num_normal_data = 3,
|
||||||
|
.max_cols = 8,
|
||||||
|
.max_rows = 12,
|
||||||
|
.col_gpios = 0x0000ff, /* GPIO 0 - 7*/
|
||||||
|
.row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stmpe_keypad {
|
||||||
|
struct stmpe *stmpe;
|
||||||
|
struct input_dev *input;
|
||||||
|
const struct stmpe_keypad_variant *variant;
|
||||||
|
const struct stmpe_keypad_platform_data *plat;
|
||||||
|
|
||||||
|
unsigned int rows;
|
||||||
|
unsigned int cols;
|
||||||
|
|
||||||
|
unsigned short keymap[STMPE_KEYPAD_KEYMAP_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data)
|
||||||
|
{
|
||||||
|
const struct stmpe_keypad_variant *variant = keypad->variant;
|
||||||
|
struct stmpe *stmpe = keypad->stmpe;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (variant->auto_increment)
|
||||||
|
return stmpe_block_read(stmpe, STMPE_KPC_DATA_BYTE0,
|
||||||
|
variant->num_data, data);
|
||||||
|
|
||||||
|
for (i = 0; i < variant->num_data; i++) {
|
||||||
|
ret = stmpe_reg_read(stmpe, STMPE_KPC_DATA_BYTE0 + i);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
data[i] = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t stmpe_keypad_irq(int irq, void *dev)
|
||||||
|
{
|
||||||
|
struct stmpe_keypad *keypad = dev;
|
||||||
|
struct input_dev *input = keypad->input;
|
||||||
|
const struct stmpe_keypad_variant *variant = keypad->variant;
|
||||||
|
u8 fifo[variant->num_data];
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ret = stmpe_keypad_read_data(keypad, fifo);
|
||||||
|
if (ret < 0)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
for (i = 0; i < variant->num_normal_data; i++) {
|
||||||
|
u8 data = fifo[i];
|
||||||
|
int row = (data & STMPE_KPC_DATA_ROW) >> 3;
|
||||||
|
int col = data & STMPE_KPC_DATA_COL;
|
||||||
|
int code = MATRIX_SCAN_CODE(row, col, STMPE_KEYPAD_ROW_SHIFT);
|
||||||
|
bool up = data & STMPE_KPC_DATA_UP;
|
||||||
|
|
||||||
|
if ((data & STMPE_KPC_DATA_NOKEY_MASK)
|
||||||
|
== STMPE_KPC_DATA_NOKEY_MASK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
input_event(input, EV_MSC, MSC_SCAN, code);
|
||||||
|
input_report_key(input, keypad->keymap[code], !up);
|
||||||
|
input_sync(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad)
|
||||||
|
{
|
||||||
|
const struct stmpe_keypad_variant *variant = keypad->variant;
|
||||||
|
unsigned int col_gpios = variant->col_gpios;
|
||||||
|
unsigned int row_gpios = variant->row_gpios;
|
||||||
|
struct stmpe *stmpe = keypad->stmpe;
|
||||||
|
unsigned int pins = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Figure out which pins need to be set to the keypad alternate
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* {cols,rows}_gpios are bitmasks of which pins on the chip can be used
|
||||||
|
* for the keypad.
|
||||||
|
*
|
||||||
|
* keypad->{cols,rows} are a bitmask of which pins (of the ones useable
|
||||||
|
* for the keypad) are used on the board.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (i = 0; i < variant->max_cols; i++) {
|
||||||
|
int num = __ffs(col_gpios);
|
||||||
|
|
||||||
|
if (keypad->cols & (1 << i))
|
||||||
|
pins |= 1 << num;
|
||||||
|
|
||||||
|
col_gpios &= ~(1 << num);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < variant->max_rows; i++) {
|
||||||
|
int num = __ffs(row_gpios);
|
||||||
|
|
||||||
|
if (keypad->rows & (1 << i))
|
||||||
|
pins |= 1 << num;
|
||||||
|
|
||||||
|
row_gpios &= ~(1 << num);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_keypad_chip_init(struct stmpe_keypad *keypad)
|
||||||
|
{
|
||||||
|
const struct stmpe_keypad_platform_data *plat = keypad->plat;
|
||||||
|
const struct stmpe_keypad_variant *variant = keypad->variant;
|
||||||
|
struct stmpe *stmpe = keypad->stmpe;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (plat->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (plat->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = stmpe_keypad_altfunc_init(keypad);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = stmpe_reg_write(stmpe, STMPE_KPC_COL, keypad->cols);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = stmpe_reg_write(stmpe, STMPE_KPC_ROW_LSB, keypad->rows);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (variant->max_rows > 8) {
|
||||||
|
ret = stmpe_set_bits(stmpe, STMPE_KPC_ROW_MSB,
|
||||||
|
STMPE_KPC_ROW_MSB_ROWS,
|
||||||
|
keypad->rows >> 8);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stmpe_set_bits(stmpe, STMPE_KPC_CTRL_MSB,
|
||||||
|
STMPE_KPC_CTRL_MSB_SCAN_COUNT,
|
||||||
|
plat->scan_count << 4);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return stmpe_set_bits(stmpe, STMPE_KPC_CTRL_LSB,
|
||||||
|
STMPE_KPC_CTRL_LSB_SCAN |
|
||||||
|
STMPE_KPC_CTRL_LSB_DEBOUNCE,
|
||||||
|
STMPE_KPC_CTRL_LSB_SCAN |
|
||||||
|
(plat->debounce_ms << 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_keypad_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
struct stmpe_keypad_platform_data *plat;
|
||||||
|
struct stmpe_keypad *keypad;
|
||||||
|
struct input_dev *input;
|
||||||
|
int ret;
|
||||||
|
int irq;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
plat = stmpe->pdata->keypad;
|
||||||
|
if (!plat)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
|
||||||
|
keypad = kzalloc(sizeof(struct stmpe_keypad), GFP_KERNEL);
|
||||||
|
if (!keypad)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
input = input_allocate_device();
|
||||||
|
if (!input) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out_freekeypad;
|
||||||
|
}
|
||||||
|
|
||||||
|
input->name = "STMPE keypad";
|
||||||
|
input->id.bustype = BUS_I2C;
|
||||||
|
input->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||||
|
|
||||||
|
__set_bit(EV_KEY, input->evbit);
|
||||||
|
if (!plat->no_autorepeat)
|
||||||
|
__set_bit(EV_REP, input->evbit);
|
||||||
|
|
||||||
|
input->keycode = keypad->keymap;
|
||||||
|
input->keycodesize = sizeof(keypad->keymap[0]);
|
||||||
|
input->keycodemax = ARRAY_SIZE(keypad->keymap);
|
||||||
|
|
||||||
|
matrix_keypad_build_keymap(plat->keymap_data, STMPE_KEYPAD_ROW_SHIFT,
|
||||||
|
input->keycode, input->keybit);
|
||||||
|
|
||||||
|
for (i = 0; i < plat->keymap_data->keymap_size; i++) {
|
||||||
|
unsigned int key = plat->keymap_data->keymap[i];
|
||||||
|
|
||||||
|
keypad->cols |= 1 << KEY_COL(key);
|
||||||
|
keypad->rows |= 1 << KEY_ROW(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
keypad->stmpe = stmpe;
|
||||||
|
keypad->plat = plat;
|
||||||
|
keypad->input = input;
|
||||||
|
keypad->variant = &stmpe_keypad_variants[stmpe->partnum];
|
||||||
|
|
||||||
|
ret = stmpe_keypad_chip_init(keypad);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out_freeinput;
|
||||||
|
|
||||||
|
ret = input_register_device(input);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"unable to register input device: %d\n", ret);
|
||||||
|
goto out_freeinput;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = request_threaded_irq(irq, NULL, stmpe_keypad_irq, IRQF_ONESHOT,
|
||||||
|
"stmpe-keypad", keypad);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
|
||||||
|
goto out_unregisterinput;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, keypad);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_unregisterinput:
|
||||||
|
input_unregister_device(input);
|
||||||
|
input = NULL;
|
||||||
|
out_freeinput:
|
||||||
|
input_free_device(input);
|
||||||
|
out_freekeypad:
|
||||||
|
kfree(keypad);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit stmpe_keypad_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
|
||||||
|
struct stmpe *stmpe = keypad->stmpe;
|
||||||
|
int irq = platform_get_irq(pdev, 0);
|
||||||
|
|
||||||
|
stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD);
|
||||||
|
|
||||||
|
free_irq(irq, keypad);
|
||||||
|
input_unregister_device(keypad->input);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
kfree(keypad);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver stmpe_keypad_driver = {
|
||||||
|
.driver.name = "stmpe-keypad",
|
||||||
|
.driver.owner = THIS_MODULE,
|
||||||
|
.probe = stmpe_keypad_probe,
|
||||||
|
.remove = __devexit_p(stmpe_keypad_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init stmpe_keypad_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&stmpe_keypad_driver);
|
||||||
|
}
|
||||||
|
module_init(stmpe_keypad_init);
|
||||||
|
|
||||||
|
static void __exit stmpe_keypad_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&stmpe_keypad_driver);
|
||||||
|
}
|
||||||
|
module_exit(stmpe_keypad_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_DESCRIPTION("STMPExxxx keypad driver");
|
||||||
|
MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
|
|
@ -628,4 +628,14 @@ config TOUCHSCREEN_TPS6507X
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called tps6507x_ts.
|
module will be called tps6507x_ts.
|
||||||
|
|
||||||
|
config TOUCHSCREEN_STMPE
|
||||||
|
tristate "STMicroelectronics STMPE touchscreens"
|
||||||
|
depends on MFD_STMPE
|
||||||
|
help
|
||||||
|
Say Y here if you want support for STMicroelectronics
|
||||||
|
STMPE touchscreen controllers.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called stmpe-ts.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -36,6 +36,7 @@ obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o
|
obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
|
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
|
||||||
|
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
|
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
|
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
|
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
|
||||||
|
|
|
@ -0,0 +1,397 @@
|
||||||
|
/* STMicroelectronics STMPE811 Touchscreen Driver
|
||||||
|
*
|
||||||
|
* (C) 2010 Luotao Fu <l.fu@pengutronix.de>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#include <linux/mfd/stmpe.h>
|
||||||
|
|
||||||
|
/* Register layouts and functionalities are identical on all stmpexxx variants
|
||||||
|
* with touchscreen controller
|
||||||
|
*/
|
||||||
|
#define STMPE_REG_INT_STA 0x0B
|
||||||
|
#define STMPE_REG_ADC_CTRL1 0x20
|
||||||
|
#define STMPE_REG_ADC_CTRL2 0x21
|
||||||
|
#define STMPE_REG_TSC_CTRL 0x40
|
||||||
|
#define STMPE_REG_TSC_CFG 0x41
|
||||||
|
#define STMPE_REG_FIFO_TH 0x4A
|
||||||
|
#define STMPE_REG_FIFO_STA 0x4B
|
||||||
|
#define STMPE_REG_FIFO_SIZE 0x4C
|
||||||
|
#define STMPE_REG_TSC_DATA_XYZ 0x52
|
||||||
|
#define STMPE_REG_TSC_FRACTION_Z 0x56
|
||||||
|
#define STMPE_REG_TSC_I_DRIVE 0x58
|
||||||
|
|
||||||
|
#define OP_MOD_XYZ 0
|
||||||
|
|
||||||
|
#define STMPE_TSC_CTRL_TSC_EN (1<<0)
|
||||||
|
|
||||||
|
#define STMPE_FIFO_STA_RESET (1<<0)
|
||||||
|
|
||||||
|
#define STMPE_IRQ_TOUCH_DET 0
|
||||||
|
|
||||||
|
#define SAMPLE_TIME(x) ((x & 0xf) << 4)
|
||||||
|
#define MOD_12B(x) ((x & 0x1) << 3)
|
||||||
|
#define REF_SEL(x) ((x & 0x1) << 1)
|
||||||
|
#define ADC_FREQ(x) (x & 0x3)
|
||||||
|
#define AVE_CTRL(x) ((x & 0x3) << 6)
|
||||||
|
#define DET_DELAY(x) ((x & 0x7) << 3)
|
||||||
|
#define SETTLING(x) (x & 0x7)
|
||||||
|
#define FRACTION_Z(x) (x & 0x7)
|
||||||
|
#define I_DRIVE(x) (x & 0x1)
|
||||||
|
#define OP_MODE(x) ((x & 0x7) << 1)
|
||||||
|
|
||||||
|
#define STMPE_TS_NAME "stmpe-ts"
|
||||||
|
#define XY_MASK 0xfff
|
||||||
|
|
||||||
|
struct stmpe_touch {
|
||||||
|
struct stmpe *stmpe;
|
||||||
|
struct input_dev *idev;
|
||||||
|
struct delayed_work work;
|
||||||
|
struct device *dev;
|
||||||
|
u8 sample_time;
|
||||||
|
u8 mod_12b;
|
||||||
|
u8 ref_sel;
|
||||||
|
u8 adc_freq;
|
||||||
|
u8 ave_ctrl;
|
||||||
|
u8 touch_det_delay;
|
||||||
|
u8 settling;
|
||||||
|
u8 fraction_z;
|
||||||
|
u8 i_drive;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __stmpe_reset_fifo(struct stmpe *stmpe)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
|
||||||
|
STMPE_FIFO_STA_RESET, STMPE_FIFO_STA_RESET);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
|
||||||
|
STMPE_FIFO_STA_RESET, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
int int_sta;
|
||||||
|
u32 timeout = 40;
|
||||||
|
|
||||||
|
struct stmpe_touch *ts =
|
||||||
|
container_of(work, struct stmpe_touch, work.work);
|
||||||
|
|
||||||
|
int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* touch_det sometimes get desasserted or just get stuck. This appears
|
||||||
|
* to be a silicon bug, We still have to clearify this with the
|
||||||
|
* manufacture. As a workaround We release the key anyway if the
|
||||||
|
* touch_det keeps coming in after 4ms, while the FIFO contains no value
|
||||||
|
* during the whole time.
|
||||||
|
*/
|
||||||
|
while ((int_sta & (1 << STMPE_IRQ_TOUCH_DET)) && (timeout > 0)) {
|
||||||
|
timeout--;
|
||||||
|
int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
|
||||||
|
udelay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset the FIFO before we report release event */
|
||||||
|
__stmpe_reset_fifo(ts->stmpe);
|
||||||
|
|
||||||
|
input_report_abs(ts->idev, ABS_PRESSURE, 0);
|
||||||
|
input_sync(ts->idev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t stmpe_ts_handler(int irq, void *data)
|
||||||
|
{
|
||||||
|
u8 data_set[4];
|
||||||
|
int x, y, z;
|
||||||
|
struct stmpe_touch *ts = data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cancel scheduled polling for release if we have new value
|
||||||
|
* available. Wait if the polling is already running.
|
||||||
|
*/
|
||||||
|
cancel_delayed_work_sync(&ts->work);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The FIFO sometimes just crashes and stops generating interrupts. This
|
||||||
|
* appears to be a silicon bug. We still have to clearify this with
|
||||||
|
* the manufacture. As a workaround we disable the TSC while we are
|
||||||
|
* collecting data and flush the FIFO after reading
|
||||||
|
*/
|
||||||
|
stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
|
||||||
|
STMPE_TSC_CTRL_TSC_EN, 0);
|
||||||
|
|
||||||
|
stmpe_block_read(ts->stmpe, STMPE_REG_TSC_DATA_XYZ, 4, data_set);
|
||||||
|
|
||||||
|
x = (data_set[0] << 4) | (data_set[1] >> 4);
|
||||||
|
y = ((data_set[1] & 0xf) << 8) | data_set[2];
|
||||||
|
z = data_set[3];
|
||||||
|
|
||||||
|
input_report_abs(ts->idev, ABS_X, x);
|
||||||
|
input_report_abs(ts->idev, ABS_Y, y);
|
||||||
|
input_report_abs(ts->idev, ABS_PRESSURE, z);
|
||||||
|
input_sync(ts->idev);
|
||||||
|
|
||||||
|
/* flush the FIFO after we have read out our values. */
|
||||||
|
__stmpe_reset_fifo(ts->stmpe);
|
||||||
|
|
||||||
|
/* reenable the tsc */
|
||||||
|
stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
|
||||||
|
STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
|
||||||
|
|
||||||
|
/* start polling for touch_det to detect release */
|
||||||
|
schedule_delayed_work(&ts->work, HZ / 50);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_init_hw(struct stmpe_touch *ts)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask;
|
||||||
|
struct stmpe *stmpe = ts->stmpe;
|
||||||
|
struct device *dev = ts->dev;
|
||||||
|
|
||||||
|
ret = stmpe_enable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Could not enable clock for ADC and TS\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) |
|
||||||
|
REF_SEL(ts->ref_sel);
|
||||||
|
adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff);
|
||||||
|
|
||||||
|
ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1,
|
||||||
|
adc_ctrl1_mask, adc_ctrl1);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Could not setup ADC\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2,
|
||||||
|
ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq));
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Could not setup ADC\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) |
|
||||||
|
SETTLING(ts->settling);
|
||||||
|
tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff);
|
||||||
|
|
||||||
|
ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Could not config touch\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z,
|
||||||
|
FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z));
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Could not config touch\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE,
|
||||||
|
I_DRIVE(0xff), I_DRIVE(ts->i_drive));
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Could not config touch\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set FIFO to 1 for single point reading */
|
||||||
|
ret = stmpe_reg_write(stmpe, STMPE_REG_FIFO_TH, 1);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Could not set FIFO\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL,
|
||||||
|
OP_MODE(0xff), OP_MODE(OP_MOD_XYZ));
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Could not set mode\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe_ts_open(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct stmpe_touch *ts = input_get_drvdata(dev);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = __stmpe_reset_fifo(ts->stmpe);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
|
||||||
|
STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_ts_close(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct stmpe_touch *ts = input_get_drvdata(dev);
|
||||||
|
|
||||||
|
cancel_delayed_work_sync(&ts->work);
|
||||||
|
|
||||||
|
stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
|
||||||
|
STMPE_TSC_CTRL_TSC_EN, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_input_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
struct stmpe_platform_data *pdata = stmpe->pdata;
|
||||||
|
struct stmpe_touch *ts;
|
||||||
|
struct input_dev *idev;
|
||||||
|
struct stmpe_ts_platform_data *ts_pdata = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
int ts_irq;
|
||||||
|
|
||||||
|
ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
|
||||||
|
if (ts_irq < 0)
|
||||||
|
return ts_irq;
|
||||||
|
|
||||||
|
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
|
||||||
|
if (!ts)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
idev = input_allocate_device();
|
||||||
|
if (!idev)
|
||||||
|
goto err_free_ts;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, ts);
|
||||||
|
ts->stmpe = stmpe;
|
||||||
|
ts->idev = idev;
|
||||||
|
ts->dev = &pdev->dev;
|
||||||
|
|
||||||
|
if (pdata)
|
||||||
|
ts_pdata = pdata->ts;
|
||||||
|
|
||||||
|
if (ts_pdata) {
|
||||||
|
ts->sample_time = ts_pdata->sample_time;
|
||||||
|
ts->mod_12b = ts_pdata->mod_12b;
|
||||||
|
ts->ref_sel = ts_pdata->ref_sel;
|
||||||
|
ts->adc_freq = ts_pdata->adc_freq;
|
||||||
|
ts->ave_ctrl = ts_pdata->ave_ctrl;
|
||||||
|
ts->touch_det_delay = ts_pdata->touch_det_delay;
|
||||||
|
ts->settling = ts_pdata->settling;
|
||||||
|
ts->fraction_z = ts_pdata->fraction_z;
|
||||||
|
ts->i_drive = ts_pdata->i_drive;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_DELAYED_WORK(&ts->work, stmpe_work);
|
||||||
|
|
||||||
|
ret = request_threaded_irq(ts_irq, NULL, stmpe_ts_handler,
|
||||||
|
IRQF_ONESHOT, STMPE_TS_NAME, ts);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Failed to request IRQ %d\n", ts_irq);
|
||||||
|
goto err_free_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stmpe_init_hw(ts);
|
||||||
|
if (ret)
|
||||||
|
goto err_free_irq;
|
||||||
|
|
||||||
|
idev->name = STMPE_TS_NAME;
|
||||||
|
idev->id.bustype = BUS_I2C;
|
||||||
|
idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||||
|
idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||||
|
|
||||||
|
idev->open = stmpe_ts_open;
|
||||||
|
idev->close = stmpe_ts_close;
|
||||||
|
|
||||||
|
input_set_drvdata(idev, ts);
|
||||||
|
|
||||||
|
input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0);
|
||||||
|
input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0);
|
||||||
|
input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0);
|
||||||
|
|
||||||
|
ret = input_register_device(idev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Could not register input device\n");
|
||||||
|
goto err_free_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err_free_irq:
|
||||||
|
free_irq(ts_irq, ts);
|
||||||
|
err_free_input:
|
||||||
|
input_free_device(idev);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
err_free_ts:
|
||||||
|
kfree(ts);
|
||||||
|
err_out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit stmpe_ts_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct stmpe_touch *ts = platform_get_drvdata(pdev);
|
||||||
|
unsigned int ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
|
||||||
|
|
||||||
|
stmpe_disable(ts->stmpe, STMPE_BLOCK_TOUCHSCREEN);
|
||||||
|
|
||||||
|
free_irq(ts_irq, ts);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
|
input_unregister_device(ts->idev);
|
||||||
|
input_free_device(ts->idev);
|
||||||
|
|
||||||
|
kfree(ts);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver stmpe_ts_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = STMPE_TS_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = stmpe_input_probe,
|
||||||
|
.remove = __devexit_p(stmpe_ts_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init stmpe_ts_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&stmpe_ts_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(stmpe_ts_init);
|
||||||
|
|
||||||
|
static void __exit stmpe_ts_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&stmpe_ts_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_exit(stmpe_ts_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
|
||||||
|
MODULE_DESCRIPTION("STMPEXXX touchscreen driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:" STMPE_TS_NAME);
|
|
@ -74,12 +74,12 @@ static struct mfd_cell backlight_devs[] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct resource led_resources[] = {
|
static struct resource led_resources[] = {
|
||||||
PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB2B),
|
PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB1B),
|
||||||
PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB2C),
|
PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB1C),
|
||||||
PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB2D),
|
PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB1D),
|
||||||
PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB1B),
|
PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB2B),
|
||||||
PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB1C),
|
PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB2C),
|
||||||
PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB1D),
|
PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB2D),
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PM8606_LED_DEVS(_i) \
|
#define PM8606_LED_DEVS(_i) \
|
||||||
|
@ -428,52 +428,44 @@ static int __devinit device_gpadc_init(struct pm860x_chip *chip,
|
||||||
{
|
{
|
||||||
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
|
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
|
||||||
: chip->companion;
|
: chip->companion;
|
||||||
int use_gpadc = 0, data, ret;
|
int data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* initialize GPADC without activating it */
|
/* initialize GPADC without activating it */
|
||||||
|
|
||||||
if (pdata && pdata->touch) {
|
if (!pdata || !pdata->touch)
|
||||||
/* set GPADC MISC1 register */
|
return -EINVAL;
|
||||||
data = 0;
|
|
||||||
data |= (pdata->touch->gpadc_prebias << 1)
|
|
||||||
& PM8607_GPADC_PREBIAS_MASK;
|
|
||||||
data |= (pdata->touch->slot_cycle << 3)
|
|
||||||
& PM8607_GPADC_SLOT_CYCLE_MASK;
|
|
||||||
data |= (pdata->touch->off_scale << 5)
|
|
||||||
& PM8607_GPADC_OFF_SCALE_MASK;
|
|
||||||
data |= (pdata->touch->sw_cal << 7)
|
|
||||||
& PM8607_GPADC_SW_CAL_MASK;
|
|
||||||
if (data) {
|
|
||||||
ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
|
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* set tsi prebias time */
|
|
||||||
if (pdata->touch->tsi_prebias) {
|
|
||||||
data = pdata->touch->tsi_prebias;
|
|
||||||
ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
|
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
/* set prebias & prechg time of pen detect */
|
|
||||||
data = 0;
|
|
||||||
data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
|
|
||||||
data |= (pdata->touch->pen_prechg << 5)
|
|
||||||
& PM8607_PD_PRECHG_MASK;
|
|
||||||
if (data) {
|
|
||||||
ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
|
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
use_gpadc = 1;
|
/* set GPADC MISC1 register */
|
||||||
|
data = 0;
|
||||||
|
data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
|
||||||
|
data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
|
||||||
|
data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
|
||||||
|
data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
|
||||||
|
if (data) {
|
||||||
|
ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* set tsi prebias time */
|
||||||
|
if (pdata->touch->tsi_prebias) {
|
||||||
|
data = pdata->touch->tsi_prebias;
|
||||||
|
ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* set prebias & prechg time of pen detect */
|
||||||
|
data = 0;
|
||||||
|
data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
|
||||||
|
data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
|
||||||
|
if (data) {
|
||||||
|
ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* turn on GPADC */
|
ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
|
||||||
if (use_gpadc) {
|
PM8607_GPADC_EN, PM8607_GPADC_EN);
|
||||||
ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
|
|
||||||
PM8607_GPADC_EN, PM8607_GPADC_EN);
|
|
||||||
}
|
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,16 @@ menuconfig MFD_SUPPORT
|
||||||
depends on HAS_IOMEM
|
depends on HAS_IOMEM
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
Configure MFD device drivers.
|
Multifunction devices embed several functions (e.g. GPIOs,
|
||||||
|
touchscreens, keyboards, current regulators, power management chips,
|
||||||
|
etc...) in one single integrated circuit. They usually talk to the
|
||||||
|
main CPU through one or more IRQ lines and low speed data busses (SPI,
|
||||||
|
I2C, etc..). They appear as one single device to the main system
|
||||||
|
through the data bus and the MFD framework allows for sub devices
|
||||||
|
(a.k.a. functions) to appear as discrete platform devices.
|
||||||
|
MFDs are typically found on embedded platforms.
|
||||||
|
|
||||||
|
This option alone does not add any kernel code.
|
||||||
|
|
||||||
if MFD_SUPPORT
|
if MFD_SUPPORT
|
||||||
|
|
||||||
|
@ -177,6 +186,38 @@ config TWL4030_CODEC
|
||||||
select MFD_CORE
|
select MFD_CORE
|
||||||
default n
|
default n
|
||||||
|
|
||||||
|
config TWL6030_PWM
|
||||||
|
tristate "TWL6030 PWM (Pulse Width Modulator) Support"
|
||||||
|
depends on TWL4030_CORE
|
||||||
|
select HAVE_PWM
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Say yes here if you want support for TWL6030 PWM.
|
||||||
|
This is used to control charging LED brightness.
|
||||||
|
|
||||||
|
config MFD_STMPE
|
||||||
|
bool "Support STMicroelectronics STMPE"
|
||||||
|
depends on I2C=y && GENERIC_HARDIRQS
|
||||||
|
select MFD_CORE
|
||||||
|
help
|
||||||
|
Support for the STMPE family of I/O Expanders from
|
||||||
|
STMicroelectronics.
|
||||||
|
|
||||||
|
Currently supported devices are:
|
||||||
|
|
||||||
|
STMPE811: GPIO, Touchscreen
|
||||||
|
STMPE1601: GPIO, Keypad
|
||||||
|
STMPE2401: GPIO, Keypad
|
||||||
|
STMPE2403: GPIO, Keypad
|
||||||
|
|
||||||
|
This driver provides common support for accessing the device,
|
||||||
|
additional drivers must be enabled in order to use the functionality
|
||||||
|
of the device. Currently available sub drivers are:
|
||||||
|
|
||||||
|
GPIO: stmpe-gpio
|
||||||
|
Keypad: stmpe-keypad
|
||||||
|
Touchscreen: stmpe-ts
|
||||||
|
|
||||||
config MFD_TC35892
|
config MFD_TC35892
|
||||||
bool "Support Toshiba TC35892"
|
bool "Support Toshiba TC35892"
|
||||||
depends on I2C=y && GENERIC_HARDIRQS
|
depends on I2C=y && GENERIC_HARDIRQS
|
||||||
|
@ -482,6 +523,28 @@ config MFD_JANZ_CMODIO
|
||||||
host many different types of MODULbus daughterboards, including
|
host many different types of MODULbus daughterboards, including
|
||||||
CAN and GPIO controllers.
|
CAN and GPIO controllers.
|
||||||
|
|
||||||
|
config MFD_JZ4740_ADC
|
||||||
|
tristate "Support for the JZ4740 SoC ADC core"
|
||||||
|
select MFD_CORE
|
||||||
|
depends on MACH_JZ4740
|
||||||
|
help
|
||||||
|
Say yes here if you want support for the ADC unit in the JZ4740 SoC.
|
||||||
|
This driver is necessary for jz4740-battery and jz4740-hwmon driver.
|
||||||
|
|
||||||
|
config MFD_TPS6586X
|
||||||
|
tristate "TPS6586x Power Management chips"
|
||||||
|
depends on I2C && GPIOLIB
|
||||||
|
select MFD_CORE
|
||||||
|
help
|
||||||
|
If you say yes here you get support for the TPS6586X series of
|
||||||
|
Power Management chips.
|
||||||
|
This driver provides common support for accessing the device,
|
||||||
|
additional drivers must be enabled in order to use the
|
||||||
|
functionality of the device.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called tps6586x.
|
||||||
|
|
||||||
endif # MFD_SUPPORT
|
endif # MFD_SUPPORT
|
||||||
|
|
||||||
menu "Multimedia Capabilities Port drivers"
|
menu "Multimedia Capabilities Port drivers"
|
||||||
|
|
|
@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
|
||||||
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
|
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
|
||||||
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
|
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_MFD_STMPE) += stmpe.o
|
||||||
obj-$(CONFIG_MFD_TC35892) += tc35892.o
|
obj-$(CONFIG_MFD_TC35892) += tc35892.o
|
||||||
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
|
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
|
||||||
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
|
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
|
||||||
|
@ -36,6 +37,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
|
||||||
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
|
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
|
||||||
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
|
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
|
||||||
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
|
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
|
||||||
|
obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
|
||||||
|
|
||||||
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
|
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
|
||||||
|
|
||||||
|
@ -71,3 +73,5 @@ obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
|
||||||
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
|
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
|
||||||
obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
|
obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
|
||||||
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
|
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
|
||||||
|
obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
|
||||||
|
obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
|
||||||
|
|
|
@ -199,7 +199,7 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err = ab3100_otp_read(otp);
|
err = ab3100_otp_read(otp);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto err_otp_read;
|
||||||
|
|
||||||
dev_info(&pdev->dev, "AB3100 OTP readout registered\n");
|
dev_info(&pdev->dev, "AB3100 OTP readout registered\n");
|
||||||
|
|
||||||
|
@ -208,21 +208,21 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
|
||||||
err = device_create_file(&pdev->dev,
|
err = device_create_file(&pdev->dev,
|
||||||
&ab3100_otp_attrs[i]);
|
&ab3100_otp_attrs[i]);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_no_sysfs;
|
goto err_create_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* debugfs entries */
|
/* debugfs entries */
|
||||||
err = ab3100_otp_init_debugfs(&pdev->dev, otp);
|
err = ab3100_otp_init_debugfs(&pdev->dev, otp);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_no_debugfs;
|
goto err_init_debugfs;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_no_sysfs:
|
err_init_debugfs:
|
||||||
for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++)
|
err_create_file:
|
||||||
device_remove_file(&pdev->dev,
|
while (--i >= 0)
|
||||||
&ab3100_otp_attrs[i]);
|
device_remove_file(&pdev->dev, &ab3100_otp_attrs[i]);
|
||||||
out_no_debugfs:
|
err_otp_read:
|
||||||
kfree(otp);
|
kfree(otp);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -589,16 +589,16 @@ static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The exported register access functionality.
|
* The register access functionality.
|
||||||
*/
|
*/
|
||||||
int ab3550_get_chip_id(struct device *dev)
|
static int ab3550_get_chip_id(struct device *dev)
|
||||||
{
|
{
|
||||||
struct ab3550 *ab = dev_get_drvdata(dev->parent);
|
struct ab3550 *ab = dev_get_drvdata(dev->parent);
|
||||||
return (int)ab->chip_id;
|
return (int)ab->chip_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank,
|
static int ab3550_mask_and_set_register_interruptible(struct device *dev,
|
||||||
u8 reg, u8 bitmask, u8 bitvalues)
|
u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
|
||||||
{
|
{
|
||||||
struct ab3550 *ab;
|
struct ab3550 *ab;
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
@ -612,15 +612,15 @@ int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank,
|
||||||
bitmask, bitvalues);
|
bitmask, bitvalues);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ab3550_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
|
static int ab3550_set_register_interruptible(struct device *dev, u8 bank,
|
||||||
u8 value)
|
u8 reg, u8 value)
|
||||||
{
|
{
|
||||||
return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
|
return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
|
||||||
value);
|
value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
|
static int ab3550_get_register_interruptible(struct device *dev, u8 bank,
|
||||||
u8 *value)
|
u8 reg, u8 *value)
|
||||||
{
|
{
|
||||||
struct ab3550 *ab;
|
struct ab3550 *ab;
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
@ -633,7 +633,7 @@ int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
|
||||||
return get_register_interruptible(ab, bank, reg, value);
|
return get_register_interruptible(ab, bank, reg, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
|
static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
|
||||||
u8 first_reg, u8 *regvals, u8 numregs)
|
u8 first_reg, u8 *regvals, u8 numregs)
|
||||||
{
|
{
|
||||||
struct ab3550 *ab;
|
struct ab3550 *ab;
|
||||||
|
@ -649,7 +649,8 @@ int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
|
||||||
numregs);
|
numregs);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event)
|
static int ab3550_event_registers_startup_state_get(struct device *dev,
|
||||||
|
u8 *event)
|
||||||
{
|
{
|
||||||
struct ab3550 *ab;
|
struct ab3550 *ab;
|
||||||
|
|
||||||
|
@ -661,7 +662,7 @@ int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
|
static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
|
||||||
{
|
{
|
||||||
struct ab3550 *ab;
|
struct ab3550 *ab;
|
||||||
struct ab3550_platform_data *plf_data;
|
struct ab3550_platform_data *plf_data;
|
||||||
|
|
|
@ -68,7 +68,12 @@ static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr)
|
||||||
|
|
||||||
ret = spi_sync(spi, &msg);
|
ret = spi_sync(spi, &msg);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = ab8500->rx_buf[0];
|
/*
|
||||||
|
* Only the 8 lowermost bytes are
|
||||||
|
* defined with value, the rest may
|
||||||
|
* vary depending on chip/board noise.
|
||||||
|
*/
|
||||||
|
ret = ab8500->rx_buf[0] & 0xFFU;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ int abx500_register_ops(struct device *dev, struct abx500_ops *ops)
|
||||||
struct abx500_device_entry *dev_entry;
|
struct abx500_device_entry *dev_entry;
|
||||||
|
|
||||||
dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL);
|
dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL);
|
||||||
if (IS_ERR(dev_entry)) {
|
if (!dev_entry) {
|
||||||
dev_err(dev, "register_ops kzalloc failed");
|
dev_err(dev, "register_ops kzalloc failed");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
|
||||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
dev_err(&pdev->dev, "no DMA resource\n");
|
dev_err(&pdev->dev, "no DMA resource\n");
|
||||||
return -ENXIO;
|
ret = -ENXIO;
|
||||||
|
goto fail4;
|
||||||
}
|
}
|
||||||
|
|
||||||
davinci_vc->davinci_vcif.dma_tx_channel = res->start;
|
davinci_vc->davinci_vcif.dma_tx_channel = res->start;
|
||||||
|
@ -104,7 +105,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
|
||||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
dev_err(&pdev->dev, "no DMA resource\n");
|
dev_err(&pdev->dev, "no DMA resource\n");
|
||||||
return -ENXIO;
|
ret = -ENXIO;
|
||||||
|
goto fail4;
|
||||||
}
|
}
|
||||||
|
|
||||||
davinci_vc->davinci_vcif.dma_rx_channel = res->start;
|
davinci_vc->davinci_vcif.dma_rx_channel = res->start;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
|
|
||||||
#include <linux/mfd/janz.h>
|
#include <linux/mfd/janz.h>
|
||||||
|
|
|
@ -0,0 +1,394 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
|
||||||
|
* JZ4740 SoC ADC driver
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
* This driver synchronizes access to the JZ4740 ADC core between the
|
||||||
|
* JZ4740 battery and hwmon drivers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/mfd/core.h>
|
||||||
|
|
||||||
|
#include <linux/jz4740-adc.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define JZ_REG_ADC_ENABLE 0x00
|
||||||
|
#define JZ_REG_ADC_CFG 0x04
|
||||||
|
#define JZ_REG_ADC_CTRL 0x08
|
||||||
|
#define JZ_REG_ADC_STATUS 0x0c
|
||||||
|
|
||||||
|
#define JZ_REG_ADC_TOUCHSCREEN_BASE 0x10
|
||||||
|
#define JZ_REG_ADC_BATTERY_BASE 0x1c
|
||||||
|
#define JZ_REG_ADC_HWMON_BASE 0x20
|
||||||
|
|
||||||
|
#define JZ_ADC_ENABLE_TOUCH BIT(2)
|
||||||
|
#define JZ_ADC_ENABLE_BATTERY BIT(1)
|
||||||
|
#define JZ_ADC_ENABLE_ADCIN BIT(0)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
JZ_ADC_IRQ_ADCIN = 0,
|
||||||
|
JZ_ADC_IRQ_BATTERY,
|
||||||
|
JZ_ADC_IRQ_TOUCH,
|
||||||
|
JZ_ADC_IRQ_PENUP,
|
||||||
|
JZ_ADC_IRQ_PENDOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct jz4740_adc {
|
||||||
|
struct resource *mem;
|
||||||
|
void __iomem *base;
|
||||||
|
|
||||||
|
int irq;
|
||||||
|
int irq_base;
|
||||||
|
|
||||||
|
struct clk *clk;
|
||||||
|
atomic_t clk_ref;
|
||||||
|
|
||||||
|
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(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct jz4740_adc *adc = get_irq_chip_data(irq);
|
||||||
|
jz4740_adc_irq_set_masked(adc, irq, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jz4740_adc_irq_unmask(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct jz4740_adc *adc = get_irq_chip_data(irq);
|
||||||
|
jz4740_adc_irq_set_masked(adc, irq, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jz4740_adc_irq_ack(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct jz4740_adc *adc = get_irq_chip_data(irq);
|
||||||
|
|
||||||
|
irq -= adc->irq_base;
|
||||||
|
writeb(BIT(irq), adc->base + JZ_REG_ADC_STATUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip jz4740_adc_irq_chip = {
|
||||||
|
.name = "jz4740-adc",
|
||||||
|
.mask = jz4740_adc_irq_mask,
|
||||||
|
.unmask = jz4740_adc_irq_unmask,
|
||||||
|
.ack = jz4740_adc_irq_ack,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc)
|
||||||
|
{
|
||||||
|
struct jz4740_adc *adc = get_irq_desc_data(desc);
|
||||||
|
uint8_t status;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
status = readb(adc->base + JZ_REG_ADC_STATUS);
|
||||||
|
|
||||||
|
for (i = 0; i < 5; ++i) {
|
||||||
|
if (status & BIT(i))
|
||||||
|
generic_handle_irq(adc->irq_base + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Refcounting for the ADC clock is done in here instead of in the clock
|
||||||
|
* framework, because it is the only clock which is shared between multiple
|
||||||
|
* devices and thus is the only clock which needs refcounting */
|
||||||
|
static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc)
|
||||||
|
{
|
||||||
|
if (atomic_inc_return(&adc->clk_ref) == 1)
|
||||||
|
clk_enable(adc->clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc)
|
||||||
|
{
|
||||||
|
if (atomic_dec_return(&adc->clk_ref) == 0)
|
||||||
|
clk_disable(adc->clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine,
|
||||||
|
bool enabled)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
uint8_t val;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&adc->lock, flags);
|
||||||
|
|
||||||
|
val = readb(adc->base + JZ_REG_ADC_ENABLE);
|
||||||
|
if (enabled)
|
||||||
|
val |= BIT(engine);
|
||||||
|
else
|
||||||
|
val &= BIT(engine);
|
||||||
|
writeb(val, adc->base + JZ_REG_ADC_ENABLE);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&adc->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int jz4740_adc_cell_enable(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
|
||||||
|
jz4740_adc_clk_enable(adc);
|
||||||
|
jz4740_adc_set_enabled(adc, pdev->id, true);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int jz4740_adc_cell_disable(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
|
||||||
|
jz4740_adc_set_enabled(adc, pdev->id, false);
|
||||||
|
jz4740_adc_clk_disable(adc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val)
|
||||||
|
{
|
||||||
|
struct jz4740_adc *adc = dev_get_drvdata(dev);
|
||||||
|
unsigned long flags;
|
||||||
|
uint32_t cfg;
|
||||||
|
|
||||||
|
if (!adc)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&adc->lock, flags);
|
||||||
|
|
||||||
|
cfg = readl(adc->base + JZ_REG_ADC_CFG);
|
||||||
|
|
||||||
|
cfg &= ~mask;
|
||||||
|
cfg |= val;
|
||||||
|
|
||||||
|
writel(cfg, adc->base + JZ_REG_ADC_CFG);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&adc->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(jz4740_adc_set_config);
|
||||||
|
|
||||||
|
static struct resource jz4740_hwmon_resources[] = {
|
||||||
|
{
|
||||||
|
.start = JZ_ADC_IRQ_ADCIN,
|
||||||
|
.flags = IORESOURCE_IRQ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.start = JZ_REG_ADC_HWMON_BASE,
|
||||||
|
.end = JZ_REG_ADC_HWMON_BASE + 3,
|
||||||
|
.flags = IORESOURCE_MEM,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct resource jz4740_battery_resources[] = {
|
||||||
|
{
|
||||||
|
.start = JZ_ADC_IRQ_BATTERY,
|
||||||
|
.flags = IORESOURCE_IRQ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.start = JZ_REG_ADC_BATTERY_BASE,
|
||||||
|
.end = JZ_REG_ADC_BATTERY_BASE + 3,
|
||||||
|
.flags = IORESOURCE_MEM,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct mfd_cell jz4740_adc_cells[] = {
|
||||||
|
{
|
||||||
|
.id = 0,
|
||||||
|
.name = "jz4740-hwmon",
|
||||||
|
.num_resources = ARRAY_SIZE(jz4740_hwmon_resources),
|
||||||
|
.resources = jz4740_hwmon_resources,
|
||||||
|
.platform_data = (void *)&jz4740_adc_cells[0],
|
||||||
|
.data_size = sizeof(struct mfd_cell),
|
||||||
|
|
||||||
|
.enable = jz4740_adc_cell_enable,
|
||||||
|
.disable = jz4740_adc_cell_disable,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.id = 1,
|
||||||
|
.name = "jz4740-battery",
|
||||||
|
.num_resources = ARRAY_SIZE(jz4740_battery_resources),
|
||||||
|
.resources = jz4740_battery_resources,
|
||||||
|
.platform_data = (void *)&jz4740_adc_cells[1],
|
||||||
|
.data_size = sizeof(struct mfd_cell),
|
||||||
|
|
||||||
|
.enable = jz4740_adc_cell_enable,
|
||||||
|
.disable = jz4740_adc_cell_disable,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __devinit jz4740_adc_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct jz4740_adc *adc;
|
||||||
|
struct resource *mem_base;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
adc = kmalloc(sizeof(*adc), GFP_KERNEL);
|
||||||
|
if (!adc) {
|
||||||
|
dev_err(&pdev->dev, "Failed to allocate driver structure\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
adc->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (adc->irq < 0) {
|
||||||
|
ret = adc->irq;
|
||||||
|
dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
adc->irq_base = platform_get_irq(pdev, 1);
|
||||||
|
if (adc->irq_base < 0) {
|
||||||
|
ret = adc->irq_base;
|
||||||
|
dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret);
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!mem_base) {
|
||||||
|
ret = -ENOENT;
|
||||||
|
dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only request the shared registers for the MFD driver */
|
||||||
|
adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS,
|
||||||
|
pdev->name);
|
||||||
|
if (!adc->mem) {
|
||||||
|
ret = -EBUSY;
|
||||||
|
dev_err(&pdev->dev, "Failed to request mmio memory region\n");
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem));
|
||||||
|
if (!adc->base) {
|
||||||
|
ret = -EBUSY;
|
||||||
|
dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
|
||||||
|
goto err_release_mem_region;
|
||||||
|
}
|
||||||
|
|
||||||
|
adc->clk = clk_get(&pdev->dev, "adc");
|
||||||
|
if (IS_ERR(adc->clk)) {
|
||||||
|
ret = PTR_ERR(adc->clk);
|
||||||
|
dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
|
||||||
|
goto err_iounmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_init(&adc->lock);
|
||||||
|
atomic_set(&adc->clk_ref, 0);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, adc);
|
||||||
|
|
||||||
|
for (irq = adc->irq_base; irq < adc->irq_base + 5; ++irq) {
|
||||||
|
set_irq_chip_data(irq, adc);
|
||||||
|
set_irq_chip_and_handler(irq, &jz4740_adc_irq_chip,
|
||||||
|
handle_level_irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_irq_data(adc->irq, adc);
|
||||||
|
set_irq_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);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_clk_put;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_clk_put:
|
||||||
|
clk_put(adc->clk);
|
||||||
|
err_iounmap:
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
iounmap(adc->base);
|
||||||
|
err_release_mem_region:
|
||||||
|
release_mem_region(adc->mem->start, resource_size(adc->mem));
|
||||||
|
err_free:
|
||||||
|
kfree(adc);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit jz4740_adc_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct jz4740_adc *adc = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
mfd_remove_devices(&pdev->dev);
|
||||||
|
|
||||||
|
set_irq_data(adc->irq, NULL);
|
||||||
|
set_irq_chained_handler(adc->irq, NULL);
|
||||||
|
|
||||||
|
iounmap(adc->base);
|
||||||
|
release_mem_region(adc->mem->start, resource_size(adc->mem));
|
||||||
|
|
||||||
|
clk_put(adc->clk);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
|
kfree(adc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct platform_driver jz4740_adc_driver = {
|
||||||
|
.probe = jz4740_adc_probe,
|
||||||
|
.remove = __devexit_p(jz4740_adc_remove),
|
||||||
|
.driver = {
|
||||||
|
.name = "jz4740-adc",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init jz4740_adc_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&jz4740_adc_driver);
|
||||||
|
}
|
||||||
|
module_init(jz4740_adc_init);
|
||||||
|
|
||||||
|
static void __exit jz4740_adc_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&jz4740_adc_driver);
|
||||||
|
}
|
||||||
|
module_exit(jz4740_adc_exit);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("JZ4740 SoC ADC driver");
|
||||||
|
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:jz4740-adc");
|
|
@ -90,6 +90,24 @@ static struct mfd_cell rtc_devs[] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct resource onkey_resources[] = {
|
||||||
|
{
|
||||||
|
.name = "max8925-onkey",
|
||||||
|
.start = MAX8925_IRQ_GPM_SW_3SEC,
|
||||||
|
.end = MAX8925_IRQ_GPM_SW_3SEC,
|
||||||
|
.flags = IORESOURCE_IRQ,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mfd_cell onkey_devs[] = {
|
||||||
|
{
|
||||||
|
.name = "max8925-onkey",
|
||||||
|
.num_resources = 1,
|
||||||
|
.resources = &onkey_resources[0],
|
||||||
|
.id = -1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#define MAX8925_REG_RESOURCE(_start, _end) \
|
#define MAX8925_REG_RESOURCE(_start, _end) \
|
||||||
{ \
|
{ \
|
||||||
.start = MAX8925_##_start, \
|
.start = MAX8925_##_start, \
|
||||||
|
@ -596,6 +614,15 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
|
||||||
dev_err(chip->dev, "Failed to add rtc subdev\n");
|
dev_err(chip->dev, "Failed to add rtc subdev\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
|
||||||
|
ARRAY_SIZE(onkey_devs),
|
||||||
|
&onkey_resources[0], 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(chip->dev, "Failed to add onkey subdev\n");
|
||||||
|
goto out_dev;
|
||||||
|
}
|
||||||
|
|
||||||
if (pdata && pdata->regulator[0]) {
|
if (pdata && pdata->regulator[0]) {
|
||||||
ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0],
|
ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0],
|
||||||
ARRAY_SIZE(regulator_devs),
|
ARRAY_SIZE(regulator_devs),
|
||||||
|
|
|
@ -11,9 +11,31 @@
|
||||||
*/
|
*/
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/mfd/mc13783-private.h>
|
#include <linux/mfd/mc13783.h>
|
||||||
|
|
||||||
|
struct mc13783 {
|
||||||
|
struct spi_device *spidev;
|
||||||
|
struct mutex lock;
|
||||||
|
int irq;
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
irq_handler_t irqhandler[MC13783_NUM_IRQ];
|
||||||
|
void *irqdata[MC13783_NUM_IRQ];
|
||||||
|
|
||||||
|
/* XXX these should go as platformdata to the regulator subdevice */
|
||||||
|
struct mc13783_regulator_init_data *regulators;
|
||||||
|
int num_regulators;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MC13783_REG_REVISION 7
|
||||||
|
#define MC13783_REG_ADC_0 43
|
||||||
|
#define MC13783_REG_ADC_1 44
|
||||||
|
#define MC13783_REG_ADC_2 45
|
||||||
|
|
||||||
#define MC13783_IRQSTAT0 0
|
#define MC13783_IRQSTAT0 0
|
||||||
#define MC13783_IRQSTAT0_ADCDONEI (1 << 0)
|
#define MC13783_IRQSTAT0_ADCDONEI (1 << 0)
|
||||||
|
@ -226,6 +248,12 @@ int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mc13783_reg_rmw);
|
EXPORT_SYMBOL(mc13783_reg_rmw);
|
||||||
|
|
||||||
|
int mc13783_get_flags(struct mc13783 *mc13783)
|
||||||
|
{
|
||||||
|
return mc13783->flags;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(mc13783_get_flags);
|
||||||
|
|
||||||
int mc13783_irq_mask(struct mc13783 *mc13783, int irq)
|
int mc13783_irq_mask(struct mc13783 *mc13783, int irq)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
|
@ -128,6 +128,39 @@
|
||||||
#define MENELAUS_RESERVED14_IRQ 14 /* Reserved */
|
#define MENELAUS_RESERVED14_IRQ 14 /* Reserved */
|
||||||
#define MENELAUS_RESERVED15_IRQ 15 /* Reserved */
|
#define MENELAUS_RESERVED15_IRQ 15 /* Reserved */
|
||||||
|
|
||||||
|
/* VCORE_CTRL1 register */
|
||||||
|
#define VCORE_CTRL1_BYP_COMP (1 << 5)
|
||||||
|
#define VCORE_CTRL1_HW_NSW (1 << 7)
|
||||||
|
|
||||||
|
/* GPIO_CTRL register */
|
||||||
|
#define GPIO_CTRL_SLOTSELEN (1 << 5)
|
||||||
|
#define GPIO_CTRL_SLPCTLEN (1 << 6)
|
||||||
|
#define GPIO1_DIR_INPUT (1 << 0)
|
||||||
|
#define GPIO2_DIR_INPUT (1 << 1)
|
||||||
|
#define GPIO3_DIR_INPUT (1 << 2)
|
||||||
|
|
||||||
|
/* MCT_CTRL1 register */
|
||||||
|
#define MCT_CTRL1_S1_CMD_OD (1 << 2)
|
||||||
|
#define MCT_CTRL1_S2_CMD_OD (1 << 3)
|
||||||
|
|
||||||
|
/* MCT_CTRL2 register */
|
||||||
|
#define MCT_CTRL2_VS2_SEL_D0 (1 << 0)
|
||||||
|
#define MCT_CTRL2_VS2_SEL_D1 (1 << 1)
|
||||||
|
#define MCT_CTRL2_S1CD_BUFEN (1 << 4)
|
||||||
|
#define MCT_CTRL2_S2CD_BUFEN (1 << 5)
|
||||||
|
#define MCT_CTRL2_S1CD_DBEN (1 << 6)
|
||||||
|
#define MCT_CTRL2_S2CD_BEN (1 << 7)
|
||||||
|
|
||||||
|
/* MCT_CTRL3 register */
|
||||||
|
#define MCT_CTRL3_SLOT1_EN (1 << 0)
|
||||||
|
#define MCT_CTRL3_SLOT2_EN (1 << 1)
|
||||||
|
#define MCT_CTRL3_S1_AUTO_EN (1 << 2)
|
||||||
|
#define MCT_CTRL3_S2_AUTO_EN (1 << 3)
|
||||||
|
|
||||||
|
/* MCT_PIN_ST register */
|
||||||
|
#define MCT_PIN_ST_S1_CD_ST (1 << 0)
|
||||||
|
#define MCT_PIN_ST_S2_CD_ST (1 << 1)
|
||||||
|
|
||||||
static void menelaus_work(struct work_struct *_menelaus);
|
static void menelaus_work(struct work_struct *_menelaus);
|
||||||
|
|
||||||
struct menelaus_chip {
|
struct menelaus_chip {
|
||||||
|
@ -249,10 +282,10 @@ static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!(reg & 0x1))
|
if (!(reg & 0x1))
|
||||||
card_mask |= (1 << 0);
|
card_mask |= MCT_PIN_ST_S1_CD_ST;
|
||||||
|
|
||||||
if (!(reg & 0x2))
|
if (!(reg & 0x2))
|
||||||
card_mask |= (1 << 1);
|
card_mask |= MCT_PIN_ST_S2_CD_ST;
|
||||||
|
|
||||||
if (menelaus_hw->mmc_callback)
|
if (menelaus_hw->mmc_callback)
|
||||||
menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data,
|
menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data,
|
||||||
|
@ -277,14 +310,14 @@ int menelaus_set_mmc_opendrain(int slot, int enable)
|
||||||
val = ret;
|
val = ret;
|
||||||
if (slot == 1) {
|
if (slot == 1) {
|
||||||
if (enable)
|
if (enable)
|
||||||
val |= 1 << 2;
|
val |= MCT_CTRL1_S1_CMD_OD;
|
||||||
else
|
else
|
||||||
val &= ~(1 << 2);
|
val &= ~MCT_CTRL1_S1_CMD_OD;
|
||||||
} else {
|
} else {
|
||||||
if (enable)
|
if (enable)
|
||||||
val |= 1 << 3;
|
val |= MCT_CTRL1_S2_CMD_OD;
|
||||||
else
|
else
|
||||||
val &= ~(1 << 3);
|
val &= ~MCT_CTRL1_S2_CMD_OD;
|
||||||
}
|
}
|
||||||
ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val);
|
ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val);
|
||||||
mutex_unlock(&the_menelaus->lock);
|
mutex_unlock(&the_menelaus->lock);
|
||||||
|
@ -301,11 +334,11 @@ int menelaus_set_slot_sel(int enable)
|
||||||
ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
|
ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
ret |= 0x02;
|
ret |= GPIO2_DIR_INPUT;
|
||||||
if (enable)
|
if (enable)
|
||||||
ret |= 1 << 5;
|
ret |= GPIO_CTRL_SLOTSELEN;
|
||||||
else
|
else
|
||||||
ret &= ~(1 << 5);
|
ret &= ~GPIO_CTRL_SLOTSELEN;
|
||||||
ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
|
ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&the_menelaus->lock);
|
mutex_unlock(&the_menelaus->lock);
|
||||||
|
@ -330,14 +363,14 @@ int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
|
||||||
val = ret;
|
val = ret;
|
||||||
if (slot == 1) {
|
if (slot == 1) {
|
||||||
if (cd_en)
|
if (cd_en)
|
||||||
val |= (1 << 4) | (1 << 6);
|
val |= MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN;
|
||||||
else
|
else
|
||||||
val &= ~((1 << 4) | (1 << 6));
|
val &= ~(MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN);
|
||||||
} else {
|
} else {
|
||||||
if (cd_en)
|
if (cd_en)
|
||||||
val |= (1 << 5) | (1 << 7);
|
val |= MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN;
|
||||||
else
|
else
|
||||||
val &= ~((1 << 5) | (1 << 7));
|
val &= ~(MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN);
|
||||||
}
|
}
|
||||||
ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val);
|
ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -349,25 +382,25 @@ int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
|
||||||
val = ret;
|
val = ret;
|
||||||
if (slot == 1) {
|
if (slot == 1) {
|
||||||
if (enable)
|
if (enable)
|
||||||
val |= 1 << 0;
|
val |= MCT_CTRL3_SLOT1_EN;
|
||||||
else
|
else
|
||||||
val &= ~(1 << 0);
|
val &= ~MCT_CTRL3_SLOT1_EN;
|
||||||
} else {
|
} else {
|
||||||
int b;
|
int b;
|
||||||
|
|
||||||
if (enable)
|
if (enable)
|
||||||
ret |= 1 << 1;
|
val |= MCT_CTRL3_SLOT2_EN;
|
||||||
else
|
else
|
||||||
ret &= ~(1 << 1);
|
val &= ~MCT_CTRL3_SLOT2_EN;
|
||||||
b = menelaus_read_reg(MENELAUS_MCT_CTRL2);
|
b = menelaus_read_reg(MENELAUS_MCT_CTRL2);
|
||||||
b &= ~0x03;
|
b &= ~(MCT_CTRL2_VS2_SEL_D0 | MCT_CTRL2_VS2_SEL_D1);
|
||||||
b |= power;
|
b |= power;
|
||||||
ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b);
|
ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
/* Disable autonomous shutdown */
|
/* Disable autonomous shutdown */
|
||||||
val &= ~(0x03 << 2);
|
val &= ~(MCT_CTRL3_S1_AUTO_EN | MCT_CTRL3_S2_AUTO_EN);
|
||||||
ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val);
|
ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val);
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&the_menelaus->lock);
|
mutex_unlock(&the_menelaus->lock);
|
||||||
|
@ -552,7 +585,7 @@ int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV)
|
||||||
if (!the_menelaus->vcore_hw_mode) {
|
if (!the_menelaus->vcore_hw_mode) {
|
||||||
val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
|
val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
|
||||||
/* HW mode, turn OFF byte comparator */
|
/* HW mode, turn OFF byte comparator */
|
||||||
val |= ((1 << 7) | (1 << 5));
|
val |= (VCORE_CTRL1_HW_NSW | VCORE_CTRL1_BYP_COMP);
|
||||||
ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
|
ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
|
||||||
the_menelaus->vcore_hw_mode = 1;
|
the_menelaus->vcore_hw_mode = 1;
|
||||||
}
|
}
|
||||||
|
@ -749,7 +782,7 @@ int menelaus_set_regulator_sleep(int enable, u32 val)
|
||||||
ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
|
ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
t = ((1 << 6) | 0x04);
|
t = (GPIO_CTRL_SLPCTLEN | GPIO3_DIR_INPUT);
|
||||||
if (enable)
|
if (enable)
|
||||||
ret |= t;
|
ret |= t;
|
||||||
else
|
else
|
||||||
|
|
|
@ -70,7 +70,9 @@ static int mfd_add_device(struct device *parent, int id,
|
||||||
goto fail_res;
|
goto fail_res;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_device_add_resources(pdev, res, cell->num_resources);
|
ret = platform_device_add_resources(pdev, res, cell->num_resources);
|
||||||
|
if (ret)
|
||||||
|
goto fail_res;
|
||||||
|
|
||||||
ret = platform_device_add(pdev);
|
ret = platform_device_add(pdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
@ -0,0 +1,985 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) ST-Ericsson SA 2010
|
||||||
|
*
|
||||||
|
* License Terms: GNU General Public License, version 2
|
||||||
|
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/mfd/core.h>
|
||||||
|
#include <linux/mfd/stmpe.h>
|
||||||
|
#include "stmpe.h"
|
||||||
|
|
||||||
|
static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
|
||||||
|
{
|
||||||
|
return stmpe->variant->enable(stmpe, blocks, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __stmpe_disable(struct stmpe *stmpe, unsigned int blocks)
|
||||||
|
{
|
||||||
|
return stmpe->variant->enable(stmpe, blocks, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __stmpe_reg_read(struct stmpe *stmpe, u8 reg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_byte_data(stmpe->i2c, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(stmpe->dev, "failed to read reg %#x: %d\n",
|
||||||
|
reg, ret);
|
||||||
|
|
||||||
|
dev_vdbg(stmpe->dev, "rd: reg %#x => data %#x\n", reg, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dev_vdbg(stmpe->dev, "wr: reg %#x <= %#x\n", reg, val);
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(stmpe->i2c, reg, val);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(stmpe->dev, "failed to write reg %#x: %d\n",
|
||||||
|
reg, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = __stmpe_reg_read(stmpe, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret &= ~mask;
|
||||||
|
ret |= val;
|
||||||
|
|
||||||
|
return __stmpe_reg_write(stmpe, reg, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length,
|
||||||
|
u8 *values)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_i2c_block_data(stmpe->i2c, reg, length, values);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(stmpe->dev, "failed to read regs %#x: %d\n",
|
||||||
|
reg, ret);
|
||||||
|
|
||||||
|
dev_vdbg(stmpe->dev, "rd: reg %#x (%d) => ret %#x\n", reg, length, ret);
|
||||||
|
stmpe_dump_bytes("stmpe rd: ", values, length);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
|
||||||
|
const u8 *values)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dev_vdbg(stmpe->dev, "wr: regs %#x (%d)\n", reg, length);
|
||||||
|
stmpe_dump_bytes("stmpe wr: ", values, length);
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_i2c_block_data(stmpe->i2c, reg, length,
|
||||||
|
values);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(stmpe->dev, "failed to write regs %#x: %d\n",
|
||||||
|
reg, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stmpe_enable - enable blocks on an STMPE device
|
||||||
|
* @stmpe: Device to work on
|
||||||
|
* @blocks: Mask of blocks (enum stmpe_block values) to enable
|
||||||
|
*/
|
||||||
|
int stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&stmpe->lock);
|
||||||
|
ret = __stmpe_enable(stmpe, blocks);
|
||||||
|
mutex_unlock(&stmpe->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(stmpe_enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stmpe_disable - disable blocks on an STMPE device
|
||||||
|
* @stmpe: Device to work on
|
||||||
|
* @blocks: Mask of blocks (enum stmpe_block values) to enable
|
||||||
|
*/
|
||||||
|
int stmpe_disable(struct stmpe *stmpe, unsigned int blocks)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&stmpe->lock);
|
||||||
|
ret = __stmpe_disable(stmpe, blocks);
|
||||||
|
mutex_unlock(&stmpe->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(stmpe_disable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stmpe_reg_read() - read a single STMPE register
|
||||||
|
* @stmpe: Device to read from
|
||||||
|
* @reg: Register to read
|
||||||
|
*/
|
||||||
|
int stmpe_reg_read(struct stmpe *stmpe, u8 reg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&stmpe->lock);
|
||||||
|
ret = __stmpe_reg_read(stmpe, reg);
|
||||||
|
mutex_unlock(&stmpe->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(stmpe_reg_read);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stmpe_reg_write() - write a single STMPE register
|
||||||
|
* @stmpe: Device to write to
|
||||||
|
* @reg: Register to write
|
||||||
|
* @val: Value to write
|
||||||
|
*/
|
||||||
|
int stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&stmpe->lock);
|
||||||
|
ret = __stmpe_reg_write(stmpe, reg, val);
|
||||||
|
mutex_unlock(&stmpe->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(stmpe_reg_write);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stmpe_set_bits() - set the value of a bitfield in a STMPE register
|
||||||
|
* @stmpe: Device to write to
|
||||||
|
* @reg: Register to write
|
||||||
|
* @mask: Mask of bits to set
|
||||||
|
* @val: Value to set
|
||||||
|
*/
|
||||||
|
int stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&stmpe->lock);
|
||||||
|
ret = __stmpe_set_bits(stmpe, reg, mask, val);
|
||||||
|
mutex_unlock(&stmpe->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(stmpe_set_bits);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stmpe_block_read() - read multiple STMPE registers
|
||||||
|
* @stmpe: Device to read from
|
||||||
|
* @reg: First register
|
||||||
|
* @length: Number of registers
|
||||||
|
* @values: Buffer to write to
|
||||||
|
*/
|
||||||
|
int stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&stmpe->lock);
|
||||||
|
ret = __stmpe_block_read(stmpe, reg, length, values);
|
||||||
|
mutex_unlock(&stmpe->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(stmpe_block_read);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stmpe_block_write() - write multiple STMPE registers
|
||||||
|
* @stmpe: Device to write to
|
||||||
|
* @reg: First register
|
||||||
|
* @length: Number of registers
|
||||||
|
* @values: Values to write
|
||||||
|
*/
|
||||||
|
int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
|
||||||
|
const u8 *values)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&stmpe->lock);
|
||||||
|
ret = __stmpe_block_write(stmpe, reg, length, values);
|
||||||
|
mutex_unlock(&stmpe->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(stmpe_block_write);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* @pins is assumed to have a bit set for each of the bits whose alternate
|
||||||
|
* function is to be changed, numbered according to the GPIOXY numbers.
|
||||||
|
*
|
||||||
|
* If the GPIO module is not enabled, this function automatically enables it in
|
||||||
|
* order to perform the change.
|
||||||
|
*/
|
||||||
|
int stmpe_set_altfunc(struct stmpe *stmpe, u32 pins, enum stmpe_block block)
|
||||||
|
{
|
||||||
|
struct stmpe_variant_info *variant = stmpe->variant;
|
||||||
|
u8 regaddr = stmpe->regs[STMPE_IDX_GPAFR_U_MSB];
|
||||||
|
int af_bits = variant->af_bits;
|
||||||
|
int numregs = DIV_ROUND_UP(stmpe->num_gpios * af_bits, 8);
|
||||||
|
int afperreg = 8 / af_bits;
|
||||||
|
int mask = (1 << af_bits) - 1;
|
||||||
|
u8 regs[numregs];
|
||||||
|
int af;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&stmpe->lock);
|
||||||
|
|
||||||
|
ret = __stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = __stmpe_block_read(stmpe, regaddr, numregs, regs);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
af = variant->get_altfunc(stmpe, block);
|
||||||
|
|
||||||
|
while (pins) {
|
||||||
|
int pin = __ffs(pins);
|
||||||
|
int regoffset = numregs - (pin / afperreg) - 1;
|
||||||
|
int pos = (pin % afperreg) * (8 / afperreg);
|
||||||
|
|
||||||
|
regs[regoffset] &= ~(mask << pos);
|
||||||
|
regs[regoffset] |= af << pos;
|
||||||
|
|
||||||
|
pins &= ~(1 << pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = __stmpe_block_write(stmpe, regaddr, numregs, regs);
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&stmpe->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(stmpe_set_altfunc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GPIO (all variants)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct resource stmpe_gpio_resources[] = {
|
||||||
|
/* Start and end filled dynamically */
|
||||||
|
{
|
||||||
|
.flags = IORESOURCE_IRQ,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mfd_cell stmpe_gpio_cell = {
|
||||||
|
.name = "stmpe-gpio",
|
||||||
|
.resources = stmpe_gpio_resources,
|
||||||
|
.num_resources = ARRAY_SIZE(stmpe_gpio_resources),
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keypad (1601, 2401, 2403)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct resource stmpe_keypad_resources[] = {
|
||||||
|
{
|
||||||
|
.name = "KEYPAD",
|
||||||
|
.start = 0,
|
||||||
|
.end = 0,
|
||||||
|
.flags = IORESOURCE_IRQ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "KEYPAD_OVER",
|
||||||
|
.start = 1,
|
||||||
|
.end = 1,
|
||||||
|
.flags = IORESOURCE_IRQ,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mfd_cell stmpe_keypad_cell = {
|
||||||
|
.name = "stmpe-keypad",
|
||||||
|
.resources = stmpe_keypad_resources,
|
||||||
|
.num_resources = ARRAY_SIZE(stmpe_keypad_resources),
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Touchscreen (STMPE811)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct resource stmpe_ts_resources[] = {
|
||||||
|
{
|
||||||
|
.name = "TOUCH_DET",
|
||||||
|
.start = 0,
|
||||||
|
.end = 0,
|
||||||
|
.flags = IORESOURCE_IRQ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "FIFO_TH",
|
||||||
|
.start = 1,
|
||||||
|
.end = 1,
|
||||||
|
.flags = IORESOURCE_IRQ,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mfd_cell stmpe_ts_cell = {
|
||||||
|
.name = "stmpe-ts",
|
||||||
|
.resources = stmpe_ts_resources,
|
||||||
|
.num_resources = ARRAY_SIZE(stmpe_ts_resources),
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STMPE811
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const u8 stmpe811_regs[] = {
|
||||||
|
[STMPE_IDX_CHIP_ID] = STMPE811_REG_CHIP_ID,
|
||||||
|
[STMPE_IDX_ICR_LSB] = STMPE811_REG_INT_CTRL,
|
||||||
|
[STMPE_IDX_IER_LSB] = STMPE811_REG_INT_EN,
|
||||||
|
[STMPE_IDX_ISR_MSB] = STMPE811_REG_INT_STA,
|
||||||
|
[STMPE_IDX_GPMR_LSB] = STMPE811_REG_GPIO_MP_STA,
|
||||||
|
[STMPE_IDX_GPSR_LSB] = STMPE811_REG_GPIO_SET_PIN,
|
||||||
|
[STMPE_IDX_GPCR_LSB] = STMPE811_REG_GPIO_CLR_PIN,
|
||||||
|
[STMPE_IDX_GPDR_LSB] = STMPE811_REG_GPIO_DIR,
|
||||||
|
[STMPE_IDX_GPRER_LSB] = STMPE811_REG_GPIO_RE,
|
||||||
|
[STMPE_IDX_GPFER_LSB] = STMPE811_REG_GPIO_FE,
|
||||||
|
[STMPE_IDX_GPAFR_U_MSB] = STMPE811_REG_GPIO_AF,
|
||||||
|
[STMPE_IDX_IEGPIOR_LSB] = STMPE811_REG_GPIO_INT_EN,
|
||||||
|
[STMPE_IDX_ISGPIOR_MSB] = STMPE811_REG_GPIO_INT_STA,
|
||||||
|
[STMPE_IDX_GPEDR_MSB] = STMPE811_REG_GPIO_ED,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct stmpe_variant_block stmpe811_blocks[] = {
|
||||||
|
{
|
||||||
|
.cell = &stmpe_gpio_cell,
|
||||||
|
.irq = STMPE811_IRQ_GPIOC,
|
||||||
|
.block = STMPE_BLOCK_GPIO,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.cell = &stmpe_ts_cell,
|
||||||
|
.irq = STMPE811_IRQ_TOUCH_DET,
|
||||||
|
.block = STMPE_BLOCK_TOUCHSCREEN,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks,
|
||||||
|
bool enable)
|
||||||
|
{
|
||||||
|
unsigned int mask = 0;
|
||||||
|
|
||||||
|
if (blocks & STMPE_BLOCK_GPIO)
|
||||||
|
mask |= STMPE811_SYS_CTRL2_GPIO_OFF;
|
||||||
|
|
||||||
|
if (blocks & STMPE_BLOCK_ADC)
|
||||||
|
mask |= STMPE811_SYS_CTRL2_ADC_OFF;
|
||||||
|
|
||||||
|
if (blocks & STMPE_BLOCK_TOUCHSCREEN)
|
||||||
|
mask |= STMPE811_SYS_CTRL2_TSC_OFF;
|
||||||
|
|
||||||
|
return __stmpe_set_bits(stmpe, STMPE811_REG_SYS_CTRL2, mask,
|
||||||
|
enable ? 0 : mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe811_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
|
||||||
|
{
|
||||||
|
/* 0 for touchscreen, 1 for GPIO */
|
||||||
|
return block != STMPE_BLOCK_TOUCHSCREEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct stmpe_variant_info stmpe811 = {
|
||||||
|
.name = "stmpe811",
|
||||||
|
.id_val = 0x0811,
|
||||||
|
.id_mask = 0xffff,
|
||||||
|
.num_gpios = 8,
|
||||||
|
.af_bits = 1,
|
||||||
|
.regs = stmpe811_regs,
|
||||||
|
.blocks = stmpe811_blocks,
|
||||||
|
.num_blocks = ARRAY_SIZE(stmpe811_blocks),
|
||||||
|
.num_irqs = STMPE811_NR_INTERNAL_IRQS,
|
||||||
|
.enable = stmpe811_enable,
|
||||||
|
.get_altfunc = stmpe811_get_altfunc,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STMPE1601
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const u8 stmpe1601_regs[] = {
|
||||||
|
[STMPE_IDX_CHIP_ID] = STMPE1601_REG_CHIP_ID,
|
||||||
|
[STMPE_IDX_ICR_LSB] = STMPE1601_REG_ICR_LSB,
|
||||||
|
[STMPE_IDX_IER_LSB] = STMPE1601_REG_IER_LSB,
|
||||||
|
[STMPE_IDX_ISR_MSB] = STMPE1601_REG_ISR_MSB,
|
||||||
|
[STMPE_IDX_GPMR_LSB] = STMPE1601_REG_GPIO_MP_LSB,
|
||||||
|
[STMPE_IDX_GPSR_LSB] = STMPE1601_REG_GPIO_SET_LSB,
|
||||||
|
[STMPE_IDX_GPCR_LSB] = STMPE1601_REG_GPIO_CLR_LSB,
|
||||||
|
[STMPE_IDX_GPDR_LSB] = STMPE1601_REG_GPIO_SET_DIR_LSB,
|
||||||
|
[STMPE_IDX_GPRER_LSB] = STMPE1601_REG_GPIO_RE_LSB,
|
||||||
|
[STMPE_IDX_GPFER_LSB] = STMPE1601_REG_GPIO_FE_LSB,
|
||||||
|
[STMPE_IDX_GPAFR_U_MSB] = STMPE1601_REG_GPIO_AF_U_MSB,
|
||||||
|
[STMPE_IDX_IEGPIOR_LSB] = STMPE1601_REG_INT_EN_GPIO_MASK_LSB,
|
||||||
|
[STMPE_IDX_ISGPIOR_MSB] = STMPE1601_REG_INT_STA_GPIO_MSB,
|
||||||
|
[STMPE_IDX_GPEDR_MSB] = STMPE1601_REG_GPIO_ED_MSB,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct stmpe_variant_block stmpe1601_blocks[] = {
|
||||||
|
{
|
||||||
|
.cell = &stmpe_gpio_cell,
|
||||||
|
.irq = STMPE24XX_IRQ_GPIOC,
|
||||||
|
.block = STMPE_BLOCK_GPIO,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.cell = &stmpe_keypad_cell,
|
||||||
|
.irq = STMPE24XX_IRQ_KEYPAD,
|
||||||
|
.block = STMPE_BLOCK_KEYPAD,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* supported autosleep timeout delay (in msecs) */
|
||||||
|
static const int stmpe_autosleep_delay[] = {
|
||||||
|
4, 16, 32, 64, 128, 256, 512, 1024,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stmpe_round_timeout(int timeout)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(stmpe_autosleep_delay); i++) {
|
||||||
|
if (stmpe_autosleep_delay[i] >= timeout)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* requests for delays longer than supported should not return the
|
||||||
|
* longest supported delay
|
||||||
|
*/
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe_autosleep(struct stmpe *stmpe, int autosleep_timeout)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!stmpe->variant->enable_autosleep)
|
||||||
|
return -ENOSYS;
|
||||||
|
|
||||||
|
mutex_lock(&stmpe->lock);
|
||||||
|
ret = stmpe->variant->enable_autosleep(stmpe, autosleep_timeout);
|
||||||
|
mutex_unlock(&stmpe->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Both stmpe 1601/2403 support same layout for autosleep
|
||||||
|
*/
|
||||||
|
static int stmpe1601_autosleep(struct stmpe *stmpe,
|
||||||
|
int autosleep_timeout)
|
||||||
|
{
|
||||||
|
int ret, timeout;
|
||||||
|
|
||||||
|
/* choose the best available timeout */
|
||||||
|
timeout = stmpe_round_timeout(autosleep_timeout);
|
||||||
|
if (timeout < 0) {
|
||||||
|
dev_err(stmpe->dev, "invalid timeout\n");
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
|
||||||
|
STMPE1601_AUTOSLEEP_TIMEOUT_MASK,
|
||||||
|
timeout);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
|
||||||
|
STPME1601_AUTOSLEEP_ENABLE,
|
||||||
|
STPME1601_AUTOSLEEP_ENABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks,
|
||||||
|
bool enable)
|
||||||
|
{
|
||||||
|
unsigned int mask = 0;
|
||||||
|
|
||||||
|
if (blocks & STMPE_BLOCK_GPIO)
|
||||||
|
mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO;
|
||||||
|
|
||||||
|
if (blocks & STMPE_BLOCK_KEYPAD)
|
||||||
|
mask |= STMPE1601_SYS_CTRL_ENABLE_KPC;
|
||||||
|
|
||||||
|
return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask,
|
||||||
|
enable ? mask : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe1601_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
|
||||||
|
{
|
||||||
|
switch (block) {
|
||||||
|
case STMPE_BLOCK_PWM:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case STMPE_BLOCK_KEYPAD:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case STMPE_BLOCK_GPIO:
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct stmpe_variant_info stmpe1601 = {
|
||||||
|
.name = "stmpe1601",
|
||||||
|
.id_val = 0x0210,
|
||||||
|
.id_mask = 0xfff0, /* at least 0x0210 and 0x0212 */
|
||||||
|
.num_gpios = 16,
|
||||||
|
.af_bits = 2,
|
||||||
|
.regs = stmpe1601_regs,
|
||||||
|
.blocks = stmpe1601_blocks,
|
||||||
|
.num_blocks = ARRAY_SIZE(stmpe1601_blocks),
|
||||||
|
.num_irqs = STMPE1601_NR_INTERNAL_IRQS,
|
||||||
|
.enable = stmpe1601_enable,
|
||||||
|
.get_altfunc = stmpe1601_get_altfunc,
|
||||||
|
.enable_autosleep = stmpe1601_autosleep,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STMPE24XX
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const u8 stmpe24xx_regs[] = {
|
||||||
|
[STMPE_IDX_CHIP_ID] = STMPE24XX_REG_CHIP_ID,
|
||||||
|
[STMPE_IDX_ICR_LSB] = STMPE24XX_REG_ICR_LSB,
|
||||||
|
[STMPE_IDX_IER_LSB] = STMPE24XX_REG_IER_LSB,
|
||||||
|
[STMPE_IDX_ISR_MSB] = STMPE24XX_REG_ISR_MSB,
|
||||||
|
[STMPE_IDX_GPMR_LSB] = STMPE24XX_REG_GPMR_LSB,
|
||||||
|
[STMPE_IDX_GPSR_LSB] = STMPE24XX_REG_GPSR_LSB,
|
||||||
|
[STMPE_IDX_GPCR_LSB] = STMPE24XX_REG_GPCR_LSB,
|
||||||
|
[STMPE_IDX_GPDR_LSB] = STMPE24XX_REG_GPDR_LSB,
|
||||||
|
[STMPE_IDX_GPRER_LSB] = STMPE24XX_REG_GPRER_LSB,
|
||||||
|
[STMPE_IDX_GPFER_LSB] = STMPE24XX_REG_GPFER_LSB,
|
||||||
|
[STMPE_IDX_GPAFR_U_MSB] = STMPE24XX_REG_GPAFR_U_MSB,
|
||||||
|
[STMPE_IDX_IEGPIOR_LSB] = STMPE24XX_REG_IEGPIOR_LSB,
|
||||||
|
[STMPE_IDX_ISGPIOR_MSB] = STMPE24XX_REG_ISGPIOR_MSB,
|
||||||
|
[STMPE_IDX_GPEDR_MSB] = STMPE24XX_REG_GPEDR_MSB,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct stmpe_variant_block stmpe24xx_blocks[] = {
|
||||||
|
{
|
||||||
|
.cell = &stmpe_gpio_cell,
|
||||||
|
.irq = STMPE24XX_IRQ_GPIOC,
|
||||||
|
.block = STMPE_BLOCK_GPIO,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.cell = &stmpe_keypad_cell,
|
||||||
|
.irq = STMPE24XX_IRQ_KEYPAD,
|
||||||
|
.block = STMPE_BLOCK_KEYPAD,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stmpe24xx_enable(struct stmpe *stmpe, unsigned int blocks,
|
||||||
|
bool enable)
|
||||||
|
{
|
||||||
|
unsigned int mask = 0;
|
||||||
|
|
||||||
|
if (blocks & STMPE_BLOCK_GPIO)
|
||||||
|
mask |= STMPE24XX_SYS_CTRL_ENABLE_GPIO;
|
||||||
|
|
||||||
|
if (blocks & STMPE_BLOCK_KEYPAD)
|
||||||
|
mask |= STMPE24XX_SYS_CTRL_ENABLE_KPC;
|
||||||
|
|
||||||
|
return __stmpe_set_bits(stmpe, STMPE24XX_REG_SYS_CTRL, mask,
|
||||||
|
enable ? mask : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stmpe24xx_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
|
||||||
|
{
|
||||||
|
switch (block) {
|
||||||
|
case STMPE_BLOCK_ROTATOR:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case STMPE_BLOCK_KEYPAD:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case STMPE_BLOCK_GPIO:
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct stmpe_variant_info stmpe2401 = {
|
||||||
|
.name = "stmpe2401",
|
||||||
|
.id_val = 0x0101,
|
||||||
|
.id_mask = 0xffff,
|
||||||
|
.num_gpios = 24,
|
||||||
|
.af_bits = 2,
|
||||||
|
.regs = stmpe24xx_regs,
|
||||||
|
.blocks = stmpe24xx_blocks,
|
||||||
|
.num_blocks = ARRAY_SIZE(stmpe24xx_blocks),
|
||||||
|
.num_irqs = STMPE24XX_NR_INTERNAL_IRQS,
|
||||||
|
.enable = stmpe24xx_enable,
|
||||||
|
.get_altfunc = stmpe24xx_get_altfunc,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct stmpe_variant_info stmpe2403 = {
|
||||||
|
.name = "stmpe2403",
|
||||||
|
.id_val = 0x0120,
|
||||||
|
.id_mask = 0xffff,
|
||||||
|
.num_gpios = 24,
|
||||||
|
.af_bits = 2,
|
||||||
|
.regs = stmpe24xx_regs,
|
||||||
|
.blocks = stmpe24xx_blocks,
|
||||||
|
.num_blocks = ARRAY_SIZE(stmpe24xx_blocks),
|
||||||
|
.num_irqs = STMPE24XX_NR_INTERNAL_IRQS,
|
||||||
|
.enable = stmpe24xx_enable,
|
||||||
|
.get_altfunc = stmpe24xx_get_altfunc,
|
||||||
|
.enable_autosleep = stmpe1601_autosleep, /* same as stmpe1601 */
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct stmpe_variant_info *stmpe_variant_info[] = {
|
||||||
|
[STMPE811] = &stmpe811,
|
||||||
|
[STMPE1601] = &stmpe1601,
|
||||||
|
[STMPE2401] = &stmpe2401,
|
||||||
|
[STMPE2403] = &stmpe2403,
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t stmpe_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct stmpe *stmpe = data;
|
||||||
|
struct stmpe_variant_info *variant = stmpe->variant;
|
||||||
|
int num = DIV_ROUND_UP(variant->num_irqs, 8);
|
||||||
|
u8 israddr = stmpe->regs[STMPE_IDX_ISR_MSB];
|
||||||
|
u8 isr[num];
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ret = stmpe_block_read(stmpe, israddr, num, isr);
|
||||||
|
if (ret < 0)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
int bank = num - i - 1;
|
||||||
|
u8 status = isr[i];
|
||||||
|
u8 clear;
|
||||||
|
|
||||||
|
status &= stmpe->ier[bank];
|
||||||
|
if (!status)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
clear = status;
|
||||||
|
while (status) {
|
||||||
|
int bit = __ffs(status);
|
||||||
|
int line = bank * 8 + bit;
|
||||||
|
|
||||||
|
handle_nested_irq(stmpe->irq_base + line);
|
||||||
|
status &= ~(1 << bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
stmpe_reg_write(stmpe, israddr + i, clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_irq_lock(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct stmpe *stmpe = get_irq_chip_data(irq);
|
||||||
|
|
||||||
|
mutex_lock(&stmpe->irq_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_irq_sync_unlock(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct stmpe *stmpe = get_irq_chip_data(irq);
|
||||||
|
struct stmpe_variant_info *variant = stmpe->variant;
|
||||||
|
int num = DIV_ROUND_UP(variant->num_irqs, 8);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
u8 new = stmpe->ier[i];
|
||||||
|
u8 old = stmpe->oldier[i];
|
||||||
|
|
||||||
|
if (new == old)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
stmpe->oldier[i] = new;
|
||||||
|
stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_IER_LSB] - i, new);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&stmpe->irq_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_irq_mask(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct stmpe *stmpe = get_irq_chip_data(irq);
|
||||||
|
int offset = irq - stmpe->irq_base;
|
||||||
|
int regoffset = offset / 8;
|
||||||
|
int mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
stmpe->ier[regoffset] &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_irq_unmask(unsigned int irq)
|
||||||
|
{
|
||||||
|
struct stmpe *stmpe = get_irq_chip_data(irq);
|
||||||
|
int offset = irq - stmpe->irq_base;
|
||||||
|
int regoffset = offset / 8;
|
||||||
|
int mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
stmpe->ier[regoffset] |= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip stmpe_irq_chip = {
|
||||||
|
.name = "stmpe",
|
||||||
|
.bus_lock = stmpe_irq_lock,
|
||||||
|
.bus_sync_unlock = stmpe_irq_sync_unlock,
|
||||||
|
.mask = stmpe_irq_mask,
|
||||||
|
.unmask = stmpe_irq_unmask,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __devinit stmpe_irq_init(struct stmpe *stmpe)
|
||||||
|
{
|
||||||
|
int num_irqs = stmpe->variant->num_irqs;
|
||||||
|
int base = stmpe->irq_base;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
for (irq = base; irq < base + num_irqs; irq++) {
|
||||||
|
set_irq_chip_data(irq, stmpe);
|
||||||
|
set_irq_chip_and_handler(irq, &stmpe_irq_chip,
|
||||||
|
handle_edge_irq);
|
||||||
|
set_irq_nested_thread(irq, 1);
|
||||||
|
#ifdef CONFIG_ARM
|
||||||
|
set_irq_flags(irq, IRQF_VALID);
|
||||||
|
#else
|
||||||
|
set_irq_noprobe(irq);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stmpe_irq_remove(struct stmpe *stmpe)
|
||||||
|
{
|
||||||
|
int num_irqs = stmpe->variant->num_irqs;
|
||||||
|
int base = stmpe->irq_base;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
for (irq = base; irq < base + num_irqs; irq++) {
|
||||||
|
#ifdef CONFIG_ARM
|
||||||
|
set_irq_flags(irq, 0);
|
||||||
|
#endif
|
||||||
|
set_irq_chip_and_handler(irq, NULL, NULL);
|
||||||
|
set_irq_chip_data(irq, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_chip_init(struct stmpe *stmpe)
|
||||||
|
{
|
||||||
|
unsigned int irq_trigger = stmpe->pdata->irq_trigger;
|
||||||
|
int autosleep_timeout = stmpe->pdata->autosleep_timeout;
|
||||||
|
struct stmpe_variant_info *variant = stmpe->variant;
|
||||||
|
u8 icr = STMPE_ICR_LSB_GIM;
|
||||||
|
unsigned int id;
|
||||||
|
u8 data[2];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = stmpe_block_read(stmpe, stmpe->regs[STMPE_IDX_CHIP_ID],
|
||||||
|
ARRAY_SIZE(data), data);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
id = (data[0] << 8) | data[1];
|
||||||
|
if ((id & variant->id_mask) != variant->id_val) {
|
||||||
|
dev_err(stmpe->dev, "unknown chip id: %#x\n", id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(stmpe->dev, "%s detected, chip id: %#x\n", variant->name, id);
|
||||||
|
|
||||||
|
/* Disable all modules -- subdrivers should enable what they need. */
|
||||||
|
ret = stmpe_disable(stmpe, ~0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (irq_trigger == IRQF_TRIGGER_FALLING ||
|
||||||
|
irq_trigger == IRQF_TRIGGER_RISING)
|
||||||
|
icr |= STMPE_ICR_LSB_EDGE;
|
||||||
|
|
||||||
|
if (irq_trigger == IRQF_TRIGGER_RISING ||
|
||||||
|
irq_trigger == IRQF_TRIGGER_HIGH)
|
||||||
|
icr |= STMPE_ICR_LSB_HIGH;
|
||||||
|
|
||||||
|
if (stmpe->pdata->irq_invert_polarity)
|
||||||
|
icr ^= STMPE_ICR_LSB_HIGH;
|
||||||
|
|
||||||
|
if (stmpe->pdata->autosleep) {
|
||||||
|
ret = stmpe_autosleep(stmpe, autosleep_timeout);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_ICR_LSB], icr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_add_device(struct stmpe *stmpe,
|
||||||
|
struct mfd_cell *cell, int irq)
|
||||||
|
{
|
||||||
|
return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1,
|
||||||
|
NULL, stmpe->irq_base + irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_devices_init(struct stmpe *stmpe)
|
||||||
|
{
|
||||||
|
struct stmpe_variant_info *variant = stmpe->variant;
|
||||||
|
unsigned int platform_blocks = stmpe->pdata->blocks;
|
||||||
|
int ret = -EINVAL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < variant->num_blocks; i++) {
|
||||||
|
struct stmpe_variant_block *block = &variant->blocks[i];
|
||||||
|
|
||||||
|
if (!(platform_blocks & block->block))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
platform_blocks &= ~block->block;
|
||||||
|
ret = stmpe_add_device(stmpe, block->cell, block->irq);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (platform_blocks)
|
||||||
|
dev_warn(stmpe->dev,
|
||||||
|
"platform wants blocks (%#x) not present on variant",
|
||||||
|
platform_blocks);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit stmpe_probe(struct i2c_client *i2c,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct stmpe_platform_data *pdata = i2c->dev.platform_data;
|
||||||
|
struct stmpe *stmpe;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!pdata)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
stmpe = kzalloc(sizeof(struct stmpe), GFP_KERNEL);
|
||||||
|
if (!stmpe)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(&stmpe->irq_lock);
|
||||||
|
mutex_init(&stmpe->lock);
|
||||||
|
|
||||||
|
stmpe->dev = &i2c->dev;
|
||||||
|
stmpe->i2c = i2c;
|
||||||
|
|
||||||
|
stmpe->pdata = pdata;
|
||||||
|
stmpe->irq_base = pdata->irq_base;
|
||||||
|
|
||||||
|
stmpe->partnum = id->driver_data;
|
||||||
|
stmpe->variant = stmpe_variant_info[stmpe->partnum];
|
||||||
|
stmpe->regs = stmpe->variant->regs;
|
||||||
|
stmpe->num_gpios = stmpe->variant->num_gpios;
|
||||||
|
|
||||||
|
i2c_set_clientdata(i2c, stmpe);
|
||||||
|
|
||||||
|
ret = stmpe_chip_init(stmpe);
|
||||||
|
if (ret)
|
||||||
|
goto out_free;
|
||||||
|
|
||||||
|
ret = stmpe_irq_init(stmpe);
|
||||||
|
if (ret)
|
||||||
|
goto out_free;
|
||||||
|
|
||||||
|
ret = request_threaded_irq(stmpe->i2c->irq, NULL, stmpe_irq,
|
||||||
|
pdata->irq_trigger | IRQF_ONESHOT,
|
||||||
|
"stmpe", stmpe);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret);
|
||||||
|
goto out_removeirq;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stmpe_devices_init(stmpe);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(stmpe->dev, "failed to add children\n");
|
||||||
|
goto out_removedevs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_removedevs:
|
||||||
|
mfd_remove_devices(stmpe->dev);
|
||||||
|
free_irq(stmpe->i2c->irq, stmpe);
|
||||||
|
out_removeirq:
|
||||||
|
stmpe_irq_remove(stmpe);
|
||||||
|
out_free:
|
||||||
|
kfree(stmpe);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit stmpe_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct stmpe *stmpe = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
mfd_remove_devices(stmpe->dev);
|
||||||
|
|
||||||
|
free_irq(stmpe->i2c->irq, stmpe);
|
||||||
|
stmpe_irq_remove(stmpe);
|
||||||
|
|
||||||
|
kfree(stmpe);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id stmpe_id[] = {
|
||||||
|
{ "stmpe811", STMPE811 },
|
||||||
|
{ "stmpe1601", STMPE1601 },
|
||||||
|
{ "stmpe2401", STMPE2401 },
|
||||||
|
{ "stmpe2403", STMPE2403 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, stmpe_id);
|
||||||
|
|
||||||
|
static struct i2c_driver stmpe_driver = {
|
||||||
|
.driver.name = "stmpe",
|
||||||
|
.driver.owner = THIS_MODULE,
|
||||||
|
.probe = stmpe_probe,
|
||||||
|
.remove = __devexit_p(stmpe_remove),
|
||||||
|
.id_table = stmpe_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init stmpe_init(void)
|
||||||
|
{
|
||||||
|
return i2c_add_driver(&stmpe_driver);
|
||||||
|
}
|
||||||
|
subsys_initcall(stmpe_init);
|
||||||
|
|
||||||
|
static void __exit stmpe_exit(void)
|
||||||
|
{
|
||||||
|
i2c_del_driver(&stmpe_driver);
|
||||||
|
}
|
||||||
|
module_exit(stmpe_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_DESCRIPTION("STMPE MFD core driver");
|
||||||
|
MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) ST-Ericsson SA 2010
|
||||||
|
*
|
||||||
|
* License Terms: GNU General Public License, version 2
|
||||||
|
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __STMPE_H
|
||||||
|
#define __STMPE_H
|
||||||
|
|
||||||
|
#ifdef STMPE_DUMP_BYTES
|
||||||
|
static inline void stmpe_dump_bytes(const char *str, const void *buf,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
print_hex_dump_bytes(str, DUMP_PREFIX_OFFSET, buf, len);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline void stmpe_dump_bytes(const char *str, const void *buf,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stmpe_variant_block - information about block
|
||||||
|
* @cell: base mfd cell
|
||||||
|
* @irq: interrupt number to be added to each IORESOURCE_IRQ
|
||||||
|
* in the cell
|
||||||
|
* @block: block id; used for identification with platform data and for
|
||||||
|
* enable and altfunc callbacks
|
||||||
|
*/
|
||||||
|
struct stmpe_variant_block {
|
||||||
|
struct mfd_cell *cell;
|
||||||
|
int irq;
|
||||||
|
enum stmpe_block block;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stmpe_variant_info - variant-specific information
|
||||||
|
* @name: part name
|
||||||
|
* @id_val: content of CHIPID register
|
||||||
|
* @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
|
||||||
|
* @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
|
||||||
|
* @enable: callback to enable the specified blocks.
|
||||||
|
* Called with the I/O lock held.
|
||||||
|
* @get_altfunc: callback to get the alternate function number for the
|
||||||
|
* specific block
|
||||||
|
* @enable_autosleep: callback to configure autosleep with specified timeout
|
||||||
|
*/
|
||||||
|
struct stmpe_variant_info {
|
||||||
|
const char *name;
|
||||||
|
u16 id_val;
|
||||||
|
u16 id_mask;
|
||||||
|
int num_gpios;
|
||||||
|
int af_bits;
|
||||||
|
const u8 *regs;
|
||||||
|
struct stmpe_variant_block *blocks;
|
||||||
|
int num_blocks;
|
||||||
|
int num_irqs;
|
||||||
|
int (*enable)(struct stmpe *stmpe, unsigned int blocks, bool enable);
|
||||||
|
int (*get_altfunc)(struct stmpe *stmpe, enum stmpe_block block);
|
||||||
|
int (*enable_autosleep)(struct stmpe *stmpe, int autosleep_timeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define STMPE_ICR_LSB_HIGH (1 << 2)
|
||||||
|
#define STMPE_ICR_LSB_EDGE (1 << 1)
|
||||||
|
#define STMPE_ICR_LSB_GIM (1 << 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STMPE811
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define STMPE811_IRQ_TOUCH_DET 0
|
||||||
|
#define STMPE811_IRQ_FIFO_TH 1
|
||||||
|
#define STMPE811_IRQ_FIFO_OFLOW 2
|
||||||
|
#define STMPE811_IRQ_FIFO_FULL 3
|
||||||
|
#define STMPE811_IRQ_FIFO_EMPTY 4
|
||||||
|
#define STMPE811_IRQ_TEMP_SENS 5
|
||||||
|
#define STMPE811_IRQ_ADC 6
|
||||||
|
#define STMPE811_IRQ_GPIOC 7
|
||||||
|
#define STMPE811_NR_INTERNAL_IRQS 8
|
||||||
|
|
||||||
|
#define STMPE811_REG_CHIP_ID 0x00
|
||||||
|
#define STMPE811_REG_SYS_CTRL2 0x04
|
||||||
|
#define STMPE811_REG_INT_CTRL 0x09
|
||||||
|
#define STMPE811_REG_INT_EN 0x0A
|
||||||
|
#define STMPE811_REG_INT_STA 0x0B
|
||||||
|
#define STMPE811_REG_GPIO_INT_EN 0x0C
|
||||||
|
#define STMPE811_REG_GPIO_INT_STA 0x0D
|
||||||
|
#define STMPE811_REG_GPIO_SET_PIN 0x10
|
||||||
|
#define STMPE811_REG_GPIO_CLR_PIN 0x11
|
||||||
|
#define STMPE811_REG_GPIO_MP_STA 0x12
|
||||||
|
#define STMPE811_REG_GPIO_DIR 0x13
|
||||||
|
#define STMPE811_REG_GPIO_ED 0x14
|
||||||
|
#define STMPE811_REG_GPIO_RE 0x15
|
||||||
|
#define STMPE811_REG_GPIO_FE 0x16
|
||||||
|
#define STMPE811_REG_GPIO_AF 0x17
|
||||||
|
|
||||||
|
#define STMPE811_SYS_CTRL2_ADC_OFF (1 << 0)
|
||||||
|
#define STMPE811_SYS_CTRL2_TSC_OFF (1 << 1)
|
||||||
|
#define STMPE811_SYS_CTRL2_GPIO_OFF (1 << 2)
|
||||||
|
#define STMPE811_SYS_CTRL2_TS_OFF (1 << 3)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STMPE1601
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define STMPE1601_IRQ_GPIOC 8
|
||||||
|
#define STMPE1601_IRQ_PWM3 7
|
||||||
|
#define STMPE1601_IRQ_PWM2 6
|
||||||
|
#define STMPE1601_IRQ_PWM1 5
|
||||||
|
#define STMPE1601_IRQ_PWM0 4
|
||||||
|
#define STMPE1601_IRQ_KEYPAD_OVER 2
|
||||||
|
#define STMPE1601_IRQ_KEYPAD 1
|
||||||
|
#define STMPE1601_IRQ_WAKEUP 0
|
||||||
|
#define STMPE1601_NR_INTERNAL_IRQS 9
|
||||||
|
|
||||||
|
#define STMPE1601_REG_SYS_CTRL 0x02
|
||||||
|
#define STMPE1601_REG_SYS_CTRL2 0x03
|
||||||
|
#define STMPE1601_REG_ICR_LSB 0x11
|
||||||
|
#define STMPE1601_REG_IER_LSB 0x13
|
||||||
|
#define STMPE1601_REG_ISR_MSB 0x14
|
||||||
|
#define STMPE1601_REG_CHIP_ID 0x80
|
||||||
|
#define STMPE1601_REG_INT_EN_GPIO_MASK_LSB 0x17
|
||||||
|
#define STMPE1601_REG_INT_STA_GPIO_MSB 0x18
|
||||||
|
#define STMPE1601_REG_GPIO_MP_LSB 0x87
|
||||||
|
#define STMPE1601_REG_GPIO_SET_LSB 0x83
|
||||||
|
#define STMPE1601_REG_GPIO_CLR_LSB 0x85
|
||||||
|
#define STMPE1601_REG_GPIO_SET_DIR_LSB 0x89
|
||||||
|
#define STMPE1601_REG_GPIO_ED_MSB 0x8A
|
||||||
|
#define STMPE1601_REG_GPIO_RE_LSB 0x8D
|
||||||
|
#define STMPE1601_REG_GPIO_FE_LSB 0x8F
|
||||||
|
#define STMPE1601_REG_GPIO_AF_U_MSB 0x92
|
||||||
|
|
||||||
|
#define STMPE1601_SYS_CTRL_ENABLE_GPIO (1 << 3)
|
||||||
|
#define STMPE1601_SYS_CTRL_ENABLE_KPC (1 << 1)
|
||||||
|
#define STMPE1601_SYSCON_ENABLE_SPWM (1 << 0)
|
||||||
|
|
||||||
|
/* The 1601/2403 share the same masks */
|
||||||
|
#define STMPE1601_AUTOSLEEP_TIMEOUT_MASK (0x7)
|
||||||
|
#define STPME1601_AUTOSLEEP_ENABLE (1 << 3)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STMPE24xx
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define STMPE24XX_IRQ_GPIOC 8
|
||||||
|
#define STMPE24XX_IRQ_PWM2 7
|
||||||
|
#define STMPE24XX_IRQ_PWM1 6
|
||||||
|
#define STMPE24XX_IRQ_PWM0 5
|
||||||
|
#define STMPE24XX_IRQ_ROT_OVER 4
|
||||||
|
#define STMPE24XX_IRQ_ROT 3
|
||||||
|
#define STMPE24XX_IRQ_KEYPAD_OVER 2
|
||||||
|
#define STMPE24XX_IRQ_KEYPAD 1
|
||||||
|
#define STMPE24XX_IRQ_WAKEUP 0
|
||||||
|
#define STMPE24XX_NR_INTERNAL_IRQS 9
|
||||||
|
|
||||||
|
#define STMPE24XX_REG_SYS_CTRL 0x02
|
||||||
|
#define STMPE24XX_REG_ICR_LSB 0x11
|
||||||
|
#define STMPE24XX_REG_IER_LSB 0x13
|
||||||
|
#define STMPE24XX_REG_ISR_MSB 0x14
|
||||||
|
#define STMPE24XX_REG_CHIP_ID 0x80
|
||||||
|
#define STMPE24XX_REG_IEGPIOR_LSB 0x18
|
||||||
|
#define STMPE24XX_REG_ISGPIOR_MSB 0x19
|
||||||
|
#define STMPE24XX_REG_GPMR_LSB 0xA5
|
||||||
|
#define STMPE24XX_REG_GPSR_LSB 0x85
|
||||||
|
#define STMPE24XX_REG_GPCR_LSB 0x88
|
||||||
|
#define STMPE24XX_REG_GPDR_LSB 0x8B
|
||||||
|
#define STMPE24XX_REG_GPEDR_MSB 0x8C
|
||||||
|
#define STMPE24XX_REG_GPRER_LSB 0x91
|
||||||
|
#define STMPE24XX_REG_GPFER_LSB 0x94
|
||||||
|
#define STMPE24XX_REG_GPAFR_U_MSB 0x9B
|
||||||
|
|
||||||
|
#define STMPE24XX_SYS_CTRL_ENABLE_GPIO (1 << 3)
|
||||||
|
#define STMPE24XX_SYSCON_ENABLE_PWM (1 << 2)
|
||||||
|
#define STMPE24XX_SYS_CTRL_ENABLE_KPC (1 << 1)
|
||||||
|
#define STMPE24XX_SYSCON_ENABLE_ROT (1 << 0)
|
||||||
|
|
||||||
|
#endif
|
|
@ -350,7 +350,6 @@ static int t7l66xb_probe(struct platform_device *dev)
|
||||||
t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M");
|
t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M");
|
||||||
if (IS_ERR(t7l66xb->clk48m)) {
|
if (IS_ERR(t7l66xb->clk48m)) {
|
||||||
ret = PTR_ERR(t7l66xb->clk48m);
|
ret = PTR_ERR(t7l66xb->clk48m);
|
||||||
clk_put(t7l66xb->clk32k);
|
|
||||||
goto err_clk48m_get;
|
goto err_clk48m_get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,6 +424,8 @@ static int t7l66xb_remove(struct platform_device *dev)
|
||||||
ret = pdata->disable(dev);
|
ret = pdata->disable(dev);
|
||||||
clk_disable(t7l66xb->clk48m);
|
clk_disable(t7l66xb->clk48m);
|
||||||
clk_put(t7l66xb->clk48m);
|
clk_put(t7l66xb->clk48m);
|
||||||
|
clk_disable(t7l66xb->clk32k);
|
||||||
|
clk_put(t7l66xb->clk32k);
|
||||||
t7l66xb_detach_irq(dev);
|
t7l66xb_detach_irq(dev);
|
||||||
iounmap(t7l66xb->scr);
|
iounmap(t7l66xb->scr);
|
||||||
release_resource(&t7l66xb->rscr);
|
release_resource(&t7l66xb->rscr);
|
||||||
|
|
|
@ -137,7 +137,7 @@ static struct mfd_cell tc6387xb_cells[] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int tc6387xb_probe(struct platform_device *dev)
|
static int __devinit tc6387xb_probe(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
|
struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
|
||||||
struct resource *iomem, *rscr;
|
struct resource *iomem, *rscr;
|
||||||
|
@ -201,6 +201,7 @@ static int tc6387xb_probe(struct platform_device *dev)
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
iounmap(tc6387xb->scr);
|
||||||
err_ioremap:
|
err_ioremap:
|
||||||
release_resource(&tc6387xb->rscr);
|
release_resource(&tc6387xb->rscr);
|
||||||
err_resource:
|
err_resource:
|
||||||
|
@ -211,14 +212,17 @@ err_no_irq:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tc6387xb_remove(struct platform_device *dev)
|
static int __devexit tc6387xb_remove(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct clk *clk32k = platform_get_drvdata(dev);
|
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
|
||||||
|
|
||||||
mfd_remove_devices(&dev->dev);
|
mfd_remove_devices(&dev->dev);
|
||||||
clk_disable(clk32k);
|
iounmap(tc6387xb->scr);
|
||||||
clk_put(clk32k);
|
release_resource(&tc6387xb->rscr);
|
||||||
|
clk_disable(tc6387xb->clk32k);
|
||||||
|
clk_put(tc6387xb->clk32k);
|
||||||
platform_set_drvdata(dev, NULL);
|
platform_set_drvdata(dev, NULL);
|
||||||
|
kfree(tc6387xb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -229,7 +233,7 @@ static struct platform_driver tc6387xb_platform_driver = {
|
||||||
.name = "tc6387xb",
|
.name = "tc6387xb",
|
||||||
},
|
},
|
||||||
.probe = tc6387xb_probe,
|
.probe = tc6387xb_probe,
|
||||||
.remove = tc6387xb_remove,
|
.remove = __devexit_p(tc6387xb_remove),
|
||||||
.suspend = tc6387xb_suspend,
|
.suspend = tc6387xb_suspend,
|
||||||
.resume = tc6387xb_resume,
|
.resume = tc6387xb_resume,
|
||||||
};
|
};
|
||||||
|
|
|
@ -732,9 +732,9 @@ err_gpio_add:
|
||||||
if (tc6393xb->gpio.base != -1)
|
if (tc6393xb->gpio.base != -1)
|
||||||
temp = gpiochip_remove(&tc6393xb->gpio);
|
temp = gpiochip_remove(&tc6393xb->gpio);
|
||||||
tcpd->disable(dev);
|
tcpd->disable(dev);
|
||||||
err_clk_enable:
|
|
||||||
clk_disable(tc6393xb->clk);
|
|
||||||
err_enable:
|
err_enable:
|
||||||
|
clk_disable(tc6393xb->clk);
|
||||||
|
err_clk_enable:
|
||||||
iounmap(tc6393xb->scr);
|
iounmap(tc6393xb->scr);
|
||||||
err_ioremap:
|
err_ioremap:
|
||||||
release_resource(&tc6393xb->rscr);
|
release_resource(&tc6393xb->rscr);
|
||||||
|
|
|
@ -89,10 +89,8 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c,
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL);
|
tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL);
|
||||||
if (tps6507x == NULL) {
|
if (tps6507x == NULL)
|
||||||
kfree(i2c);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
i2c_set_clientdata(i2c, tps6507x);
|
i2c_set_clientdata(i2c, tps6507x);
|
||||||
tps6507x->dev = &i2c->dev;
|
tps6507x->dev = &i2c->dev;
|
||||||
|
|
|
@ -0,0 +1,375 @@
|
||||||
|
/*
|
||||||
|
* Core driver for TI TPS6586x PMIC family
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 CompuLab Ltd.
|
||||||
|
* Mike Rapoport <mike@compulab.co.il>
|
||||||
|
*
|
||||||
|
* Based on da903x.c.
|
||||||
|
* Copyright (C) 2008 Compulab, Ltd.
|
||||||
|
* Mike Rapoport <mike@compulab.co.il>
|
||||||
|
* Copyright (C) 2006-2008 Marvell International Ltd.
|
||||||
|
* Eric Miao <eric.miao@marvell.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
|
||||||
|
#include <linux/mfd/core.h>
|
||||||
|
#include <linux/mfd/tps6586x.h>
|
||||||
|
|
||||||
|
/* GPIO control registers */
|
||||||
|
#define TPS6586X_GPIOSET1 0x5d
|
||||||
|
#define TPS6586X_GPIOSET2 0x5e
|
||||||
|
|
||||||
|
/* device id */
|
||||||
|
#define TPS6586X_VERSIONCRC 0xcd
|
||||||
|
#define TPS658621A_VERSIONCRC 0x15
|
||||||
|
|
||||||
|
struct tps6586x {
|
||||||
|
struct mutex lock;
|
||||||
|
struct device *dev;
|
||||||
|
struct i2c_client *client;
|
||||||
|
|
||||||
|
struct gpio_chip gpio;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int __tps6586x_read(struct i2c_client *client,
|
||||||
|
int reg, uint8_t *val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_byte_data(client, reg);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
*val = (uint8_t)ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int __tps6586x_reads(struct i2c_client *client, int reg,
|
||||||
|
int len, uint8_t *val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int __tps6586x_write(struct i2c_client *client,
|
||||||
|
int reg, uint8_t val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(client, reg, val);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
|
||||||
|
val, reg);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int __tps6586x_writes(struct i2c_client *client, int reg,
|
||||||
|
int len, uint8_t *val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "failed writings to 0x%02x\n", reg);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tps6586x_write(struct device *dev, int reg, uint8_t val)
|
||||||
|
{
|
||||||
|
return __tps6586x_write(to_i2c_client(dev), reg, val);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tps6586x_write);
|
||||||
|
|
||||||
|
int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val)
|
||||||
|
{
|
||||||
|
return __tps6586x_writes(to_i2c_client(dev), reg, len, val);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tps6586x_writes);
|
||||||
|
|
||||||
|
int tps6586x_read(struct device *dev, int reg, uint8_t *val)
|
||||||
|
{
|
||||||
|
return __tps6586x_read(to_i2c_client(dev), reg, val);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tps6586x_read);
|
||||||
|
|
||||||
|
int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val)
|
||||||
|
{
|
||||||
|
return __tps6586x_reads(to_i2c_client(dev), reg, len, val);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tps6586x_reads);
|
||||||
|
|
||||||
|
int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
|
||||||
|
{
|
||||||
|
struct tps6586x *tps6586x = dev_get_drvdata(dev);
|
||||||
|
uint8_t reg_val;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&tps6586x->lock);
|
||||||
|
|
||||||
|
ret = __tps6586x_read(to_i2c_client(dev), reg, ®_val);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if ((reg_val & bit_mask) == 0) {
|
||||||
|
reg_val |= bit_mask;
|
||||||
|
ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
mutex_unlock(&tps6586x->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tps6586x_set_bits);
|
||||||
|
|
||||||
|
int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
|
||||||
|
{
|
||||||
|
struct tps6586x *tps6586x = dev_get_drvdata(dev);
|
||||||
|
uint8_t reg_val;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&tps6586x->lock);
|
||||||
|
|
||||||
|
ret = __tps6586x_read(to_i2c_client(dev), reg, ®_val);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (reg_val & bit_mask) {
|
||||||
|
reg_val &= ~bit_mask;
|
||||||
|
ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
mutex_unlock(&tps6586x->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tps6586x_clr_bits);
|
||||||
|
|
||||||
|
int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
|
||||||
|
{
|
||||||
|
struct tps6586x *tps6586x = dev_get_drvdata(dev);
|
||||||
|
uint8_t reg_val;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&tps6586x->lock);
|
||||||
|
|
||||||
|
ret = __tps6586x_read(tps6586x->client, reg, ®_val);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if ((reg_val & mask) != val) {
|
||||||
|
reg_val = (reg_val & ~mask) | val;
|
||||||
|
ret = __tps6586x_write(tps6586x->client, reg, reg_val);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
mutex_unlock(&tps6586x->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tps6586x_update);
|
||||||
|
|
||||||
|
static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
|
||||||
|
{
|
||||||
|
struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
|
||||||
|
uint8_t val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = __tps6586x_read(tps6586x->client, TPS6586X_GPIOSET2, &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return !!(val & (1 << offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset,
|
||||||
|
int value)
|
||||||
|
{
|
||||||
|
struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio);
|
||||||
|
|
||||||
|
__tps6586x_write(tps6586x->client, TPS6586X_GPIOSET2,
|
||||||
|
value << offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
|
||||||
|
int value)
|
||||||
|
{
|
||||||
|
struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
|
||||||
|
uint8_t val, mask;
|
||||||
|
|
||||||
|
tps6586x_gpio_set(gc, offset, value);
|
||||||
|
|
||||||
|
val = 0x1 << (offset * 2);
|
||||||
|
mask = 0x3 << (offset * 2);
|
||||||
|
|
||||||
|
return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!gpio_base)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tps6586x->gpio.owner = THIS_MODULE;
|
||||||
|
tps6586x->gpio.label = tps6586x->client->name;
|
||||||
|
tps6586x->gpio.dev = tps6586x->dev;
|
||||||
|
tps6586x->gpio.base = gpio_base;
|
||||||
|
tps6586x->gpio.ngpio = 4;
|
||||||
|
tps6586x->gpio.can_sleep = 1;
|
||||||
|
|
||||||
|
/* FIXME: add handling of GPIOs as dedicated inputs */
|
||||||
|
tps6586x->gpio.direction_output = tps6586x_gpio_output;
|
||||||
|
tps6586x->gpio.set = tps6586x_gpio_set;
|
||||||
|
tps6586x->gpio.get = tps6586x_gpio_get;
|
||||||
|
|
||||||
|
ret = gpiochip_add(&tps6586x->gpio);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(tps6586x->dev, "GPIO registration failed: %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __remove_subdev(struct device *dev, void *unused)
|
||||||
|
{
|
||||||
|
platform_device_unregister(to_platform_device(dev));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tps6586x_remove_subdevs(struct tps6586x *tps6586x)
|
||||||
|
{
|
||||||
|
return device_for_each_child(tps6586x->dev, NULL, __remove_subdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x,
|
||||||
|
struct tps6586x_platform_data *pdata)
|
||||||
|
{
|
||||||
|
struct tps6586x_subdev_info *subdev;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
int i, ret = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < pdata->num_subdevs; i++) {
|
||||||
|
subdev = &pdata->subdevs[i];
|
||||||
|
|
||||||
|
pdev = platform_device_alloc(subdev->name, subdev->id);
|
||||||
|
|
||||||
|
pdev->dev.parent = tps6586x->dev;
|
||||||
|
pdev->dev.platform_data = subdev->platform_data;
|
||||||
|
|
||||||
|
ret = platform_device_add(pdev);
|
||||||
|
if (ret)
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
tps6586x_remove_subdevs(tps6586x);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct tps6586x_platform_data *pdata = client->dev.platform_data;
|
||||||
|
struct tps6586x *tps6586x;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!pdata) {
|
||||||
|
dev_err(&client->dev, "tps6586x requires platform data\n");
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "Chip ID read failed: %d\n", ret);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != TPS658621A_VERSIONCRC) {
|
||||||
|
dev_err(&client->dev, "Unsupported chip ID: %x\n", ret);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL);
|
||||||
|
if (tps6586x == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
tps6586x->client = client;
|
||||||
|
tps6586x->dev = &client->dev;
|
||||||
|
i2c_set_clientdata(client, tps6586x);
|
||||||
|
|
||||||
|
mutex_init(&tps6586x->lock);
|
||||||
|
|
||||||
|
ret = tps6586x_add_subdevs(tps6586x, pdata);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&client->dev, "add devices failed: %d\n", ret);
|
||||||
|
goto err_add_devs;
|
||||||
|
}
|
||||||
|
|
||||||
|
tps6586x_gpio_init(tps6586x, pdata->gpio_base);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_add_devs:
|
||||||
|
kfree(tps6586x);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id tps6586x_id_table[] = {
|
||||||
|
{ "tps6586x", 0 },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, tps6586x_id_table);
|
||||||
|
|
||||||
|
static struct i2c_driver tps6586x_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "tps6586x",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = tps6586x_i2c_probe,
|
||||||
|
.remove = __devexit_p(tps6586x_i2c_remove),
|
||||||
|
.id_table = tps6586x_id_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init tps6586x_init(void)
|
||||||
|
{
|
||||||
|
return i2c_add_driver(&tps6586x_driver);
|
||||||
|
}
|
||||||
|
subsys_initcall(tps6586x_init);
|
||||||
|
|
||||||
|
static void __exit tps6586x_exit(void)
|
||||||
|
{
|
||||||
|
i2c_del_driver(&tps6586x_driver);
|
||||||
|
}
|
||||||
|
module_exit(tps6586x_exit);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("TPS6586X core driver");
|
||||||
|
MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
* twl6030_pwm.c
|
||||||
|
* Driver for PHOENIX (TWL6030) Pulse Width Modulator
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Texas Instruments
|
||||||
|
* Author: Hemanth V <hemanthv@ti.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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/i2c/twl.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define LED_PWM_CTRL1 0xF4
|
||||||
|
#define LED_PWM_CTRL2 0xF5
|
||||||
|
|
||||||
|
/* Max value for CTRL1 register */
|
||||||
|
#define PWM_CTRL1_MAX 255
|
||||||
|
|
||||||
|
/* Pull down disable */
|
||||||
|
#define PWM_CTRL2_DIS_PD (1 << 6)
|
||||||
|
|
||||||
|
/* Current control 2.5 milli Amps */
|
||||||
|
#define PWM_CTRL2_CURR_02 (2 << 4)
|
||||||
|
|
||||||
|
/* LED supply source */
|
||||||
|
#define PWM_CTRL2_SRC_VAC (1 << 2)
|
||||||
|
|
||||||
|
/* LED modes */
|
||||||
|
#define PWM_CTRL2_MODE_HW (0 << 0)
|
||||||
|
#define PWM_CTRL2_MODE_SW (1 << 0)
|
||||||
|
#define PWM_CTRL2_MODE_DIS (2 << 0)
|
||||||
|
|
||||||
|
#define PWM_CTRL2_MODE_MASK 0x3
|
||||||
|
|
||||||
|
struct pwm_device {
|
||||||
|
const char *label;
|
||||||
|
unsigned int pwm_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
|
||||||
|
{
|
||||||
|
u8 duty_cycle;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
duty_cycle = (duty_ns * PWM_CTRL1_MAX) / period_ns;
|
||||||
|
|
||||||
|
ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, duty_cycle, LED_PWM_CTRL1);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("%s: Failed to configure PWM, Error %d\n",
|
||||||
|
pwm->label, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(pwm_config);
|
||||||
|
|
||||||
|
int pwm_enable(struct pwm_device *pwm)
|
||||||
|
{
|
||||||
|
u8 val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change mode to software control */
|
||||||
|
val &= ~PWM_CTRL2_MODE_MASK;
|
||||||
|
val |= PWM_CTRL2_MODE_SW;
|
||||||
|
|
||||||
|
ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(pwm_enable);
|
||||||
|
|
||||||
|
void pwm_disable(struct pwm_device *pwm)
|
||||||
|
{
|
||||||
|
u8 val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("%s: Failed to disable PWM, Error %d\n",
|
||||||
|
pwm->label, ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
val &= ~PWM_CTRL2_MODE_MASK;
|
||||||
|
val |= PWM_CTRL2_MODE_HW;
|
||||||
|
|
||||||
|
ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("%s: Failed to disable PWM, Error %d\n",
|
||||||
|
pwm->label, ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(pwm_disable);
|
||||||
|
|
||||||
|
struct pwm_device *pwm_request(int pwm_id, const char *label)
|
||||||
|
{
|
||||||
|
u8 val;
|
||||||
|
int ret;
|
||||||
|
struct pwm_device *pwm;
|
||||||
|
|
||||||
|
pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
|
||||||
|
if (pwm == NULL) {
|
||||||
|
pr_err("%s: failed to allocate memory\n", label);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pwm->label = label;
|
||||||
|
pwm->pwm_id = pwm_id;
|
||||||
|
|
||||||
|
/* Configure PWM */
|
||||||
|
val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC |
|
||||||
|
PWM_CTRL2_MODE_HW;
|
||||||
|
|
||||||
|
ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("%s: Failed to configure PWM, Error %d\n",
|
||||||
|
pwm->label, ret);
|
||||||
|
|
||||||
|
kfree(pwm);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pwm;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(pwm_request);
|
||||||
|
|
||||||
|
void pwm_free(struct pwm_device *pwm)
|
||||||
|
{
|
||||||
|
pwm_disable(pwm);
|
||||||
|
kfree(pwm);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(pwm_free);
|
|
@ -114,7 +114,7 @@ static int ucb1400_core_probe(struct device *dev)
|
||||||
err3:
|
err3:
|
||||||
platform_device_put(ucb->ucb1400_ts);
|
platform_device_put(ucb->ucb1400_ts);
|
||||||
err2:
|
err2:
|
||||||
platform_device_unregister(ucb->ucb1400_gpio);
|
platform_device_del(ucb->ucb1400_gpio);
|
||||||
err1:
|
err1:
|
||||||
platform_device_put(ucb->ucb1400_gpio);
|
platform_device_put(ucb->ucb1400_gpio);
|
||||||
err0:
|
err0:
|
||||||
|
|
|
@ -95,6 +95,7 @@ enum wm831x_parent {
|
||||||
WM8311 = 0x8311,
|
WM8311 = 0x8311,
|
||||||
WM8312 = 0x8312,
|
WM8312 = 0x8312,
|
||||||
WM8320 = 0x8320,
|
WM8320 = 0x8320,
|
||||||
|
WM8321 = 0x8321,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
|
static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
|
||||||
|
@ -1533,6 +1534,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||||
dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
|
dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WM8321:
|
||||||
|
parent = WM8321;
|
||||||
|
wm831x->num_gpio = 12;
|
||||||
|
dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
|
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -1607,6 +1614,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WM8321:
|
||||||
|
ret = mfd_add_devices(wm831x->dev, -1,
|
||||||
|
wm8320_devs, ARRAY_SIZE(wm8320_devs),
|
||||||
|
NULL, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* If this happens the bus probe function is buggy */
|
/* If this happens the bus probe function is buggy */
|
||||||
BUG();
|
BUG();
|
||||||
|
@ -1744,10 +1757,8 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
|
||||||
struct wm831x *wm831x;
|
struct wm831x *wm831x;
|
||||||
|
|
||||||
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
|
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
|
||||||
if (wm831x == NULL) {
|
if (wm831x == NULL)
|
||||||
kfree(i2c);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
i2c_set_clientdata(i2c, wm831x);
|
i2c_set_clientdata(i2c, wm831x);
|
||||||
wm831x->dev = &i2c->dev;
|
wm831x->dev = &i2c->dev;
|
||||||
|
@ -1779,6 +1790,7 @@ static const struct i2c_device_id wm831x_i2c_id[] = {
|
||||||
{ "wm8311", WM8311 },
|
{ "wm8311", WM8311 },
|
||||||
{ "wm8312", WM8312 },
|
{ "wm8312", WM8312 },
|
||||||
{ "wm8320", WM8320 },
|
{ "wm8320", WM8320 },
|
||||||
|
{ "wm8321", WM8321 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
|
MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
|
||||||
|
|
|
@ -536,6 +536,7 @@ static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
kfree(wm8350->reg_cache);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,7 +701,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
|
||||||
|
|
||||||
ret = wm8350_irq_init(wm8350, irq, pdata);
|
ret = wm8350_irq_init(wm8350, irq, pdata);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err;
|
goto err_free;
|
||||||
|
|
||||||
if (wm8350->irq_base) {
|
if (wm8350->irq_base) {
|
||||||
ret = request_threaded_irq(wm8350->irq_base +
|
ret = request_threaded_irq(wm8350->irq_base +
|
||||||
|
@ -738,8 +739,9 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
|
||||||
|
|
||||||
err_irq:
|
err_irq:
|
||||||
wm8350_irq_exit(wm8350);
|
wm8350_irq_exit(wm8350);
|
||||||
err:
|
err_free:
|
||||||
kfree(wm8350->reg_cache);
|
kfree(wm8350->reg_cache);
|
||||||
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(wm8350_device_init);
|
EXPORT_SYMBOL_GPL(wm8350_device_init);
|
||||||
|
|
|
@ -326,8 +326,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
|
||||||
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
|
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
|
||||||
ARRAY_SIZE(wm8994_main_supplies),
|
ARRAY_SIZE(wm8994_main_supplies),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!wm8994->supplies)
|
if (!wm8994->supplies) {
|
||||||
|
ret = -ENOMEM;
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
|
for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
|
||||||
wm8994->supplies[i].supply = wm8994_main_supplies[i];
|
wm8994->supplies[i].supply = wm8994_main_supplies[i];
|
||||||
|
@ -495,10 +497,8 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
|
||||||
struct wm8994 *wm8994;
|
struct wm8994 *wm8994;
|
||||||
|
|
||||||
wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL);
|
wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL);
|
||||||
if (wm8994 == NULL) {
|
if (wm8994 == NULL)
|
||||||
kfree(i2c);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
i2c_set_clientdata(i2c, wm8994);
|
i2c_set_clientdata(i2c, wm8994);
|
||||||
wm8994->dev = &i2c->dev;
|
wm8994->dev = &i2c->dev;
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
#ifndef __LINUX_JZ4740_ADC
|
||||||
|
#define __LINUX_JZ4740_ADC
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* jz4740_adc_set_config - Configure a JZ4740 adc device
|
||||||
|
* @dev: Pointer to a jz4740-adc device
|
||||||
|
* @mask: Mask for the config value to be set
|
||||||
|
* @val: Value to be set
|
||||||
|
*
|
||||||
|
* This function can be used by the JZ4740 ADC mfd cells to configure their
|
||||||
|
* options in the shared config register.
|
||||||
|
*/
|
||||||
|
int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val);
|
||||||
|
|
||||||
|
#define JZ_ADC_CONFIG_SPZZ BIT(31)
|
||||||
|
#define JZ_ADC_CONFIG_EX_IN BIT(30)
|
||||||
|
#define JZ_ADC_CONFIG_DNUM_MASK (0x7 << 16)
|
||||||
|
#define JZ_ADC_CONFIG_DMA_ENABLE BIT(15)
|
||||||
|
#define JZ_ADC_CONFIG_XYZ_MASK (0x2 << 13)
|
||||||
|
#define JZ_ADC_CONFIG_SAMPLE_NUM_MASK (0x7 << 10)
|
||||||
|
#define JZ_ADC_CONFIG_CLKDIV_MASK (0xf << 5)
|
||||||
|
#define JZ_ADC_CONFIG_BAT_MB BIT(4)
|
||||||
|
|
||||||
|
#define JZ_ADC_CONFIG_DNUM(dnum) ((dnum) << 16)
|
||||||
|
#define JZ_ADC_CONFIG_XYZ_OFFSET(dnum) ((xyz) << 13)
|
||||||
|
#define JZ_ADC_CONFIG_SAMPLE_NUM(x) ((x) << 10)
|
||||||
|
#define JZ_ADC_CONFIG_CLKDIV(div) ((div) << 5)
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,220 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
|
|
||||||
*
|
|
||||||
* Initial development of this code was funded by
|
|
||||||
* Phytec Messtechnik GmbH, http://www.phytec.de
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __LINUX_MFD_MC13783_PRIV_H
|
|
||||||
#define __LINUX_MFD_MC13783_PRIV_H
|
|
||||||
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/mfd/mc13783.h>
|
|
||||||
#include <linux/mutex.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
|
|
||||||
struct mc13783 {
|
|
||||||
struct spi_device *spidev;
|
|
||||||
struct mutex lock;
|
|
||||||
int irq;
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
irq_handler_t irqhandler[MC13783_NUM_IRQ];
|
|
||||||
void *irqdata[MC13783_NUM_IRQ];
|
|
||||||
|
|
||||||
/* XXX these should go as platformdata to the regulator subdevice */
|
|
||||||
struct mc13783_regulator_init_data *regulators;
|
|
||||||
int num_regulators;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MC13783_REG_INTERRUPT_STATUS_0 0
|
|
||||||
#define MC13783_REG_INTERRUPT_MASK_0 1
|
|
||||||
#define MC13783_REG_INTERRUPT_SENSE_0 2
|
|
||||||
#define MC13783_REG_INTERRUPT_STATUS_1 3
|
|
||||||
#define MC13783_REG_INTERRUPT_MASK_1 4
|
|
||||||
#define MC13783_REG_INTERRUPT_SENSE_1 5
|
|
||||||
#define MC13783_REG_POWER_UP_MODE_SENSE 6
|
|
||||||
#define MC13783_REG_REVISION 7
|
|
||||||
#define MC13783_REG_SEMAPHORE 8
|
|
||||||
#define MC13783_REG_ARBITRATION_PERIPHERAL_AUDIO 9
|
|
||||||
#define MC13783_REG_ARBITRATION_SWITCHERS 10
|
|
||||||
#define MC13783_REG_ARBITRATION_REGULATORS_0 11
|
|
||||||
#define MC13783_REG_ARBITRATION_REGULATORS_1 12
|
|
||||||
#define MC13783_REG_POWER_CONTROL_0 13
|
|
||||||
#define MC13783_REG_POWER_CONTROL_1 14
|
|
||||||
#define MC13783_REG_POWER_CONTROL_2 15
|
|
||||||
#define MC13783_REG_REGEN_ASSIGNMENT 16
|
|
||||||
#define MC13783_REG_CONTROL_SPARE 17
|
|
||||||
#define MC13783_REG_MEMORY_A 18
|
|
||||||
#define MC13783_REG_MEMORY_B 19
|
|
||||||
#define MC13783_REG_RTC_TIME 20
|
|
||||||
#define MC13783_REG_RTC_ALARM 21
|
|
||||||
#define MC13783_REG_RTC_DAY 22
|
|
||||||
#define MC13783_REG_RTC_DAY_ALARM 23
|
|
||||||
#define MC13783_REG_SWITCHERS_0 24
|
|
||||||
#define MC13783_REG_SWITCHERS_1 25
|
|
||||||
#define MC13783_REG_SWITCHERS_2 26
|
|
||||||
#define MC13783_REG_SWITCHERS_3 27
|
|
||||||
#define MC13783_REG_SWITCHERS_4 28
|
|
||||||
#define MC13783_REG_SWITCHERS_5 29
|
|
||||||
#define MC13783_REG_REGULATOR_SETTING_0 30
|
|
||||||
#define MC13783_REG_REGULATOR_SETTING_1 31
|
|
||||||
#define MC13783_REG_REGULATOR_MODE_0 32
|
|
||||||
#define MC13783_REG_REGULATOR_MODE_1 33
|
|
||||||
#define MC13783_REG_POWER_MISCELLANEOUS 34
|
|
||||||
#define MC13783_REG_POWER_SPARE 35
|
|
||||||
#define MC13783_REG_AUDIO_RX_0 36
|
|
||||||
#define MC13783_REG_AUDIO_RX_1 37
|
|
||||||
#define MC13783_REG_AUDIO_TX 38
|
|
||||||
#define MC13783_REG_AUDIO_SSI_NETWORK 39
|
|
||||||
#define MC13783_REG_AUDIO_CODEC 40
|
|
||||||
#define MC13783_REG_AUDIO_STEREO_DAC 41
|
|
||||||
#define MC13783_REG_AUDIO_SPARE 42
|
|
||||||
#define MC13783_REG_ADC_0 43
|
|
||||||
#define MC13783_REG_ADC_1 44
|
|
||||||
#define MC13783_REG_ADC_2 45
|
|
||||||
#define MC13783_REG_ADC_3 46
|
|
||||||
#define MC13783_REG_ADC_4 47
|
|
||||||
#define MC13783_REG_CHARGER 48
|
|
||||||
#define MC13783_REG_USB 49
|
|
||||||
#define MC13783_REG_CHARGE_USB_SPARE 50
|
|
||||||
#define MC13783_REG_LED_CONTROL_0 51
|
|
||||||
#define MC13783_REG_LED_CONTROL_1 52
|
|
||||||
#define MC13783_REG_LED_CONTROL_2 53
|
|
||||||
#define MC13783_REG_LED_CONTROL_3 54
|
|
||||||
#define MC13783_REG_LED_CONTROL_4 55
|
|
||||||
#define MC13783_REG_LED_CONTROL_5 56
|
|
||||||
#define MC13783_REG_SPARE 57
|
|
||||||
#define MC13783_REG_TRIM_0 58
|
|
||||||
#define MC13783_REG_TRIM_1 59
|
|
||||||
#define MC13783_REG_TEST_0 60
|
|
||||||
#define MC13783_REG_TEST_1 61
|
|
||||||
#define MC13783_REG_TEST_2 62
|
|
||||||
#define MC13783_REG_TEST_3 63
|
|
||||||
#define MC13783_REG_NB 64
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reg Regulator Mode 0
|
|
||||||
*/
|
|
||||||
#define MC13783_REGCTRL_VAUDIO_EN (1 << 0)
|
|
||||||
#define MC13783_REGCTRL_VAUDIO_STBY (1 << 1)
|
|
||||||
#define MC13783_REGCTRL_VAUDIO_MODE (1 << 2)
|
|
||||||
#define MC13783_REGCTRL_VIOHI_EN (1 << 3)
|
|
||||||
#define MC13783_REGCTRL_VIOHI_STBY (1 << 4)
|
|
||||||
#define MC13783_REGCTRL_VIOHI_MODE (1 << 5)
|
|
||||||
#define MC13783_REGCTRL_VIOLO_EN (1 << 6)
|
|
||||||
#define MC13783_REGCTRL_VIOLO_STBY (1 << 7)
|
|
||||||
#define MC13783_REGCTRL_VIOLO_MODE (1 << 8)
|
|
||||||
#define MC13783_REGCTRL_VDIG_EN (1 << 9)
|
|
||||||
#define MC13783_REGCTRL_VDIG_STBY (1 << 10)
|
|
||||||
#define MC13783_REGCTRL_VDIG_MODE (1 << 11)
|
|
||||||
#define MC13783_REGCTRL_VGEN_EN (1 << 12)
|
|
||||||
#define MC13783_REGCTRL_VGEN_STBY (1 << 13)
|
|
||||||
#define MC13783_REGCTRL_VGEN_MODE (1 << 14)
|
|
||||||
#define MC13783_REGCTRL_VRFDIG_EN (1 << 15)
|
|
||||||
#define MC13783_REGCTRL_VRFDIG_STBY (1 << 16)
|
|
||||||
#define MC13783_REGCTRL_VRFDIG_MODE (1 << 17)
|
|
||||||
#define MC13783_REGCTRL_VRFREF_EN (1 << 18)
|
|
||||||
#define MC13783_REGCTRL_VRFREF_STBY (1 << 19)
|
|
||||||
#define MC13783_REGCTRL_VRFREF_MODE (1 << 20)
|
|
||||||
#define MC13783_REGCTRL_VRFCP_EN (1 << 21)
|
|
||||||
#define MC13783_REGCTRL_VRFCP_STBY (1 << 22)
|
|
||||||
#define MC13783_REGCTRL_VRFCP_MODE (1 << 23)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reg Regulator Mode 1
|
|
||||||
*/
|
|
||||||
#define MC13783_REGCTRL_VSIM_EN (1 << 0)
|
|
||||||
#define MC13783_REGCTRL_VSIM_STBY (1 << 1)
|
|
||||||
#define MC13783_REGCTRL_VSIM_MODE (1 << 2)
|
|
||||||
#define MC13783_REGCTRL_VESIM_EN (1 << 3)
|
|
||||||
#define MC13783_REGCTRL_VESIM_STBY (1 << 4)
|
|
||||||
#define MC13783_REGCTRL_VESIM_MODE (1 << 5)
|
|
||||||
#define MC13783_REGCTRL_VCAM_EN (1 << 6)
|
|
||||||
#define MC13783_REGCTRL_VCAM_STBY (1 << 7)
|
|
||||||
#define MC13783_REGCTRL_VCAM_MODE (1 << 8)
|
|
||||||
#define MC13783_REGCTRL_VRFBG_EN (1 << 9)
|
|
||||||
#define MC13783_REGCTRL_VRFBG_STBY (1 << 10)
|
|
||||||
#define MC13783_REGCTRL_VVIB_EN (1 << 11)
|
|
||||||
#define MC13783_REGCTRL_VRF1_EN (1 << 12)
|
|
||||||
#define MC13783_REGCTRL_VRF1_STBY (1 << 13)
|
|
||||||
#define MC13783_REGCTRL_VRF1_MODE (1 << 14)
|
|
||||||
#define MC13783_REGCTRL_VRF2_EN (1 << 15)
|
|
||||||
#define MC13783_REGCTRL_VRF2_STBY (1 << 16)
|
|
||||||
#define MC13783_REGCTRL_VRF2_MODE (1 << 17)
|
|
||||||
#define MC13783_REGCTRL_VMMC1_EN (1 << 18)
|
|
||||||
#define MC13783_REGCTRL_VMMC1_STBY (1 << 19)
|
|
||||||
#define MC13783_REGCTRL_VMMC1_MODE (1 << 20)
|
|
||||||
#define MC13783_REGCTRL_VMMC2_EN (1 << 21)
|
|
||||||
#define MC13783_REGCTRL_VMMC2_STBY (1 << 22)
|
|
||||||
#define MC13783_REGCTRL_VMMC2_MODE (1 << 23)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reg Regulator Misc.
|
|
||||||
*/
|
|
||||||
#define MC13783_REGCTRL_GPO1_EN (1 << 6)
|
|
||||||
#define MC13783_REGCTRL_GPO2_EN (1 << 8)
|
|
||||||
#define MC13783_REGCTRL_GPO3_EN (1 << 10)
|
|
||||||
#define MC13783_REGCTRL_GPO4_EN (1 << 12)
|
|
||||||
#define MC13783_REGCTRL_VIBPINCTRL (1 << 14)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reg Switcher 4
|
|
||||||
*/
|
|
||||||
#define MC13783_SWCTRL_SW1A_MODE (1 << 0)
|
|
||||||
#define MC13783_SWCTRL_SW1A_STBY_MODE (1 << 2)
|
|
||||||
#define MC13783_SWCTRL_SW1A_DVS_SPEED (1 << 6)
|
|
||||||
#define MC13783_SWCTRL_SW1A_PANIC_MODE (1 << 8)
|
|
||||||
#define MC13783_SWCTRL_SW1A_SOFTSTART (1 << 9)
|
|
||||||
#define MC13783_SWCTRL_SW1B_MODE (1 << 10)
|
|
||||||
#define MC13783_SWCTRL_SW1B_STBY_MODE (1 << 12)
|
|
||||||
#define MC13783_SWCTRL_SW1B_DVS_SPEED (1 << 14)
|
|
||||||
#define MC13783_SWCTRL_SW1B_PANIC_MODE (1 << 16)
|
|
||||||
#define MC13783_SWCTRL_SW1B_SOFTSTART (1 << 17)
|
|
||||||
#define MC13783_SWCTRL_PLL_EN (1 << 18)
|
|
||||||
#define MC13783_SWCTRL_PLL_FACTOR (1 << 19)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reg Switcher 5
|
|
||||||
*/
|
|
||||||
#define MC13783_SWCTRL_SW2A_MODE (1 << 0)
|
|
||||||
#define MC13783_SWCTRL_SW2A_STBY_MODE (1 << 2)
|
|
||||||
#define MC13783_SWCTRL_SW2A_DVS_SPEED (1 << 6)
|
|
||||||
#define MC13783_SWCTRL_SW2A_PANIC_MODE (1 << 8)
|
|
||||||
#define MC13783_SWCTRL_SW2A_SOFTSTART (1 << 9)
|
|
||||||
#define MC13783_SWCTRL_SW2B_MODE (1 << 10)
|
|
||||||
#define MC13783_SWCTRL_SW2B_STBY_MODE (1 << 12)
|
|
||||||
#define MC13783_SWCTRL_SW2B_DVS_SPEED (1 << 14)
|
|
||||||
#define MC13783_SWCTRL_SW2B_PANIC_MODE (1 << 16)
|
|
||||||
#define MC13783_SWCTRL_SW2B_SOFTSTART (1 << 17)
|
|
||||||
#define MC13783_SWSET_SW3 (1 << 18)
|
|
||||||
#define MC13783_SWCTRL_SW3_EN (1 << 20)
|
|
||||||
#define MC13783_SWCTRL_SW3_STBY (1 << 21)
|
|
||||||
#define MC13783_SWCTRL_SW3_MODE (1 << 22)
|
|
||||||
|
|
||||||
static inline int mc13783_set_bits(struct mc13783 *mc13783, unsigned int offset,
|
|
||||||
u32 mask, u32 val)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
mc13783_lock(mc13783);
|
|
||||||
ret = mc13783_reg_rmw(mc13783, offset, mask, val);
|
|
||||||
mc13783_unlock(mc13783);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* __LINUX_MFD_MC13783_PRIV_H */
|
|
|
@ -21,6 +21,8 @@ int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val);
|
||||||
int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
|
int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
|
||||||
u32 mask, u32 val);
|
u32 mask, u32 val);
|
||||||
|
|
||||||
|
int mc13783_get_flags(struct mc13783 *mc13783);
|
||||||
|
|
||||||
int mc13783_irq_request(struct mc13783 *mc13783, int irq,
|
int mc13783_irq_request(struct mc13783 *mc13783, int irq,
|
||||||
irq_handler_t handler, const char *name, void *dev);
|
irq_handler_t handler, const char *name, void *dev);
|
||||||
int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
|
int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) ST-Ericsson SA 2010
|
||||||
|
*
|
||||||
|
* License Terms: GNU General Public License, version 2
|
||||||
|
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LINUX_MFD_STMPE_H
|
||||||
|
#define __LINUX_MFD_STMPE_H
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
|
||||||
|
enum stmpe_block {
|
||||||
|
STMPE_BLOCK_GPIO = 1 << 0,
|
||||||
|
STMPE_BLOCK_KEYPAD = 1 << 1,
|
||||||
|
STMPE_BLOCK_TOUCHSCREEN = 1 << 2,
|
||||||
|
STMPE_BLOCK_ADC = 1 << 3,
|
||||||
|
STMPE_BLOCK_PWM = 1 << 4,
|
||||||
|
STMPE_BLOCK_ROTATOR = 1 << 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum stmpe_partnum {
|
||||||
|
STMPE811,
|
||||||
|
STMPE1601,
|
||||||
|
STMPE2401,
|
||||||
|
STMPE2403,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For registers whose locations differ on variants, the correct address is
|
||||||
|
* obtained by indexing stmpe->regs with one of the following.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
STMPE_IDX_CHIP_ID,
|
||||||
|
STMPE_IDX_ICR_LSB,
|
||||||
|
STMPE_IDX_IER_LSB,
|
||||||
|
STMPE_IDX_ISR_MSB,
|
||||||
|
STMPE_IDX_GPMR_LSB,
|
||||||
|
STMPE_IDX_GPSR_LSB,
|
||||||
|
STMPE_IDX_GPCR_LSB,
|
||||||
|
STMPE_IDX_GPDR_LSB,
|
||||||
|
STMPE_IDX_GPEDR_MSB,
|
||||||
|
STMPE_IDX_GPRER_LSB,
|
||||||
|
STMPE_IDX_GPFER_LSB,
|
||||||
|
STMPE_IDX_GPAFR_U_MSB,
|
||||||
|
STMPE_IDX_IEGPIOR_LSB,
|
||||||
|
STMPE_IDX_ISGPIOR_MSB,
|
||||||
|
STMPE_IDX_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct stmpe_variant_info;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stmpe - STMPE MFD structure
|
||||||
|
* @lock: lock protecting I/O operations
|
||||||
|
* @irq_lock: IRQ bus lock
|
||||||
|
* @dev: device, mostly for dev_dbg()
|
||||||
|
* @i2c: i2c client
|
||||||
|
* @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_*.
|
||||||
|
* @irq_base: starting IRQ number for internal IRQs
|
||||||
|
* @num_gpios: number of gpios, differs for variants
|
||||||
|
* @ier: cache of IER registers for bus_lock
|
||||||
|
* @oldier: cache of IER registers for bus_lock
|
||||||
|
* @pdata: platform data
|
||||||
|
*/
|
||||||
|
struct stmpe {
|
||||||
|
struct mutex lock;
|
||||||
|
struct mutex irq_lock;
|
||||||
|
struct device *dev;
|
||||||
|
struct i2c_client *i2c;
|
||||||
|
enum stmpe_partnum partnum;
|
||||||
|
struct stmpe_variant_info *variant;
|
||||||
|
const u8 *regs;
|
||||||
|
|
||||||
|
int irq_base;
|
||||||
|
int num_gpios;
|
||||||
|
u8 ier[2];
|
||||||
|
u8 oldier[2];
|
||||||
|
struct stmpe_platform_data *pdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 data);
|
||||||
|
extern int stmpe_reg_read(struct stmpe *stmpe, u8 reg);
|
||||||
|
extern int stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length,
|
||||||
|
u8 *values);
|
||||||
|
extern int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
|
||||||
|
const u8 *values);
|
||||||
|
extern int stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val);
|
||||||
|
extern int stmpe_set_altfunc(struct stmpe *stmpe, u32 pins,
|
||||||
|
enum stmpe_block block);
|
||||||
|
extern int stmpe_enable(struct stmpe *stmpe, unsigned int blocks);
|
||||||
|
extern int stmpe_disable(struct stmpe *stmpe, unsigned int blocks);
|
||||||
|
|
||||||
|
struct matrix_keymap_data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stmpe_keypad_platform_data - STMPE keypad platform data
|
||||||
|
* @keymap_data: key map table and size
|
||||||
|
* @debounce_ms: debounce interval, in ms. Maximum is
|
||||||
|
* %STMPE_KEYPAD_MAX_DEBOUNCE.
|
||||||
|
* @scan_count: number of key scanning cycles to confirm key data.
|
||||||
|
* Maximum is %STMPE_KEYPAD_MAX_SCAN_COUNT.
|
||||||
|
* @no_autorepeat: disable key autorepeat
|
||||||
|
*/
|
||||||
|
struct stmpe_keypad_platform_data {
|
||||||
|
struct matrix_keymap_data *keymap_data;
|
||||||
|
unsigned int debounce_ms;
|
||||||
|
unsigned int scan_count;
|
||||||
|
bool no_autorepeat;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stmpe_gpio_platform_data - STMPE GPIO platform data
|
||||||
|
* @gpio_base: first gpio number assigned. A maximum of
|
||||||
|
* %STMPE_NR_GPIOS GPIOs will be allocated.
|
||||||
|
*/
|
||||||
|
struct stmpe_gpio_platform_data {
|
||||||
|
int gpio_base;
|
||||||
|
void (*setup)(struct stmpe *stmpe, unsigned gpio_base);
|
||||||
|
void (*remove)(struct stmpe *stmpe, unsigned gpio_base);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stmpe_ts_platform_data - stmpe811 touch screen controller platform
|
||||||
|
* data
|
||||||
|
* @sample_time: ADC converstion time in number of clock.
|
||||||
|
* (0 -> 36 clocks, 1 -> 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks,
|
||||||
|
* 4 -> 80 clocks, 5 -> 96 clocks, 6 -> 144 clocks),
|
||||||
|
* recommended is 4.
|
||||||
|
* @mod_12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC)
|
||||||
|
* @ref_sel: ADC reference source
|
||||||
|
* (0 -> internal reference, 1 -> external reference)
|
||||||
|
* @adc_freq: ADC Clock speed
|
||||||
|
* (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz)
|
||||||
|
* @ave_ctrl: Sample average control
|
||||||
|
* (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples)
|
||||||
|
* @touch_det_delay: Touch detect interrupt delay
|
||||||
|
* (0 -> 10 us, 1 -> 50 us, 2 -> 100 us, 3 -> 500 us,
|
||||||
|
* 4-> 1 ms, 5 -> 5 ms, 6 -> 10 ms, 7 -> 50 ms)
|
||||||
|
* recommended is 3
|
||||||
|
* @settling: Panel driver settling time
|
||||||
|
* (0 -> 10 us, 1 -> 100 us, 2 -> 500 us, 3 -> 1 ms,
|
||||||
|
* 4 -> 5 ms, 5 -> 10 ms, 6 for 50 ms, 7 -> 100 ms)
|
||||||
|
* recommended is 2
|
||||||
|
* @fraction_z: Length of the fractional part in z
|
||||||
|
* (fraction_z ([0..7]) = Count of the fractional part)
|
||||||
|
* recommended is 7
|
||||||
|
* @i_drive: current limit value of the touchscreen drivers
|
||||||
|
* (0 -> 20 mA typical 35 mA max, 1 -> 50 mA typical 80 mA max)
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
struct stmpe_ts_platform_data {
|
||||||
|
u8 sample_time;
|
||||||
|
u8 mod_12b;
|
||||||
|
u8 ref_sel;
|
||||||
|
u8 adc_freq;
|
||||||
|
u8 ave_ctrl;
|
||||||
|
u8 touch_det_delay;
|
||||||
|
u8 settling;
|
||||||
|
u8 fraction_z;
|
||||||
|
u8 i_drive;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stmpe_platform_data - STMPE platform data
|
||||||
|
* @id: device id to distinguish between multiple STMPEs on the same board
|
||||||
|
* @blocks: bitmask of blocks to enable (use STMPE_BLOCK_*)
|
||||||
|
* @irq_trigger: IRQ trigger to use for the interrupt to the host
|
||||||
|
* @irq_invert_polarity: IRQ line is connected with reversed polarity
|
||||||
|
* @autosleep: bool to enable/disable stmpe autosleep
|
||||||
|
* @autosleep_timeout: inactivity timeout in milliseconds for autosleep
|
||||||
|
* @irq_base: base IRQ number. %STMPE_NR_IRQS irqs will be used, or
|
||||||
|
* %STMPE_NR_INTERNAL_IRQS if the GPIO driver is not used.
|
||||||
|
* @gpio: GPIO-specific platform data
|
||||||
|
* @keypad: keypad-specific platform data
|
||||||
|
* @ts: touchscreen-specific platform data
|
||||||
|
*/
|
||||||
|
struct stmpe_platform_data {
|
||||||
|
int id;
|
||||||
|
unsigned int blocks;
|
||||||
|
int irq_base;
|
||||||
|
unsigned int irq_trigger;
|
||||||
|
bool irq_invert_polarity;
|
||||||
|
bool autosleep;
|
||||||
|
int autosleep_timeout;
|
||||||
|
|
||||||
|
struct stmpe_gpio_platform_data *gpio;
|
||||||
|
struct stmpe_keypad_platform_data *keypad;
|
||||||
|
struct stmpe_ts_platform_data *ts;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define STMPE_NR_INTERNAL_IRQS 9
|
||||||
|
#define STMPE_INT_GPIO(x) (STMPE_NR_INTERNAL_IRQS + (x))
|
||||||
|
|
||||||
|
#define STMPE_NR_GPIOS 24
|
||||||
|
#define STMPE_NR_IRQS STMPE_INT_GPIO(STMPE_NR_GPIOS)
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef __LINUX_MFD_TPS6586X_H
|
||||||
|
#define __LINUX_MFD_TPS6586X_H
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TPS6586X_ID_SM_0,
|
||||||
|
TPS6586X_ID_SM_1,
|
||||||
|
TPS6586X_ID_SM_2,
|
||||||
|
TPS6586X_ID_LDO_0,
|
||||||
|
TPS6586X_ID_LDO_1,
|
||||||
|
TPS6586X_ID_LDO_2,
|
||||||
|
TPS6586X_ID_LDO_3,
|
||||||
|
TPS6586X_ID_LDO_4,
|
||||||
|
TPS6586X_ID_LDO_5,
|
||||||
|
TPS6586X_ID_LDO_6,
|
||||||
|
TPS6586X_ID_LDO_7,
|
||||||
|
TPS6586X_ID_LDO_8,
|
||||||
|
TPS6586X_ID_LDO_9,
|
||||||
|
TPS6586X_ID_LDO_RTC,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tps6586x_subdev_info {
|
||||||
|
int id;
|
||||||
|
const char *name;
|
||||||
|
void *platform_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tps6586x_platform_data {
|
||||||
|
int num_subdevs;
|
||||||
|
struct tps6586x_subdev_info *subdevs;
|
||||||
|
|
||||||
|
int gpio_base;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: the functions below are not intended for use outside
|
||||||
|
* of the TPS6586X sub-device drivers
|
||||||
|
*/
|
||||||
|
extern int tps6586x_write(struct device *dev, int reg, uint8_t val);
|
||||||
|
extern int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val);
|
||||||
|
extern int tps6586x_read(struct device *dev, int reg, uint8_t *val);
|
||||||
|
extern int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val);
|
||||||
|
extern int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask);
|
||||||
|
extern int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask);
|
||||||
|
extern int tps6586x_update(struct device *dev, int reg, uint8_t val,
|
||||||
|
uint8_t mask);
|
||||||
|
|
||||||
|
#endif /*__LINUX_MFD_TPS6586X_H */
|
|
@ -36,6 +36,10 @@
|
||||||
#define WM8994_GP_FN_WSEQ_STATUS 16
|
#define WM8994_GP_FN_WSEQ_STATUS 16
|
||||||
#define WM8994_GP_FN_FIFO_ERROR 17
|
#define WM8994_GP_FN_FIFO_ERROR 17
|
||||||
#define WM8994_GP_FN_OPCLK 18
|
#define WM8994_GP_FN_OPCLK 18
|
||||||
|
#define WM8994_GP_FN_THW 19
|
||||||
|
#define WM8994_GP_FN_DCS_DONE 20
|
||||||
|
#define WM8994_GP_FN_FLL1_OUT 21
|
||||||
|
#define WM8994_GP_FN_FLL2_OUT 22
|
||||||
|
|
||||||
#define WM8994_GPN_DIR 0x8000 /* GPN_DIR */
|
#define WM8994_GPN_DIR 0x8000 /* GPN_DIR */
|
||||||
#define WM8994_GPN_DIR_MASK 0x8000 /* GPN_DIR */
|
#define WM8994_GPN_DIR_MASK 0x8000 /* GPN_DIR */
|
||||||
|
|
Loading…
Reference in New Issue