Merge tag 'mfd-3.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6

Pull MFS update from Samuel Ortiz:
 "This is the MFD patch set for the 3.8 merge window.

  We have several new drivers, most of the time coming with their sub
  devices drivers:

   - Austria Microsystem's AS3711
   - Nano River's viperboard
   - TI's TPS80031, AM335x TS/ADC,
   - Realtek's MMC/memstick card reader
   - Nokia's retu

  We also got some notable cleanups and improvements:

   - tps6586x got converted to IRQ domains.
   - tps65910 and tps65090 moved to the regmap IRQ API.
   - STMPE is now Device Tree aware.
   - A general twl6040 and twl-core cleanup, with moves to the regmap
     I/O and IRQ APIs and a conversion to the recently added PWM
     framework.
   - sta2x11 gained regmap support.

  Then the rest is mostly tiny cleanups and fixes, among which we have
  Mark's wm5xxx and wm8xxx patchset."

Far amount of annoying but largely trivial conflicts.  Many due to
__devinit/exit removal, others due to one or two of the new drivers also
having come in through another tree.

* tag 'mfd-3.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (119 commits)
  mfd: tps6507x: Convert to devm_kzalloc
  mfd: stmpe: Update DT support for stmpe driver
  mfd: wm5102: Add readback of DSP status 3 register
  mfd: arizona: Log if we fail to create the primary IRQ domain
  mfd: tps80031: MFD_TPS80031 needs to select REGMAP_IRQ
  mfd: tps80031: Add terminating entry for tps80031_id_table
  mfd: sta2x11: Fix potential NULL pointer dereference in __sta2x11_mfd_mask()
  mfd: wm5102: Add tuning for revision B
  mfd: arizona: Defer patch initialistation until after first device boot
  mfd: tps65910: Fix wrong ack_base register
  mfd: tps65910: Remove unused data
  mfd: stmpe: Get rid of irq_invert_polarity
  mfd: ab8500-core: Fix invalid free of devm_ allocated data
  mfd: wm5102: Mark DSP memory regions as volatile
  mfd: wm5102: Correct default for LDO1_CONTROL_2
  mfd: arizona: Register haptics devices
  mfd: wm8994: Make current device behaviour the default
  mfd: tps65090: MFD_TPS65090 needs to select REGMAP_IRQ
  mfd: Fix stmpe.c build when OF is not enabled
  mfd: jz4740-adc: Use devm_kzalloc
  ...
This commit is contained in:
Linus Torvalds
2012-12-16 18:55:20 -08:00
87 changed files with 6780 additions and 2455 deletions
@@ -0,0 +1,28 @@
* ST Microelectronics STMPE Multi-Functional Device
STMPE is an MFD device which may expose the following inbuilt devices: gpio,
keypad, touchscreen, adc, pwm, rotator.
Required properties:
- compatible : "st,stmpe[610|801|811|1601|2401|2403]"
- reg : I2C/SPI address of the device
Optional properties:
- interrupts : The interrupt outputs from the controller
- interrupt-controller : Marks the device node as an interrupt controller
- interrupt-parent : Specifies which IRQ controller we're connected to
- wakeup-source : Marks the input device as wakable
- st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024
Example:
stmpe1601: stmpe1601@40 {
compatible = "st,stmpe1601";
reg = <0x40>;
interrupts = <26 0x4>;
interrupt-parent = <&gpio6>;
interrupt-controller;
wakeup-source;
st,autosleep-timeout = <1024>;
};
@@ -11,6 +11,9 @@ Required properties:
using the standard binding for regulators found at
Documentation/devicetree/bindings/regulator/regulator.txt.
Optional properties:
- ti,pmic-shutdown-controller: Telling the PMIC to shutdown on PWR_EN toggle.
The valid names for regulators are:
tps65217: dcdc1, dcdc2, dcdc3, ldo1, ldo2, ldo3 and ldo4
@@ -20,6 +23,7 @@ Example:
tps: tps@24 {
compatible = "ti,tps65217";
ti,pmic-shutdown-controller;
regulators {
dcdc1_reg: dcdc1 {
+13
View File
@@ -683,4 +683,17 @@ config GPIO_MSIC
Enable support for GPIO on intel MSIC controllers found in
intel MID devices
comment "USB GPIO expanders:"
config GPIO_VIPERBOARD
tristate "Viperboard GPIO a & b support"
depends on MFD_VIPERBOARD && USB
help
Say yes here to access the GPIO signals of Nano River
Technologies Viperboard. There are two GPIO chips on the
board: gpioa and gpiob.
See viperboard API specification and Nano
River Tech's viperboard.h for detailed meaning
of the module parameters.
endif
+1
View File
@@ -76,6 +76,7 @@ obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
+5 -1
View File
@@ -185,7 +185,11 @@ static int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset)
struct da9052_gpio *gpio = to_da9052_gpio(gc);
struct da9052 *da9052 = gpio->da9052;
return da9052->irq_base + DA9052_IRQ_GPI0 + offset;
int irq;
irq = regmap_irq_get_virq(da9052->irq_data, DA9052_IRQ_GPI0 + offset);
return irq;
}
static struct gpio_chip reference_gp = {
+9
View File
@@ -80,6 +80,14 @@ static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
val, mask);
}
static int tps6586x_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
return tps6586x_irq_get_virq(tps6586x_gpio->parent,
TPS6586X_INT_PLDO_0 + offset);
}
static int tps6586x_gpio_probe(struct platform_device *pdev)
{
struct tps6586x_platform_data *pdata;
@@ -106,6 +114,7 @@ static int tps6586x_gpio_probe(struct platform_device *pdev)
tps6586x_gpio->gpio_chip.direction_output = tps6586x_gpio_output;
tps6586x_gpio->gpio_chip.set = tps6586x_gpio_set;
tps6586x_gpio->gpio_chip.get = tps6586x_gpio_get;
tps6586x_gpio->gpio_chip.to_irq = tps6586x_gpio_to_irq;
#ifdef CONFIG_OF_GPIO
tps6586x_gpio->gpio_chip.of_node = pdev->dev.parent->of_node;
+6 -6
View File
@@ -355,13 +355,13 @@ static struct gpio_chip twl_gpiochip = {
static int gpio_twl4030_pulls(u32 ups, u32 downs)
{
u8 message[6];
u8 message[5];
unsigned i, gpio_bit;
/* For most pins, a pulldown was enabled by default.
* We should have data that's specific to this board.
*/
for (gpio_bit = 1, i = 1; i < 6; i++) {
for (gpio_bit = 1, i = 0; i < 5; i++) {
u8 bit_mask;
unsigned j;
@@ -380,16 +380,16 @@ static int gpio_twl4030_pulls(u32 ups, u32 downs)
static int gpio_twl4030_debounce(u32 debounce, u8 mmc_cd)
{
u8 message[4];
u8 message[3];
/* 30 msec of debouncing is always used for MMC card detect,
* and is optional for everything else.
*/
message[1] = (debounce & 0xff) | (mmc_cd & 0x03);
message[0] = (debounce & 0xff) | (mmc_cd & 0x03);
debounce >>= 8;
message[2] = (debounce & 0xff);
message[1] = (debounce & 0xff);
debounce >>= 8;
message[3] = (debounce & 0x03);
message[2] = (debounce & 0x03);
return twl_i2c_write(TWL4030_MODULE_GPIO, message,
REG_GPIO_DEBEN1, 3);
File diff suppressed because it is too large Load Diff
+10
View File
@@ -818,6 +818,16 @@ config I2C_TINY_USB
This driver can also be built as a module. If so, the module
will be called i2c-tiny-usb.
config I2C_VIPERBOARD
tristate "Viperboard I2C master support"
depends on MFD_VIPERBOARD && USB
help
Say yes here to access the I2C part of the Nano River
Technologies Viperboard as I2C master.
See viperboard API specification and Nano
River Tech's viperboard.h for detailed meaning
of the module parameters.
comment "Other I2C/SMBus bus drivers"
config I2C_ACORN
+1
View File
@@ -79,6 +79,7 @@ obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o
# Other I2C/SMBus bus drivers
obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
+480
View File
@@ -0,0 +1,480 @@
/*
* Nano River Technologies viperboard i2c master driver
*
* (C) 2012 by Lemonage GmbH
* Author: Lars Poeschel <poeschel@lemonage.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/errno.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/mfd/viperboard.h>
struct vprbrd_i2c {
struct i2c_adapter i2c;
u8 bus_freq_param;
};
/* i2c bus frequency module parameter */
static u8 i2c_bus_param;
static unsigned int i2c_bus_freq = 100;
module_param(i2c_bus_freq, int, 0);
MODULE_PARM_DESC(i2c_bus_freq,
"i2c bus frequency in khz (default is 100) valid values: 10, 100, 200, 400, 1000, 3000, 6000");
static int vprbrd_i2c_status(struct i2c_adapter *i2c,
struct vprbrd_i2c_status *status, bool prev_error)
{
u16 bytes_xfer;
int ret;
struct vprbrd *vb = (struct vprbrd *)i2c->algo_data;
/* check for protocol error */
bytes_xfer = sizeof(struct vprbrd_i2c_status);
ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
VPRBRD_USB_REQUEST_I2C, VPRBRD_USB_TYPE_IN, 0x0000, 0x0000,
status, bytes_xfer, VPRBRD_USB_TIMEOUT_MS);
if (ret != bytes_xfer)
prev_error = true;
if (prev_error) {
dev_err(&i2c->dev, "failure in usb communication\n");
return -EREMOTEIO;
}
dev_dbg(&i2c->dev, " status = %d\n", status->status);
if (status->status != 0x00) {
dev_err(&i2c->dev, "failure: i2c protocol error\n");
return -EPROTO;
}
return 0;
}
static int vprbrd_i2c_receive(struct usb_device *usb_dev,
struct vprbrd_i2c_read_msg *rmsg, int bytes_xfer)
{
int ret, bytes_actual;
int error = 0;
/* send the read request */
ret = usb_bulk_msg(usb_dev,
usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), rmsg,
sizeof(struct vprbrd_i2c_read_hdr), &bytes_actual,
VPRBRD_USB_TIMEOUT_MS);
if ((ret < 0)
|| (bytes_actual != sizeof(struct vprbrd_i2c_read_hdr))) {
dev_err(&usb_dev->dev, "failure transmitting usb\n");
error = -EREMOTEIO;
}
/* read the actual data */
ret = usb_bulk_msg(usb_dev,
usb_rcvbulkpipe(usb_dev, VPRBRD_EP_IN), rmsg,
bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS);
if ((ret < 0) || (bytes_xfer != bytes_actual)) {
dev_err(&usb_dev->dev, "failure receiving usb\n");
error = -EREMOTEIO;
}
return error;
}
static int vprbrd_i2c_addr(struct usb_device *usb_dev,
struct vprbrd_i2c_addr_msg *amsg)
{
int ret, bytes_actual;
ret = usb_bulk_msg(usb_dev,
usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), amsg,
sizeof(struct vprbrd_i2c_addr_msg), &bytes_actual,
VPRBRD_USB_TIMEOUT_MS);
if ((ret < 0) ||
(sizeof(struct vprbrd_i2c_addr_msg) != bytes_actual)) {
dev_err(&usb_dev->dev, "failure transmitting usb\n");
return -EREMOTEIO;
}
return 0;
}
static int vprbrd_i2c_read(struct vprbrd *vb, struct i2c_msg *msg)
{
int ret;
u16 remain_len, bytes_xfer, len1, len2,
start = 0x0000;
struct vprbrd_i2c_read_msg *rmsg =
(struct vprbrd_i2c_read_msg *)vb->buf;
remain_len = msg->len;
rmsg->header.cmd = VPRBRD_I2C_CMD_READ;
while (remain_len > 0) {
rmsg->header.addr = cpu_to_le16(start + 0x4000);
if (remain_len <= 255) {
len1 = remain_len;
len2 = 0x00;
rmsg->header.len0 = remain_len;
rmsg->header.len1 = 0x00;
rmsg->header.len2 = 0x00;
rmsg->header.len3 = 0x00;
rmsg->header.len4 = 0x00;
rmsg->header.len5 = 0x00;
remain_len = 0;
} else if (remain_len <= 510) {
len1 = remain_len;
len2 = 0x00;
rmsg->header.len0 = remain_len - 255;
rmsg->header.len1 = 0xff;
rmsg->header.len2 = 0x00;
rmsg->header.len3 = 0x00;
rmsg->header.len4 = 0x00;
rmsg->header.len5 = 0x00;
remain_len = 0;
} else if (remain_len <= 512) {
len1 = remain_len;
len2 = 0x00;
rmsg->header.len0 = remain_len - 510;
rmsg->header.len1 = 0xff;
rmsg->header.len2 = 0xff;
rmsg->header.len3 = 0x00;
rmsg->header.len4 = 0x00;
rmsg->header.len5 = 0x00;
remain_len = 0;
} else if (remain_len <= 767) {
len1 = 512;
len2 = remain_len - 512;
rmsg->header.len0 = 0x02;
rmsg->header.len1 = 0xff;
rmsg->header.len2 = 0xff;
rmsg->header.len3 = remain_len - 512;
rmsg->header.len4 = 0x00;
rmsg->header.len5 = 0x00;
bytes_xfer = remain_len;
remain_len = 0;
} else if (remain_len <= 1022) {
len1 = 512;
len2 = remain_len - 512;
rmsg->header.len0 = 0x02;
rmsg->header.len1 = 0xff;
rmsg->header.len2 = 0xff;
rmsg->header.len3 = remain_len - 767;
rmsg->header.len4 = 0xff;
rmsg->header.len5 = 0x00;
remain_len = 0;
} else if (remain_len <= 1024) {
len1 = 512;
len2 = remain_len - 512;
rmsg->header.len0 = 0x02;
rmsg->header.len1 = 0xff;
rmsg->header.len2 = 0xff;
rmsg->header.len3 = remain_len - 1022;
rmsg->header.len4 = 0xff;
rmsg->header.len5 = 0xff;
remain_len = 0;
} else {
len1 = 512;
len2 = 512;
rmsg->header.len0 = 0x02;
rmsg->header.len1 = 0xff;
rmsg->header.len2 = 0xff;
rmsg->header.len3 = 0x02;
rmsg->header.len4 = 0xff;
rmsg->header.len5 = 0xff;
remain_len -= 1024;
start += 1024;
}
rmsg->header.tf1 = cpu_to_le16(len1);
rmsg->header.tf2 = cpu_to_le16(len2);
/* first read transfer */
ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len1);
if (ret < 0)
return ret;
/* copy the received data */
memcpy(msg->buf + start, rmsg, len1);
/* second read transfer if neccessary */
if (len2 > 0) {
ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len2);
if (ret < 0)
return ret;
/* copy the received data */
memcpy(msg->buf + start + 512, rmsg, len2);
}
}
return 0;
}
static int vprbrd_i2c_write(struct vprbrd *vb, struct i2c_msg *msg)
{
int ret, bytes_actual;
u16 remain_len, bytes_xfer,
start = 0x0000;
struct vprbrd_i2c_write_msg *wmsg =
(struct vprbrd_i2c_write_msg *)vb->buf;
remain_len = msg->len;
wmsg->header.cmd = VPRBRD_I2C_CMD_WRITE;
wmsg->header.last = 0x00;
wmsg->header.chan = 0x00;
wmsg->header.spi = 0x0000;
while (remain_len > 0) {
wmsg->header.addr = cpu_to_le16(start + 0x4000);
if (remain_len > 503) {
wmsg->header.len1 = 0xff;
wmsg->header.len2 = 0xf8;
remain_len -= 503;
bytes_xfer = 503 + sizeof(struct vprbrd_i2c_write_hdr);
start += 503;
} else if (remain_len > 255) {
wmsg->header.len1 = 0xff;
wmsg->header.len2 = (remain_len - 255);
bytes_xfer = remain_len +
sizeof(struct vprbrd_i2c_write_hdr);
remain_len = 0;
} else {
wmsg->header.len1 = remain_len;
wmsg->header.len2 = 0x00;
bytes_xfer = remain_len +
sizeof(struct vprbrd_i2c_write_hdr);
remain_len = 0;
}
memcpy(wmsg->data, msg->buf + start,
bytes_xfer - sizeof(struct vprbrd_i2c_write_hdr));
ret = usb_bulk_msg(vb->usb_dev,
usb_sndbulkpipe(vb->usb_dev,
VPRBRD_EP_OUT), wmsg,
bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS);
if ((ret < 0) || (bytes_xfer != bytes_actual))
return -EREMOTEIO;
}
return 0;
}
static int vprbrd_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs,
int num)
{
struct i2c_msg *pmsg;
int i, ret,
error = 0;
struct vprbrd *vb = (struct vprbrd *)i2c->algo_data;
struct vprbrd_i2c_addr_msg *amsg =
(struct vprbrd_i2c_addr_msg *)vb->buf;
struct vprbrd_i2c_status *smsg = (struct vprbrd_i2c_status *)vb->buf;
dev_dbg(&i2c->dev, "master xfer %d messages:\n", num);
for (i = 0 ; i < num ; i++) {
pmsg = &msgs[i];
dev_dbg(&i2c->dev,
" %d: %s (flags %d) %d bytes to 0x%02x\n",
i, pmsg->flags & I2C_M_RD ? "read" : "write",
pmsg->flags, pmsg->len, pmsg->addr);
/* msgs longer than 2048 bytes are not supported by adapter */
if (pmsg->len > 2048)
return -EINVAL;
mutex_lock(&vb->lock);
/* directly send the message */
if (pmsg->flags & I2C_M_RD) {
/* read data */
amsg->cmd = VPRBRD_I2C_CMD_ADDR;
amsg->unknown2 = 0x00;
amsg->unknown3 = 0x00;
amsg->addr = pmsg->addr;
amsg->unknown1 = 0x01;
amsg->len = cpu_to_le16(pmsg->len);
/* send the addr and len, we're interested to board */
ret = vprbrd_i2c_addr(vb->usb_dev, amsg);
if (ret < 0)
error = ret;
ret = vprbrd_i2c_read(vb, pmsg);
if (ret < 0)
error = ret;
ret = vprbrd_i2c_status(i2c, smsg, error);
if (ret < 0)
error = ret;
/* in case of protocol error, return the error */
if (error < 0)
goto error;
} else {
/* write data */
ret = vprbrd_i2c_write(vb, pmsg);
amsg->cmd = VPRBRD_I2C_CMD_ADDR;
amsg->unknown2 = 0x00;
amsg->unknown3 = 0x00;
amsg->addr = pmsg->addr;
amsg->unknown1 = 0x00;
amsg->len = cpu_to_le16(pmsg->len);
/* send the addr, the data goes to to board */
ret = vprbrd_i2c_addr(vb->usb_dev, amsg);
if (ret < 0)
error = ret;
ret = vprbrd_i2c_status(i2c, smsg, error);
if (ret < 0)
error = ret;
if (error < 0)
goto error;
}
mutex_unlock(&vb->lock);
}
return 0;
error:
mutex_unlock(&vb->lock);
return error;
}
static u32 vprbrd_i2c_func(struct i2c_adapter *i2c)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
/* This is the actual algorithm we define */
static const struct i2c_algorithm vprbrd_algorithm = {
.master_xfer = vprbrd_i2c_xfer,
.functionality = vprbrd_i2c_func,
};
static int __devinit vprbrd_i2c_probe(struct platform_device *pdev)
{
struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
struct vprbrd_i2c *vb_i2c;
int ret;
int pipe;
vb_i2c = kzalloc(sizeof(*vb_i2c), GFP_KERNEL);
if (vb_i2c == NULL)
return -ENOMEM;
/* setup i2c adapter description */
vb_i2c->i2c.owner = THIS_MODULE;
vb_i2c->i2c.class = I2C_CLASS_HWMON;
vb_i2c->i2c.algo = &vprbrd_algorithm;
vb_i2c->i2c.algo_data = vb;
/* save the param in usb capabable memory */
vb_i2c->bus_freq_param = i2c_bus_param;
snprintf(vb_i2c->i2c.name, sizeof(vb_i2c->i2c.name),
"viperboard at bus %03d device %03d",
vb->usb_dev->bus->busnum, vb->usb_dev->devnum);
/* setting the bus frequency */
if ((i2c_bus_param <= VPRBRD_I2C_FREQ_10KHZ)
&& (i2c_bus_param >= VPRBRD_I2C_FREQ_6MHZ)) {
pipe = usb_sndctrlpipe(vb->usb_dev, 0);
ret = usb_control_msg(vb->usb_dev, pipe,
VPRBRD_USB_REQUEST_I2C_FREQ, VPRBRD_USB_TYPE_OUT,
0x0000, 0x0000, &vb_i2c->bus_freq_param, 1,
VPRBRD_USB_TIMEOUT_MS);
if (ret != 1) {
dev_err(&pdev->dev,
"failure setting i2c_bus_freq to %d\n", i2c_bus_freq);
ret = -EIO;
goto error;
}
} else {
dev_err(&pdev->dev,
"invalid i2c_bus_freq setting:%d\n", i2c_bus_freq);
ret = -EIO;
goto error;
}
vb_i2c->i2c.dev.parent = &pdev->dev;
/* attach to i2c layer */
i2c_add_adapter(&vb_i2c->i2c);
platform_set_drvdata(pdev, vb_i2c);
return 0;
error:
kfree(vb_i2c);
return ret;
}
static int __devexit vprbrd_i2c_remove(struct platform_device *pdev)
{
struct vprbrd_i2c *vb_i2c = platform_get_drvdata(pdev);
int ret;
ret = i2c_del_adapter(&vb_i2c->i2c);
return ret;
}
static struct platform_driver vprbrd_i2c_driver = {
.driver.name = "viperboard-i2c",
.driver.owner = THIS_MODULE,
.probe = vprbrd_i2c_probe,
.remove = __devexit_p(vprbrd_i2c_remove),
};
static int __init vprbrd_i2c_init(void)
{
switch (i2c_bus_freq) {
case 6000:
i2c_bus_param = VPRBRD_I2C_FREQ_6MHZ;
break;
case 3000:
i2c_bus_param = VPRBRD_I2C_FREQ_3MHZ;
break;
case 1000:
i2c_bus_param = VPRBRD_I2C_FREQ_1MHZ;
break;
case 400:
i2c_bus_param = VPRBRD_I2C_FREQ_400KHZ;
break;
case 200:
i2c_bus_param = VPRBRD_I2C_FREQ_200KHZ;
break;
case 100:
i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ;
break;
case 10:
i2c_bus_param = VPRBRD_I2C_FREQ_10KHZ;
break;
default:
pr_warn("invalid i2c_bus_freq (%d)\n", i2c_bus_freq);
i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ;
}
return platform_driver_register(&vprbrd_i2c_driver);
}
subsys_initcall(vprbrd_i2c_init);
static void __exit vprbrd_i2c_exit(void)
{
platform_driver_unregister(&vprbrd_i2c_driver);
}
module_exit(vprbrd_i2c_exit);
MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
MODULE_DESCRIPTION("I2C master driver for Nano River Techs Viperboard");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:viperboard-i2c");
+14
View File
@@ -125,4 +125,18 @@ config TI_ADC081C
This driver can also be built as a module. If so, the module will be
called ti-adc081c.
config TI_AM335X_ADC
tristate "TI's ADC driver"
depends on MFD_TI_AM335X_TSCADC
help
Say yes here to build support for Texas Instruments ADC
driver which is also a MFD client.
config VIPERBOARD_ADC
tristate "Viperboard ADC support"
depends on MFD_VIPERBOARD && USB
help
Say yes here to access the ADC part of the Nano River
Technologies Viperboard.
endmenu
+2 -1
View File
@@ -13,4 +13,5 @@ obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
+260
View File
@@ -0,0 +1,260 @@
/*
* TI ADC MFD driver
*
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/iio/iio.h>
#include <linux/mfd/ti_am335x_tscadc.h>
#include <linux/platform_data/ti_am335x_adc.h>
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
int channels;
};
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
{
return readl(adc->mfd_tscadc->tscadc_base + reg);
}
static void tiadc_writel(struct tiadc_device *adc, unsigned int reg,
unsigned int val)
{
writel(val, adc->mfd_tscadc->tscadc_base + reg);
}
static void tiadc_step_config(struct tiadc_device *adc_dev)
{
unsigned int stepconfig;
int i, channels = 0, steps;
/*
* There are 16 configurable steps and 8 analog input
* lines available which are shared between Touchscreen and ADC.
*
* Steps backwards i.e. from 16 towards 0 are used by ADC
* depending on number of input lines needed.
* Channel would represent which analog input
* needs to be given to ADC to digitalize data.
*/
steps = TOTAL_STEPS - adc_dev->channels;
channels = TOTAL_CHANNELS - adc_dev->channels;
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
for (i = (steps + 1); i <= TOTAL_STEPS; i++) {
tiadc_writel(adc_dev, REG_STEPCONFIG(i),
stepconfig | STEPCONFIG_INP(channels));
tiadc_writel(adc_dev, REG_STEPDELAY(i),
STEPCONFIG_OPENDLY);
channels++;
}
tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB);
}
static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
{
struct iio_chan_spec *chan_array;
int i;
indio_dev->num_channels = channels;
chan_array = kcalloc(indio_dev->num_channels,
sizeof(struct iio_chan_spec), GFP_KERNEL);
if (chan_array == NULL)
return -ENOMEM;
for (i = 0; i < (indio_dev->num_channels); i++) {
struct iio_chan_spec *chan = chan_array + i;
chan->type = IIO_VOLTAGE;
chan->indexed = 1;
chan->channel = i;
chan->info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT;
}
indio_dev->channels = chan_array;
return indio_dev->num_channels;
}
static void tiadc_channels_remove(struct iio_dev *indio_dev)
{
kfree(indio_dev->channels);
}
static int tiadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
int i;
unsigned int fifo1count, readx1;
/*
* When the sub-system is first enabled,
* the sequencer will always start with the
* lowest step (1) and continue until step (16).
* For ex: If we have enabled 4 ADC channels and
* currently use only 1 out of them, the
* sequencer still configures all the 4 steps,
* leading to 3 unwanted data.
* Hence we need to flush out this data.
*/
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
for (i = 0; i < fifo1count; i++) {
readx1 = tiadc_readl(adc_dev, REG_FIFO1);
if (i == chan->channel)
*val = readx1 & 0xfff;
}
tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB);
return IIO_VAL_INT;
}
static const struct iio_info tiadc_info = {
.read_raw = &tiadc_read_raw,
};
static int __devinit tiadc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct tiadc_device *adc_dev;
struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
struct mfd_tscadc_board *pdata;
int err;
pdata = tscadc_dev->dev->platform_data;
if (!pdata || !pdata->adc_init) {
dev_err(&pdev->dev, "Could not find platform data\n");
return -EINVAL;
}
indio_dev = iio_device_alloc(sizeof(struct tiadc_device));
if (indio_dev == NULL) {
dev_err(&pdev->dev, "failed to allocate iio device\n");
err = -ENOMEM;
goto err_ret;
}
adc_dev = iio_priv(indio_dev);
adc_dev->mfd_tscadc = tscadc_dev;
adc_dev->channels = pdata->adc_init->adc_channels;
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &tiadc_info;
tiadc_step_config(adc_dev);
err = tiadc_channel_init(indio_dev, adc_dev->channels);
if (err < 0)
goto err_free_device;
err = iio_device_register(indio_dev);
if (err)
goto err_free_channels;
platform_set_drvdata(pdev, indio_dev);
return 0;
err_free_channels:
tiadc_channels_remove(indio_dev);
err_free_device:
iio_device_free(indio_dev);
err_ret:
return err;
}
static int __devexit tiadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
tiadc_channels_remove(indio_dev);
iio_device_free(indio_dev);
return 0;
}
#ifdef CONFIG_PM
static int tiadc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct tiadc_device *adc_dev = iio_priv(indio_dev);
struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
unsigned int idle;
if (!device_may_wakeup(tscadc_dev->dev)) {
idle = tiadc_readl(adc_dev, REG_CTRL);
idle &= ~(CNTRLREG_TSCSSENB);
tiadc_writel(adc_dev, REG_CTRL, (idle |
CNTRLREG_POWERDOWN));
}
return 0;
}
static int tiadc_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct tiadc_device *adc_dev = iio_priv(indio_dev);
unsigned int restore;
/* Make sure ADC is powered up */
restore = tiadc_readl(adc_dev, REG_CTRL);
restore &= ~(CNTRLREG_POWERDOWN);
tiadc_writel(adc_dev, REG_CTRL, restore);
tiadc_step_config(adc_dev);
return 0;
}
static const struct dev_pm_ops tiadc_pm_ops = {
.suspend = tiadc_suspend,
.resume = tiadc_resume,
};
#define TIADC_PM_OPS (&tiadc_pm_ops)
#else
#define TIADC_PM_OPS NULL
#endif
static struct platform_driver tiadc_driver = {
.driver = {
.name = "tiadc",
.owner = THIS_MODULE,
.pm = TIADC_PM_OPS,
},
.probe = tiadc_probe,
.remove = __devexit_p(tiadc_remove),
};
module_platform_driver(tiadc_driver);
MODULE_DESCRIPTION("TI ADC controller driver");
MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
MODULE_LICENSE("GPL");
+181
View File
@@ -0,0 +1,181 @@
/*
* Nano River Technologies viperboard IIO ADC driver
*
* (C) 2012 by Lemonage GmbH
* Author: Lars Poeschel <poeschel@lemonage.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/errno.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/usb.h>
#include <linux/iio/iio.h>
#include <linux/mfd/viperboard.h>
#define VPRBRD_ADC_CMD_GET 0x00
struct vprbrd_adc_msg {
u8 cmd;
u8 chan;
u8 val;
} __packed;
struct vprbrd_adc {
struct vprbrd *vb;
};
#define VPRBRD_ADC_CHANNEL(_index) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _index, \
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \
.scan_index = _index, \
.scan_type = { \
.sign = 'u', \
.realbits = 8, \
.storagebits = 8, \
}, \
}
static struct iio_chan_spec const vprbrd_adc_iio_channels[] = {
VPRBRD_ADC_CHANNEL(0),
VPRBRD_ADC_CHANNEL(1),
VPRBRD_ADC_CHANNEL(2),
VPRBRD_ADC_CHANNEL(3),
};
static int vprbrd_iio_read_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long info)
{
int ret, error = 0;
struct vprbrd_adc *adc = iio_priv(iio_dev);
struct vprbrd *vb = adc->vb;
struct vprbrd_adc_msg *admsg = (struct vprbrd_adc_msg *)vb->buf;
switch (info) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&vb->lock);
admsg->cmd = VPRBRD_ADC_CMD_GET;
admsg->chan = chan->scan_index;
admsg->val = 0x00;
ret = usb_control_msg(vb->usb_dev,
usb_sndctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg,
sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
if (ret != sizeof(struct vprbrd_adc_msg)) {
dev_err(&iio_dev->dev, "usb send error on adc read\n");
error = -EREMOTEIO;
}
ret = usb_control_msg(vb->usb_dev,
usb_rcvctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, admsg,
sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
*val = admsg->val;
mutex_unlock(&vb->lock);
if (ret != sizeof(struct vprbrd_adc_msg)) {
dev_err(&iio_dev->dev, "usb recv error on adc read\n");
error = -EREMOTEIO;
}
if (error)
goto error;
return IIO_VAL_INT;
default:
error = -EINVAL;
break;
}
error:
return error;
}
static const struct iio_info vprbrd_adc_iio_info = {
.read_raw = &vprbrd_iio_read_raw,
.driver_module = THIS_MODULE,
};
static int __devinit vprbrd_adc_probe(struct platform_device *pdev)
{
struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
struct vprbrd_adc *adc;
struct iio_dev *indio_dev;
int ret;
/* registering iio */
indio_dev = iio_device_alloc(sizeof(*adc));
if (!indio_dev) {
dev_err(&pdev->dev, "failed allocating iio device\n");
return -ENOMEM;
}
adc = iio_priv(indio_dev);
adc->vb = vb;
indio_dev->name = "viperboard adc";
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &vprbrd_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = vprbrd_adc_iio_channels;
indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels);
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&pdev->dev, "could not register iio (adc)");
goto error;
}
platform_set_drvdata(pdev, indio_dev);
return 0;
error:
iio_device_free(indio_dev);
return ret;
}
static int __devexit vprbrd_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
iio_device_free(indio_dev);
return 0;
}
static struct platform_driver vprbrd_adc_driver = {
.driver = {
.name = "viperboard-adc",
.owner = THIS_MODULE,
},
.probe = vprbrd_adc_probe,
.remove = __devexit_p(vprbrd_adc_remove),
};
module_platform_driver(vprbrd_adc_driver);
MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
MODULE_DESCRIPTION("IIO ADC driver for Nano River Techs Viperboard");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:viperboard-adc");
+5 -17
View File
@@ -24,7 +24,6 @@ struct da9052_onkey {
struct da9052 *da9052;
struct input_dev *input;
struct delayed_work work;
unsigned int irq;
};
static void da9052_onkey_query(struct da9052_onkey *onkey)
@@ -76,7 +75,6 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev)
struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
struct da9052_onkey *onkey;
struct input_dev *input_dev;
int irq;
int error;
if (!da9052) {
@@ -84,13 +82,6 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev)
return -EINVAL;
}
irq = platform_get_irq_byname(pdev, "ONKEY");
if (irq < 0) {
dev_err(&pdev->dev,
"Failed to get an IRQ for input device, %d\n", irq);
return -EINVAL;
}
onkey = kzalloc(sizeof(*onkey), GFP_KERNEL);
input_dev = input_allocate_device();
if (!onkey || !input_dev) {
@@ -101,7 +92,6 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev)
onkey->input = input_dev;
onkey->da9052 = da9052;
onkey->irq = irq;
INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work);
input_dev->name = "da9052-onkey";
@@ -111,13 +101,11 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev)
input_dev->evbit[0] = BIT_MASK(EV_KEY);
__set_bit(KEY_POWER, input_dev->keybit);
error = request_threaded_irq(onkey->irq, NULL, da9052_onkey_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"ONKEY", onkey);
error = da9052_request_irq(onkey->da9052, DA9052_IRQ_NONKEY, "ONKEY",
da9052_onkey_irq, onkey);
if (error < 0) {
dev_err(onkey->da9052->dev,
"Failed to register ONKEY IRQ %d, error = %d\n",
onkey->irq, error);
"Failed to register ONKEY IRQ: %d\n", error);
goto err_free_mem;
}
@@ -132,7 +120,7 @@ static int __devinit da9052_onkey_probe(struct platform_device *pdev)
return 0;
err_free_irq:
free_irq(onkey->irq, onkey);
da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey);
cancel_delayed_work_sync(&onkey->work);
err_free_mem:
input_free_device(input_dev);
@@ -145,7 +133,7 @@ static int __devexit da9052_onkey_remove(struct platform_device *pdev)
{
struct da9052_onkey *onkey = platform_get_drvdata(pdev);
free_irq(onkey->irq, onkey);
da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey);
cancel_delayed_work_sync(&onkey->work);
input_unregister_device(onkey->input);
+3 -3
View File
@@ -529,9 +529,9 @@ config TOUCHSCREEN_TOUCHWIN
To compile this driver as a module, choose M here: the
module will be called touchwin.
config TOUCHSCREEN_TI_TSCADC
config TOUCHSCREEN_TI_AM335X_TSC
tristate "TI Touchscreen Interface"
depends on ARCH_OMAP2PLUS
depends on MFD_TI_AM335X_TSCADC
help
Say Y here if you have 4/5/8 wire touchscreen controller
to be connected to the ADC controller on your TI AM335x SoC.
@@ -539,7 +539,7 @@ config TOUCHSCREEN_TI_TSCADC
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called ti_tscadc.
module will be called ti_am335x_tsc.
config TOUCHSCREEN_ATMEL_TSADCC
tristate "Atmel Touchscreen Interface"
+1 -1
View File
@@ -52,7 +52,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o
obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
+20 -39
View File
@@ -27,8 +27,6 @@ struct da9052_tsi {
struct input_dev *dev;
struct delayed_work ts_pen_work;
struct mutex mutex;
unsigned int irq_pendwn;
unsigned int irq_datardy;
bool stopped;
bool adc_on;
};
@@ -45,8 +43,8 @@ static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data)
if (!tsi->stopped) {
/* Mask PEN_DOWN event and unmask TSI_READY event */
disable_irq_nosync(tsi->irq_pendwn);
enable_irq(tsi->irq_datardy);
da9052_disable_irq_nosync(tsi->da9052, DA9052_IRQ_PENDOWN);
da9052_enable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
da9052_ts_adc_toggle(tsi, true);
@@ -137,8 +135,8 @@ static void da9052_ts_pen_work(struct work_struct *work)
return;
/* Mask TSI_READY event and unmask PEN_DOWN event */
disable_irq(tsi->irq_datardy);
enable_irq(tsi->irq_pendwn);
da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
}
}
}
@@ -197,7 +195,7 @@ static int da9052_ts_input_open(struct input_dev *input_dev)
mb();
/* Unmask PEN_DOWN event */
enable_irq(tsi->irq_pendwn);
da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
/* Enable Pen Detect Circuit */
return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG,
@@ -210,11 +208,11 @@ static void da9052_ts_input_close(struct input_dev *input_dev)
tsi->stopped = true;
mb();
disable_irq(tsi->irq_pendwn);
da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
cancel_delayed_work_sync(&tsi->ts_pen_work);
if (tsi->adc_on) {
disable_irq(tsi->irq_datardy);
da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
da9052_ts_adc_toggle(tsi, false);
/*
@@ -222,7 +220,7 @@ static void da9052_ts_input_close(struct input_dev *input_dev)
* twice and we need to enable it to keep enable/disable
* counter balanced. IRQ is still off though.
*/
enable_irq(tsi->irq_pendwn);
da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
}
/* Disable Pen Detect Circuit */
@@ -234,21 +232,12 @@ static int __devinit da9052_ts_probe(struct platform_device *pdev)
struct da9052 *da9052;
struct da9052_tsi *tsi;
struct input_dev *input_dev;
int irq_pendwn;
int irq_datardy;
int error;
da9052 = dev_get_drvdata(pdev->dev.parent);
if (!da9052)
return -EINVAL;
irq_pendwn = platform_get_irq_byname(pdev, "PENDWN");
irq_datardy = platform_get_irq_byname(pdev, "TSIRDY");
if (irq_pendwn < 0 || irq_datardy < 0) {
dev_err(da9052->dev, "Unable to determine device interrupts\n");
return -ENXIO;
}
tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL);
input_dev = input_allocate_device();
if (!tsi || !input_dev) {
@@ -258,8 +247,6 @@ static int __devinit da9052_ts_probe(struct platform_device *pdev)
tsi->da9052 = da9052;
tsi->dev = input_dev;
tsi->irq_pendwn = da9052->irq_base + irq_pendwn;
tsi->irq_datardy = da9052->irq_base + irq_datardy;
tsi->stopped = true;
INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work);
@@ -287,31 +274,25 @@ static int __devinit da9052_ts_probe(struct platform_device *pdev)
/* Disable ADC */
da9052_ts_adc_toggle(tsi, false);
error = request_threaded_irq(tsi->irq_pendwn,
NULL, da9052_ts_pendwn_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"PENDWN", tsi);
error = da9052_request_irq(tsi->da9052, DA9052_IRQ_PENDOWN,
"pendown-irq", da9052_ts_pendwn_irq, tsi);
if (error) {
dev_err(tsi->da9052->dev,
"Failed to register PENDWN IRQ %d, error = %d\n",
tsi->irq_pendwn, error);
"Failed to register PENDWN IRQ: %d\n", error);
goto err_free_mem;
}
error = request_threaded_irq(tsi->irq_datardy,
NULL, da9052_ts_datardy_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"TSIRDY", tsi);
error = da9052_request_irq(tsi->da9052, DA9052_IRQ_TSIREADY,
"tsiready-irq", da9052_ts_datardy_irq, tsi);
if (error) {
dev_err(tsi->da9052->dev,
"Failed to register TSIRDY IRQ %d, error = %d\n",
tsi->irq_datardy, error);
"Failed to register TSIRDY IRQ :%d\n", error);
goto err_free_pendwn_irq;
}
/* Mask PEN_DOWN and TSI_READY events */
disable_irq(tsi->irq_pendwn);
disable_irq(tsi->irq_datardy);
da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
error = da9052_configure_tsi(tsi);
if (error)
@@ -326,9 +307,9 @@ static int __devinit da9052_ts_probe(struct platform_device *pdev)
return 0;
err_free_datardy_irq:
free_irq(tsi->irq_datardy, tsi);
da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
err_free_pendwn_irq:
free_irq(tsi->irq_pendwn, tsi);
da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
err_free_mem:
kfree(tsi);
input_free_device(input_dev);
@@ -342,8 +323,8 @@ static int __devexit da9052_ts_remove(struct platform_device *pdev)
da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19);
free_irq(tsi->irq_pendwn, tsi);
free_irq(tsi->irq_datardy, tsi);
da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
input_unregister_device(tsi->dev);
kfree(tsi);
+398
View File
@@ -0,0 +1,398 @@
/*
* TI Touch Screen driver
*
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/input/ti_am335x_tsc.h>
#include <linux/delay.h>
#include <linux/mfd/ti_am335x_tscadc.h>
#define ADCFSM_STEPID 0x10
#define SEQ_SETTLE 275
#define MAX_12BIT ((1 << 12) - 1)
struct titsc {
struct input_dev *input;
struct ti_tscadc_dev *mfd_tscadc;
unsigned int irq;
unsigned int wires;
unsigned int x_plate_resistance;
bool pen_down;
int steps_to_configure;
};
static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
{
return readl(ts->mfd_tscadc->tscadc_base + reg);
}
static void titsc_writel(struct titsc *tsc, unsigned int reg,
unsigned int val)
{
writel(val, tsc->mfd_tscadc->tscadc_base + reg);
}
static void titsc_step_config(struct titsc *ts_dev)
{
unsigned int config;
int i, total_steps;
/* Configure the Step registers */
total_steps = 2 * ts_dev->steps_to_configure;
config = STEPCONFIG_MODE_HWSYNC |
STEPCONFIG_AVG_16 | STEPCONFIG_XPP;
switch (ts_dev->wires) {
case 4:
config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN;
break;
case 5:
config |= STEPCONFIG_YNN |
STEPCONFIG_INP_AN4 | STEPCONFIG_XNN |
STEPCONFIG_YPP;
break;
case 8:
config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN;
break;
}
for (i = 1; i <= ts_dev->steps_to_configure; i++) {
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
}
config = 0;
config = STEPCONFIG_MODE_HWSYNC |
STEPCONFIG_AVG_16 | STEPCONFIG_YNN |
STEPCONFIG_INM_ADCREFM | STEPCONFIG_FIFO1;
switch (ts_dev->wires) {
case 4:
config |= STEPCONFIG_YPP;
break;
case 5:
config |= STEPCONFIG_XPP | STEPCONFIG_INP_AN4 |
STEPCONFIG_XNP | STEPCONFIG_YPN;
break;
case 8:
config |= STEPCONFIG_YPP;
break;
}
for (i = (ts_dev->steps_to_configure + 1); i <= total_steps; i++) {
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
}
config = 0;
/* Charge step configuration */
config = STEPCONFIG_XPP | STEPCONFIG_YNN |
STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR |
STEPCHARGE_INM_AN1 | STEPCHARGE_INP_AN1;
titsc_writel(ts_dev, REG_CHARGECONFIG, config);
titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY);
config = 0;
/* Configure to calculate pressure */
config = STEPCONFIG_MODE_HWSYNC |
STEPCONFIG_AVG_16 | STEPCONFIG_YPP |
STEPCONFIG_XNN | STEPCONFIG_INM_ADCREFM;
titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 1), config);
titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 1),
STEPCONFIG_OPENDLY);
config |= STEPCONFIG_INP_AN3 | STEPCONFIG_FIFO1;
titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 2), config);
titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 2),
STEPCONFIG_OPENDLY);
titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC);
}
static void titsc_read_coordinates(struct titsc *ts_dev,
unsigned int *x, unsigned int *y)
{
unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT);
unsigned int prev_val_x = ~0, prev_val_y = ~0;
unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
unsigned int read, diff;
unsigned int i, channel;
/*
* Delta filter is used to remove large variations in sampled
* values from ADC. The filter tries to predict where the next
* coordinate could be. This is done by taking a previous
* coordinate and subtracting it form current one. Further the
* algorithm compares the difference with that of a present value,
* if true the value is reported to the sub system.
*/
for (i = 0; i < fifocount - 1; i++) {
read = titsc_readl(ts_dev, REG_FIFO0);
channel = read & 0xf0000;
channel = channel >> 0x10;
if ((channel >= 0) && (channel < ts_dev->steps_to_configure)) {
read &= 0xfff;
diff = abs(read - prev_val_x);
if (diff < prev_diff_x) {
prev_diff_x = diff;
*x = read;
}
prev_val_x = read;
}
read = titsc_readl(ts_dev, REG_FIFO1);
channel = read & 0xf0000;
channel = channel >> 0x10;
if ((channel >= ts_dev->steps_to_configure) &&
(channel < (2 * ts_dev->steps_to_configure - 1))) {
read &= 0xfff;
diff = abs(read - prev_val_y);
if (diff < prev_diff_y) {
prev_diff_y = diff;
*y = read;
}
prev_val_y = read;
}
}
}
static irqreturn_t titsc_irq(int irq, void *dev)
{
struct titsc *ts_dev = dev;
struct input_dev *input_dev = ts_dev->input;
unsigned int status, irqclr = 0;
unsigned int x = 0, y = 0;
unsigned int z1, z2, z;
unsigned int fsm;
unsigned int fifo1count, fifo0count;
int i;
status = titsc_readl(ts_dev, REG_IRQSTATUS);
if (status & IRQENB_FIFO0THRES) {
titsc_read_coordinates(ts_dev, &x, &y);
z1 = titsc_readl(ts_dev, REG_FIFO0) & 0xfff;
z2 = titsc_readl(ts_dev, REG_FIFO1) & 0xfff;
fifo1count = titsc_readl(ts_dev, REG_FIFO1CNT);
for (i = 0; i < fifo1count; i++)
titsc_readl(ts_dev, REG_FIFO1);
fifo0count = titsc_readl(ts_dev, REG_FIFO0CNT);
for (i = 0; i < fifo0count; i++)
titsc_readl(ts_dev, REG_FIFO0);
if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
/*
* Calculate pressure using formula
* Resistance(touch) = x plate resistance *
* x postion/4096 * ((z2 / z1) - 1)
*/
z = z2 - z1;
z *= x;
z *= ts_dev->x_plate_resistance;
z /= z1;
z = (z + 2047) >> 12;
if (z <= MAX_12BIT) {
input_report_abs(input_dev, ABS_X, x);
input_report_abs(input_dev, ABS_Y, y);
input_report_abs(input_dev, ABS_PRESSURE, z);
input_report_key(input_dev, BTN_TOUCH, 1);
input_sync(input_dev);
}
}
irqclr |= IRQENB_FIFO0THRES;
}
/*
* Time for sequencer to settle, to read
* correct state of the sequencer.
*/
udelay(SEQ_SETTLE);
status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
if (status & IRQENB_PENUP) {
/* Pen up event */
fsm = titsc_readl(ts_dev, REG_ADCFSM);
if (fsm == ADCFSM_STEPID) {
ts_dev->pen_down = false;
input_report_key(input_dev, BTN_TOUCH, 0);
input_report_abs(input_dev, ABS_PRESSURE, 0);
input_sync(input_dev);
} else {
ts_dev->pen_down = true;
}
irqclr |= IRQENB_PENUP;
}
titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC);
return IRQ_HANDLED;
}
/*
* The functions for inserting/removing driver as a module.
*/
static int __devinit titsc_probe(struct platform_device *pdev)
{
struct titsc *ts_dev;
struct input_dev *input_dev;
struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
struct mfd_tscadc_board *pdata;
int err;
pdata = tscadc_dev->dev->platform_data;
if (!pdata) {
dev_err(&pdev->dev, "Could not find platform data\n");
return -EINVAL;
}
/* Allocate memory for device */
ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL);
input_dev = input_allocate_device();
if (!ts_dev || !input_dev) {
dev_err(&pdev->dev, "failed to allocate memory.\n");
err = -ENOMEM;
goto err_free_mem;
}
tscadc_dev->tsc = ts_dev;
ts_dev->mfd_tscadc = tscadc_dev;
ts_dev->input = input_dev;
ts_dev->irq = tscadc_dev->irq;
ts_dev->wires = pdata->tsc_init->wires;
ts_dev->x_plate_resistance = pdata->tsc_init->x_plate_resistance;
ts_dev->steps_to_configure = pdata->tsc_init->steps_to_configure;
err = request_irq(ts_dev->irq, titsc_irq,
0, pdev->dev.driver->name, ts_dev);
if (err) {
dev_err(&pdev->dev, "failed to allocate irq.\n");
goto err_free_mem;
}
titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
titsc_step_config(ts_dev);
titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure);
input_dev->name = "ti-tsc";
input_dev->dev.parent = &pdev->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
/* register to the input system */
err = input_register_device(input_dev);
if (err)
goto err_free_irq;
platform_set_drvdata(pdev, ts_dev);
return 0;
err_free_irq:
free_irq(ts_dev->irq, ts_dev);
err_free_mem:
input_free_device(input_dev);
kfree(ts_dev);
return err;
}
static int __devexit titsc_remove(struct platform_device *pdev)
{
struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
struct titsc *ts_dev = tscadc_dev->tsc;
free_irq(ts_dev->irq, ts_dev);
input_unregister_device(ts_dev->input);
platform_set_drvdata(pdev, NULL);
kfree(ts_dev);
return 0;
}
#ifdef CONFIG_PM
static int titsc_suspend(struct device *dev)
{
struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
struct titsc *ts_dev = tscadc_dev->tsc;
unsigned int idle;
if (device_may_wakeup(tscadc_dev->dev)) {
idle = titsc_readl(ts_dev, REG_IRQENABLE);
titsc_writel(ts_dev, REG_IRQENABLE,
(idle | IRQENB_HW_PEN));
titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB);
}
return 0;
}
static int titsc_resume(struct device *dev)
{
struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
struct titsc *ts_dev = tscadc_dev->tsc;
if (device_may_wakeup(tscadc_dev->dev)) {
titsc_writel(ts_dev, REG_IRQWAKEUP,
0x00);
titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
}
titsc_step_config(ts_dev);
titsc_writel(ts_dev, REG_FIFO0THR,
ts_dev->steps_to_configure);
return 0;
}
static const struct dev_pm_ops titsc_pm_ops = {
.suspend = titsc_suspend,
.resume = titsc_resume,
};
#define TITSC_PM_OPS (&titsc_pm_ops)
#else
#define TITSC_PM_OPS NULL
#endif
static struct platform_driver ti_tsc_driver = {
.probe = titsc_probe,
.remove = __devexit_p(titsc_remove),
.driver = {
.name = "tsc",
.owner = THIS_MODULE,
.pm = TITSC_PM_OPS,
},
};
module_platform_driver(ti_tsc_driver);
MODULE_DESCRIPTION("TI touchscreen controller driver");
MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
MODULE_LICENSE("GPL");

Some files were not shown because too many files have changed in this diff Show More