提交 2dfea380 编写于 作者: L Linus Torvalds

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
  ...
* 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 {
......
......@@ -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
......@@ -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
......
......@@ -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 = {
......
......@@ -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;
......
......@@ -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);
......
/*
* Nano River Technologies viperboard GPIO lib 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/gpio.h>
#include <linux/mfd/viperboard.h>
#define VPRBRD_GPIOA_CLK_1MHZ 0
#define VPRBRD_GPIOA_CLK_100KHZ 1
#define VPRBRD_GPIOA_CLK_10KHZ 2
#define VPRBRD_GPIOA_CLK_1KHZ 3
#define VPRBRD_GPIOA_CLK_100HZ 4
#define VPRBRD_GPIOA_CLK_10HZ 5
#define VPRBRD_GPIOA_FREQ_DEFAULT 1000
#define VPRBRD_GPIOA_CMD_CONT 0x00
#define VPRBRD_GPIOA_CMD_PULSE 0x01
#define VPRBRD_GPIOA_CMD_PWM 0x02
#define VPRBRD_GPIOA_CMD_SETOUT 0x03
#define VPRBRD_GPIOA_CMD_SETIN 0x04
#define VPRBRD_GPIOA_CMD_SETINT 0x05
#define VPRBRD_GPIOA_CMD_GETIN 0x06
#define VPRBRD_GPIOB_CMD_SETDIR 0x00
#define VPRBRD_GPIOB_CMD_SETVAL 0x01
struct vprbrd_gpioa_msg {
u8 cmd;
u8 clk;
u8 offset;
u8 t1;
u8 t2;
u8 invert;
u8 pwmlevel;
u8 outval;
u8 risefall;
u8 answer;
u8 __fill;
} __packed;
struct vprbrd_gpiob_msg {
u8 cmd;
u16 val;
u16 mask;
} __packed;
struct vprbrd_gpio {
struct gpio_chip gpioa; /* gpio a related things */
u32 gpioa_out;
u32 gpioa_val;
struct gpio_chip gpiob; /* gpio b related things */
u32 gpiob_out;
u32 gpiob_val;
struct vprbrd *vb;
};
/* gpioa sampling clock module parameter */
static unsigned char gpioa_clk;
static unsigned int gpioa_freq = VPRBRD_GPIOA_FREQ_DEFAULT;
module_param(gpioa_freq, uint, 0);
MODULE_PARM_DESC(gpioa_freq,
"gpio-a sampling freq in Hz (default is 1000Hz) valid values: 10, 100, 1000, 10000, 100000, 1000000");
/* ----- begin of gipo a chip -------------------------------------------- */
static int vprbrd_gpioa_get(struct gpio_chip *chip,
unsigned offset)
{
int ret, answer, error = 0;
struct vprbrd_gpio *gpio =
container_of(chip, struct vprbrd_gpio, gpioa);
struct vprbrd *vb = gpio->vb;
struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
/* if io is set to output, just return the saved value */
if (gpio->gpioa_out & (1 << offset))
return gpio->gpioa_val & (1 << offset);
mutex_lock(&vb->lock);
gamsg->cmd = VPRBRD_GPIOA_CMD_GETIN;
gamsg->clk = 0x00;
gamsg->offset = offset;
gamsg->t1 = 0x00;
gamsg->t2 = 0x00;
gamsg->invert = 0x00;
gamsg->pwmlevel = 0x00;
gamsg->outval = 0x00;
gamsg->risefall = 0x00;
gamsg->answer = 0x00;
gamsg->__fill = 0x00;
ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
VPRBRD_USB_TIMEOUT_MS);
if (ret != sizeof(struct vprbrd_gpioa_msg))
error = -EREMOTEIO;
ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_IN, 0x0000,
0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
VPRBRD_USB_TIMEOUT_MS);
answer = gamsg->answer & 0x01;
mutex_unlock(&vb->lock);
if (ret != sizeof(struct vprbrd_gpioa_msg))
error = -EREMOTEIO;
if (error)
return error;
return answer;
}
static void vprbrd_gpioa_set(struct gpio_chip *chip,
unsigned offset, int value)
{
int ret;
struct vprbrd_gpio *gpio =
container_of(chip, struct vprbrd_gpio, gpioa);
struct vprbrd *vb = gpio->vb;
struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
if (gpio->gpioa_out & (1 << offset)) {
if (value)
gpio->gpioa_val |= (1 << offset);
else
gpio->gpioa_val &= ~(1 << offset);
mutex_lock(&vb->lock);
gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT;
gamsg->clk = 0x00;
gamsg->offset = offset;
gamsg->t1 = 0x00;
gamsg->t2 = 0x00;
gamsg->invert = 0x00;
gamsg->pwmlevel = 0x00;
gamsg->outval = value;
gamsg->risefall = 0x00;
gamsg->answer = 0x00;
gamsg->__fill = 0x00;
ret = usb_control_msg(vb->usb_dev,
usb_sndctrlpipe(vb->usb_dev, 0),
VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT,
0x0000, 0x0000, gamsg,
sizeof(struct vprbrd_gpioa_msg), VPRBRD_USB_TIMEOUT_MS);
mutex_unlock(&vb->lock);
if (ret != sizeof(struct vprbrd_gpioa_msg))
dev_err(chip->dev, "usb error setting pin value\n");
}
}
static int vprbrd_gpioa_direction_input(struct gpio_chip *chip,
unsigned offset)
{
int ret;
struct vprbrd_gpio *gpio =
container_of(chip, struct vprbrd_gpio, gpioa);
struct vprbrd *vb = gpio->vb;
struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
gpio->gpioa_out &= ~(1 << offset);
mutex_lock(&vb->lock);
gamsg->cmd = VPRBRD_GPIOA_CMD_SETIN;
gamsg->clk = gpioa_clk;
gamsg->offset = offset;
gamsg->t1 = 0x00;
gamsg->t2 = 0x00;
gamsg->invert = 0x00;
gamsg->pwmlevel = 0x00;
gamsg->outval = 0x00;
gamsg->risefall = 0x00;
gamsg->answer = 0x00;
gamsg->__fill = 0x00;
ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
VPRBRD_USB_TIMEOUT_MS);
mutex_unlock(&vb->lock);
if (ret != sizeof(struct vprbrd_gpioa_msg))
return -EREMOTEIO;
return 0;
}
static int vprbrd_gpioa_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
int ret;
struct vprbrd_gpio *gpio =
container_of(chip, struct vprbrd_gpio, gpioa);
struct vprbrd *vb = gpio->vb;
struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
gpio->gpioa_out |= (1 << offset);
if (value)
gpio->gpioa_val |= (1 << offset);
else
gpio->gpioa_val &= ~(1 << offset);
mutex_lock(&vb->lock);
gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT;
gamsg->clk = 0x00;
gamsg->offset = offset;
gamsg->t1 = 0x00;
gamsg->t2 = 0x00;
gamsg->invert = 0x00;
gamsg->pwmlevel = 0x00;
gamsg->outval = value;
gamsg->risefall = 0x00;
gamsg->answer = 0x00;
gamsg->__fill = 0x00;
ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
VPRBRD_USB_TIMEOUT_MS);
mutex_unlock(&vb->lock);
if (ret != sizeof(struct vprbrd_gpioa_msg))
return -EREMOTEIO;
return 0;
}
/* ----- end of gpio a chip ---------------------------------------------- */
/* ----- begin of gipo b chip -------------------------------------------- */
static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned offset,
unsigned dir)
{
struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
int ret;
gbmsg->cmd = VPRBRD_GPIOB_CMD_SETDIR;
gbmsg->val = cpu_to_be16(dir << offset);
gbmsg->mask = cpu_to_be16(0x0001 << offset);
ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, 0x0000,
0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg),
VPRBRD_USB_TIMEOUT_MS);
if (ret != sizeof(struct vprbrd_gpiob_msg))
return -EREMOTEIO;
return 0;
}
static int vprbrd_gpiob_get(struct gpio_chip *chip,
unsigned offset)
{
int ret;
u16 val;
struct vprbrd_gpio *gpio =
container_of(chip, struct vprbrd_gpio, gpiob);
struct vprbrd *vb = gpio->vb;
struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
/* if io is set to output, just return the saved value */
if (gpio->gpiob_out & (1 << offset))
return gpio->gpiob_val & (1 << offset);
mutex_lock(&vb->lock);
ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_IN, 0x0000,
0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg),
VPRBRD_USB_TIMEOUT_MS);
val = gbmsg->val;
mutex_unlock(&vb->lock);
if (ret != sizeof(struct vprbrd_gpiob_msg))
return ret;
/* cache the read values */
gpio->gpiob_val = be16_to_cpu(val);
return (gpio->gpiob_val >> offset) & 0x1;
}
static void vprbrd_gpiob_set(struct gpio_chip *chip,
unsigned offset, int value)
{
int ret;
struct vprbrd_gpio *gpio =
container_of(chip, struct vprbrd_gpio, gpiob);
struct vprbrd *vb = gpio->vb;
struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
if (gpio->gpiob_out & (1 << offset)) {
if (value)
gpio->gpiob_val |= (1 << offset);
else
gpio->gpiob_val &= ~(1 << offset);
mutex_lock(&vb->lock);
gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL;
gbmsg->val = cpu_to_be16(value << offset);
gbmsg->mask = cpu_to_be16(0x0001 << offset);
ret = usb_control_msg(vb->usb_dev,
usb_sndctrlpipe(vb->usb_dev, 0),
VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT,
0x0000, 0x0000, gbmsg,
sizeof(struct vprbrd_gpiob_msg), VPRBRD_USB_TIMEOUT_MS);
mutex_unlock(&vb->lock);
if (ret != sizeof(struct vprbrd_gpiob_msg))
dev_err(chip->dev, "usb error setting pin value\n");
}
}
static int vprbrd_gpiob_direction_input(struct gpio_chip *chip,
unsigned offset)
{
int ret;
struct vprbrd_gpio *gpio =
container_of(chip, struct vprbrd_gpio, gpiob);
struct vprbrd *vb = gpio->vb;
gpio->gpiob_out &= ~(1 << offset);
mutex_lock(&vb->lock);
ret = vprbrd_gpiob_setdir(vb, offset, 0);
mutex_unlock(&vb->lock);
if (ret)
dev_err(chip->dev, "usb error setting pin to input\n");
return ret;
}
static int vprbrd_gpiob_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
int ret;
struct vprbrd_gpio *gpio =
container_of(chip, struct vprbrd_gpio, gpiob);
struct vprbrd *vb = gpio->vb;
gpio->gpiob_out |= (1 << offset);
if (value)
gpio->gpiob_val |= (1 << offset);
else
gpio->gpiob_val &= ~(1 << offset);
mutex_lock(&vb->lock);
ret = vprbrd_gpiob_setdir(vb, offset, 1);
if (ret)
dev_err(chip->dev, "usb error setting pin to output\n");
mutex_unlock(&vb->lock);
vprbrd_gpiob_set(chip, offset, value);
return ret;
}
/* ----- end of gpio b chip ---------------------------------------------- */
static int __devinit vprbrd_gpio_probe(struct platform_device *pdev)
{
struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
struct vprbrd_gpio *vb_gpio;
int ret;
vb_gpio = devm_kzalloc(&pdev->dev, sizeof(*vb_gpio), GFP_KERNEL);
if (vb_gpio == NULL)
return -ENOMEM;
vb_gpio->vb = vb;
/* registering gpio a */
vb_gpio->gpioa.label = "viperboard gpio a";
vb_gpio->gpioa.dev = &pdev->dev;
vb_gpio->gpioa.owner = THIS_MODULE;
vb_gpio->gpioa.base = -1;
vb_gpio->gpioa.ngpio = 16;
vb_gpio->gpioa.can_sleep = 1;
vb_gpio->gpioa.set = vprbrd_gpioa_set;
vb_gpio->gpioa.get = vprbrd_gpioa_get;
vb_gpio->gpioa.direction_input = vprbrd_gpioa_direction_input;
vb_gpio->gpioa.direction_output = vprbrd_gpioa_direction_output;
ret = gpiochip_add(&vb_gpio->gpioa);
if (ret < 0) {
dev_err(vb_gpio->gpioa.dev, "could not add gpio a");
goto err_gpioa;
}
/* registering gpio b */
vb_gpio->gpiob.label = "viperboard gpio b";
vb_gpio->gpiob.dev = &pdev->dev;
vb_gpio->gpiob.owner = THIS_MODULE;
vb_gpio->gpiob.base = -1;
vb_gpio->gpiob.ngpio = 16;
vb_gpio->gpiob.can_sleep = 1;
vb_gpio->gpiob.set = vprbrd_gpiob_set;
vb_gpio->gpiob.get = vprbrd_gpiob_get;
vb_gpio->gpiob.direction_input = vprbrd_gpiob_direction_input;
vb_gpio->gpiob.direction_output = vprbrd_gpiob_direction_output;
ret = gpiochip_add(&vb_gpio->gpiob);
if (ret < 0) {
dev_err(vb_gpio->gpiob.dev, "could not add gpio b");
goto err_gpiob;
}
platform_set_drvdata(pdev, vb_gpio);
return ret;
err_gpiob:
ret = gpiochip_remove(&vb_gpio->gpioa);
err_gpioa:
return ret;
}
static int __devexit vprbrd_gpio_remove(struct platform_device *pdev)
{
struct vprbrd_gpio *vb_gpio = platform_get_drvdata(pdev);
int ret;
ret = gpiochip_remove(&vb_gpio->gpiob);
if (ret == 0)
ret = gpiochip_remove(&vb_gpio->gpioa);
return ret;
}
static struct platform_driver vprbrd_gpio_driver = {
.driver.name = "viperboard-gpio",
.driver.owner = THIS_MODULE,
.probe = vprbrd_gpio_probe,
.remove = __devexit_p(vprbrd_gpio_remove),
};
static int __init vprbrd_gpio_init(void)
{
switch (gpioa_freq) {
case 1000000:
gpioa_clk = VPRBRD_GPIOA_CLK_1MHZ;
break;
case 100000:
gpioa_clk = VPRBRD_GPIOA_CLK_100KHZ;
break;
case 10000:
gpioa_clk = VPRBRD_GPIOA_CLK_10KHZ;
break;
case 1000:
gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ;
break;
case 100:
gpioa_clk = VPRBRD_GPIOA_CLK_100HZ;
break;
case 10:
gpioa_clk = VPRBRD_GPIOA_CLK_10HZ;
break;
default:
pr_warn("invalid gpioa_freq (%d)\n", gpioa_freq);
gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ;
}
return platform_driver_register(&vprbrd_gpio_driver);
}
subsys_initcall(vprbrd_gpio_init);
static void __exit vprbrd_gpio_exit(void)
{
platform_driver_unregister(&vprbrd_gpio_driver);
}
module_exit(vprbrd_gpio_exit);
MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
MODULE_DESCRIPTION("GPIO driver for Nano River Techs Viperboard");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:viperboard-gpio");
......@@ -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
......
......@@ -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
......
/*
* 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");
......@@ -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
......@@ -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
/*
* 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");
/*
* 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");
......@@ -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);
......
......@@ -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"
......
......@@ -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
......
......@@ -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);
......
......@@ -24,131 +24,75 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/input/ti_tscadc.h>
#include <linux/input/ti_am335x_tsc.h>
#include <linux/delay.h>
#define REG_IRQEOI 0x020
#define REG_RAWIRQSTATUS 0x024
#define REG_IRQSTATUS 0x028
#define REG_IRQENABLE 0x02C
#define REG_IRQWAKEUP 0x034
#define REG_CTRL 0x040
#define REG_ADCFSM 0x044
#define REG_CLKDIV 0x04C
#define REG_SE 0x054
#define REG_IDLECONFIG 0x058
#define REG_CHARGECONFIG 0x05C
#define REG_CHARGEDELAY 0x060
#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8))
#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8))
#define REG_STEPCONFIG13 0x0C4
#define REG_STEPDELAY13 0x0C8
#define REG_STEPCONFIG14 0x0CC
#define REG_STEPDELAY14 0x0D0
#define REG_FIFO0CNT 0xE4
#define REG_FIFO1THR 0xF4
#define REG_FIFO0 0x100
#define REG_FIFO1 0x200
/* Register Bitfields */
#define IRQWKUP_ENB BIT(0)
#define STPENB_STEPENB 0x7FFF
#define IRQENB_FIFO1THRES BIT(5)
#define IRQENB_PENUP BIT(9)
#define STEPCONFIG_MODE_HWSYNC 0x2
#define STEPCONFIG_SAMPLES_AVG (1 << 4)
#define STEPCONFIG_XPP (1 << 5)
#define STEPCONFIG_XNN (1 << 6)
#define STEPCONFIG_YPP (1 << 7)
#define STEPCONFIG_YNN (1 << 8)
#define STEPCONFIG_XNP (1 << 9)
#define STEPCONFIG_YPN (1 << 10)
#define STEPCONFIG_INM (1 << 18)
#define STEPCONFIG_INP (1 << 20)
#define STEPCONFIG_INP_5 (1 << 21)
#define STEPCONFIG_FIFO1 (1 << 26)
#define STEPCONFIG_OPENDLY 0xff
#define STEPCONFIG_Z1 (3 << 19)
#define STEPIDLE_INP (1 << 22)
#define STEPCHARGE_RFP (1 << 12)
#define STEPCHARGE_INM (1 << 15)
#define STEPCHARGE_INP (1 << 19)
#define STEPCHARGE_RFM (1 << 23)
#define STEPCHARGE_DELAY 0x1
#define CNTRLREG_TSCSSENB (1 << 0)
#define CNTRLREG_STEPID (1 << 1)
#define CNTRLREG_STEPCONFIGWRT (1 << 2)
#define CNTRLREG_4WIRE (1 << 5)
#define CNTRLREG_5WIRE (1 << 6)
#define CNTRLREG_8WIRE (3 << 5)
#define CNTRLREG_TSCENB (1 << 7)
#define ADCFSM_STEPID 0x10
#include <linux/mfd/ti_am335x_tscadc.h>
#define ADCFSM_STEPID 0x10
#define SEQ_SETTLE 275
#define ADC_CLK 3000000
#define MAX_12BIT ((1 << 12) - 1)
#define TSCADC_DELTA_X 15
#define TSCADC_DELTA_Y 15
struct tscadc {
struct titsc {
struct input_dev *input;
struct clk *tsc_ick;
void __iomem *tsc_base;
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 tscadc_readl(struct tscadc *ts, unsigned int reg)
static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
{
return readl(ts->tsc_base + reg);
return readl(ts->mfd_tscadc->tscadc_base + reg);
}
static void tscadc_writel(struct tscadc *tsc, unsigned int reg,
static void titsc_writel(struct titsc *tsc, unsigned int reg,
unsigned int val)
{
writel(val, tsc->tsc_base + reg);
writel(val, tsc->mfd_tscadc->tscadc_base + reg);
}
static void tscadc_step_config(struct tscadc *ts_dev)
static void titsc_step_config(struct titsc *ts_dev)
{
unsigned int config;
int i;
int i, total_steps;
/* Configure the Step registers */
total_steps = 2 * ts_dev->steps_to_configure;
config = STEPCONFIG_MODE_HWSYNC |
STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP;
STEPCONFIG_AVG_16 | STEPCONFIG_XPP;
switch (ts_dev->wires) {
case 4:
config |= STEPCONFIG_INP | STEPCONFIG_XNN;
config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN;
break;
case 5:
config |= STEPCONFIG_YNN |
STEPCONFIG_INP_5 | STEPCONFIG_XNN |
STEPCONFIG_INP_AN4 | STEPCONFIG_XNN |
STEPCONFIG_YPP;
break;
case 8:
config |= STEPCONFIG_INP | STEPCONFIG_XNN;
config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN;
break;
}
for (i = 1; i < 7; i++) {
tscadc_writel(ts_dev, REG_STEPCONFIG(i), config);
tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
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_SAMPLES_AVG | STEPCONFIG_YNN |
STEPCONFIG_INM | STEPCONFIG_FIFO1;
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_5 |
config |= STEPCONFIG_XPP | STEPCONFIG_INP_AN4 |
STEPCONFIG_XNP | STEPCONFIG_YPN;
break;
case 8:
......@@ -156,53 +100,45 @@ static void tscadc_step_config(struct tscadc *ts_dev)
break;
}
for (i = 7; i < 13; i++) {
tscadc_writel(ts_dev, REG_STEPCONFIG(i), config);
tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
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 | STEPCHARGE_RFM |
STEPCHARGE_INM | STEPCHARGE_INP;
STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR |
STEPCHARGE_INM_AN1 | STEPCHARGE_INP_AN1;
tscadc_writel(ts_dev, REG_CHARGECONFIG, config);
tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY);
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_SAMPLES_AVG | STEPCONFIG_YPP |
STEPCONFIG_XNN | STEPCONFIG_INM;
tscadc_writel(ts_dev, REG_STEPCONFIG13, config);
tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY);
config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1;
tscadc_writel(ts_dev, REG_STEPCONFIG14, config);
tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY);
tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB);
}
static void tscadc_idle_config(struct tscadc *ts_config)
{
unsigned int idleconfig;
idleconfig = STEPCONFIG_YNN |
STEPCONFIG_INM |
STEPCONFIG_YPN | STEPIDLE_INP;
tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig);
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 tscadc_read_coordinates(struct tscadc *ts_dev,
static void titsc_read_coordinates(struct titsc *ts_dev,
unsigned int *x, unsigned int *y)
{
unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT);
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;
unsigned int i, channel;
/*
* Delta filter is used to remove large variations in sampled
......@@ -213,39 +149,60 @@ static void tscadc_read_coordinates(struct tscadc *ts_dev,
* if true the value is reported to the sub system.
*/
for (i = 0; i < fifocount - 1; i++) {
read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff;
diff = abs(read - prev_val_x);
if (diff < prev_diff_x) {
prev_diff_x = diff;
*x = read;
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;
}
prev_val_x = read;
read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff;
diff = abs(read - prev_val_y);
if (diff < prev_diff_y) {
prev_diff_y = diff;
*y = 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;
}
prev_val_y = read;
}
}
static irqreturn_t tscadc_irq(int irq, void *dev)
static irqreturn_t titsc_irq(int irq, void *dev)
{
struct tscadc *ts_dev = 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 = tscadc_readl(ts_dev, REG_IRQSTATUS);
if (status & IRQENB_FIFO1THRES) {
tscadc_read_coordinates(ts_dev, &x, &y);
status = titsc_readl(ts_dev, REG_IRQSTATUS);
if (status & IRQENB_FIFO0THRES) {
titsc_read_coordinates(ts_dev, &x, &y);
z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff;
z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff;
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) {
/*
......@@ -267,7 +224,7 @@ static irqreturn_t tscadc_irq(int irq, void *dev)
input_sync(input_dev);
}
}
irqclr |= IRQENB_FIFO1THRES;
irqclr |= IRQENB_FIFO0THRES;
}
/*
......@@ -276,10 +233,10 @@ static irqreturn_t tscadc_irq(int irq, void *dev)
*/
udelay(SEQ_SETTLE);
status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS);
status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
if (status & IRQENB_PENUP) {
/* Pen up event */
fsm = tscadc_readl(ts_dev, REG_ADCFSM);
fsm = titsc_readl(ts_dev, REG_ADCFSM);
if (fsm == ADCFSM_STEPID) {
ts_dev->pen_down = false;
input_report_key(input_dev, BTN_TOUCH, 0);
......@@ -291,11 +248,9 @@ static irqreturn_t tscadc_irq(int irq, void *dev)
irqclr |= IRQENB_PENUP;
}
tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr);
/* check pending interrupts */
tscadc_writel(ts_dev, REG_IRQEOI, 0x0);
titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB);
titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC);
return IRQ_HANDLED;
}
......@@ -303,35 +258,23 @@ static irqreturn_t tscadc_irq(int irq, void *dev)
* The functions for inserting/removing driver as a module.
*/
static int __devinit tscadc_probe(struct platform_device *pdev)
static int __devinit titsc_probe(struct platform_device *pdev)
{
const struct tsc_data *pdata = pdev->dev.platform_data;
struct resource *res;
struct tscadc *ts_dev;
struct titsc *ts_dev;
struct input_dev *input_dev;
struct clk *clk;
struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
struct mfd_tscadc_board *pdata;
int err;
int clk_value, ctrl, irq;
if (!pdata) {
dev_err(&pdev->dev, "missing platform data.\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no memory resource defined.\n");
return -EINVAL;
}
pdata = tscadc_dev->dev->platform_data;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq ID is specified.\n");
if (!pdata) {
dev_err(&pdev->dev, "Could not find platform data\n");
return -EINVAL;
}
/* Allocate memory for device */
ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL);
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");
......@@ -339,84 +282,26 @@ static int __devinit tscadc_probe(struct platform_device *pdev)
goto err_free_mem;
}
tscadc_dev->tsc = ts_dev;
ts_dev->mfd_tscadc = tscadc_dev;
ts_dev->input = input_dev;
ts_dev->irq = irq;
ts_dev->wires = pdata->wires;
ts_dev->x_plate_resistance = pdata->x_plate_resistance;
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (!res) {
dev_err(&pdev->dev, "failed to reserve registers.\n");
err = -EBUSY;
goto err_free_mem;
}
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;
ts_dev->tsc_base = ioremap(res->start, resource_size(res));
if (!ts_dev->tsc_base) {
dev_err(&pdev->dev, "failed to map registers.\n");
err = -ENOMEM;
goto err_release_mem_region;
}
err = request_irq(ts_dev->irq, tscadc_irq,
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_unmap_regs;
}
ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick");
if (IS_ERR(ts_dev->tsc_ick)) {
dev_err(&pdev->dev, "failed to get TSC ick\n");
goto err_free_irq;
}
clk_enable(ts_dev->tsc_ick);
clk = clk_get(&pdev->dev, "adc_tsc_fck");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get TSC fck\n");
err = PTR_ERR(clk);
goto err_disable_clk;
}
clk_value = clk_get_rate(clk) / ADC_CLK;
clk_put(clk);
if (clk_value < 7) {
dev_err(&pdev->dev, "clock input less than min clock requirement\n");
goto err_disable_clk;
}
/* CLKDIV needs to be configured to the value minus 1 */
tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1);
/* Enable wake-up of the SoC using touchscreen */
tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB);
ctrl = CNTRLREG_STEPCONFIGWRT |
CNTRLREG_TSCENB |
CNTRLREG_STEPID;
switch (ts_dev->wires) {
case 4:
ctrl |= CNTRLREG_4WIRE;
break;
case 5:
ctrl |= CNTRLREG_5WIRE;
break;
case 8:
ctrl |= CNTRLREG_8WIRE;
break;
goto err_free_mem;
}
tscadc_writel(ts_dev, REG_CTRL, ctrl);
tscadc_idle_config(ts_dev);
tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
tscadc_step_config(ts_dev);
tscadc_writel(ts_dev, REG_FIFO1THR, 6);
ctrl |= CNTRLREG_TSCSSENB;
tscadc_writel(ts_dev, REG_CTRL, ctrl);
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-adc";
input_dev->name = "ti-tsc";
input_dev->dev.parent = &pdev->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
......@@ -429,54 +314,81 @@ static int __devinit tscadc_probe(struct platform_device *pdev)
/* register to the input system */
err = input_register_device(input_dev);
if (err)
goto err_disable_clk;
goto err_free_irq;
platform_set_drvdata(pdev, ts_dev);
return 0;
err_disable_clk:
clk_disable(ts_dev->tsc_ick);
clk_put(ts_dev->tsc_ick);
err_free_irq:
free_irq(ts_dev->irq, ts_dev);
err_unmap_regs:
iounmap(ts_dev->tsc_base);
err_release_mem_region:
release_mem_region(res->start, resource_size(res));
err_free_mem:
input_free_device(input_dev);
kfree(ts_dev);
return err;
}
static int __devexit tscadc_remove(struct platform_device *pdev)
static int __devexit titsc_remove(struct platform_device *pdev)
{
struct tscadc *ts_dev = platform_get_drvdata(pdev);
struct resource *res;
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);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(ts_dev->tsc_base);
release_mem_region(res->start, resource_size(res));
platform_set_drvdata(pdev, NULL);
kfree(ts_dev);
return 0;
}
clk_disable(ts_dev->tsc_ick);
clk_put(ts_dev->tsc_ick);
#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;
}
kfree(ts_dev);
static int titsc_resume(struct device *dev)
{
struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
struct titsc *ts_dev = tscadc_dev->tsc;
platform_set_drvdata(pdev, NULL);
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 = tscadc_probe,
.remove = __devexit_p(tscadc_remove),
.probe = titsc_probe,
.remove = __devexit_p(titsc_remove),
.driver = {
.name = "tsc",
.owner = THIS_MODULE,
.pm = TITSC_PM_OPS,
},
};
module_platform_driver(ti_tsc_driver);
......
......@@ -104,6 +104,17 @@ config MFD_TI_SSP
To compile this driver as a module, choose M here: the
module will be called ti-ssp.
config MFD_TI_AM335X_TSCADC
tristate "TI ADC / Touch Screen chip support"
select MFD_CORE
select REGMAP
select REGMAP_MMIO
help
If you say yes here you get support for Texas Instruments series
of Touch Screen /ADC chips.
To compile this driver as a module, choose M here: the
module will be called ti_am335x_tscadc.
config HTC_EGPIO
bool "HTC EGPIO support"
depends on GENERIC_HARDIRQS && GPIOLIB && ARM
......@@ -253,6 +264,20 @@ config MFD_TPS65912_SPI
If you say yes here you get support for the TPS65912 series of
PM chips with SPI interface.
config MFD_TPS80031
bool "TI TPS80031/TPS80032 Power Management chips"
depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
If you say yes here you get support for the Texas Instruments
TPS80031/ TPS80032 Fully Integrated Power Management with Power
Path and Battery Charger. The device provides five configurable
step-down converters, 11 general purpose LDOs, USB OTG Module,
ADC, RTC, 2 PWM, System Voltage Regulator/Battery Charger with
Power Path from USB, 32K clock generator.
config MENELAUS
bool "Texas Instruments TWL92330/Menelaus PM chip"
depends on I2C=y && ARCH_OMAP2
......@@ -309,10 +334,10 @@ config MFD_TWL4030_AUDIO
config TWL6040_CORE
bool "Support for TWL6040 audio codec"
depends on I2C=y && GENERIC_HARDIRQS
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
select IRQ_DOMAIN
select REGMAP_IRQ
default n
help
Say yes here if you want support for Texas Instruments TWL6040 audio
......@@ -990,6 +1015,7 @@ config MFD_TPS65090
depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
If you say yes here you get support for the TPS65090 series of
Power Management chips.
......@@ -1034,6 +1060,7 @@ config MFD_STA2X11
bool "STA2X11 multi function device support"
depends on STA2X11
select MFD_CORE
select REGMAP_MMIO
config MFD_SYSCON
bool "System Controller Register R/W Based on Regmap"
......@@ -1053,6 +1080,38 @@ config MFD_PALMAS
If you say yes here you get support for the Palmas
series of PMIC chips from Texas Instruments.
config MFD_VIPERBOARD
tristate "Support for Nano River Technologies Viperboard"
select MFD_CORE
depends on USB
default n
help
Say yes here if you want support for Nano River Technologies
Viperboard.
There are mfd cell drivers available for i2c master, adc and
both gpios found on the board. The spi part does not yet
have a driver.
You need to select the mfd cell drivers separately.
The drivers do not support all features the board exposes.
config MFD_RETU
tristate "Support for Retu multi-function device"
select MFD_CORE
depends on I2C
select REGMAP_IRQ
help
Retu is a multi-function device found on Nokia Internet Tablets
(770, N800 and N810).
config MFD_AS3711
bool "Support for AS3711"
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
depends on I2C=y
help
Support for the AS3711 PMIC from AMS
endmenu
endif
......
......@@ -19,6 +19,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o
obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o
obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o
obj-$(CONFIG_MFD_STMPE) += stmpe.o
......@@ -55,18 +56,19 @@ obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
obj-$(CONFIG_MFD_TPS65217) += tps65217.o
obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
obj-$(CONFIG_MFD_TPS65910) += tps65910.o
tps65912-objs := tps65912-core.o tps65912-irq.o
obj-$(CONFIG_MFD_TPS65912) += tps65912.o
obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
obj-$(CONFIG_MFD_TPS80031) += tps80031.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
obj-$(CONFIG_TWL6040_CORE) += twl6040.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
......@@ -89,6 +91,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o
obj-$(CONFIG_PMIC_DA903X) += da903x.o
obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o
obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
......@@ -137,8 +140,11 @@ obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
obj-$(CONFIG_MFD_SYSCON) += syscon.o
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o
obj-$(CONFIG_MFD_RETU) += retu-mfd.o
obj-$(CONFIG_MFD_AS3711) += as3711.o
......@@ -586,38 +586,6 @@ int ab8500_suspend(struct ab8500 *ab8500)
return 0;
}
/* AB8500 GPIO Resources */
static struct resource __devinitdata ab8500_gpio_resources[] = {
{
.name = "GPIO_INT6",
.start = AB8500_INT_GPIO6R,
.end = AB8500_INT_GPIO41F,
.flags = IORESOURCE_IRQ,
}
};
/* AB9540 GPIO Resources */
static struct resource __devinitdata ab9540_gpio_resources[] = {
{
.name = "GPIO_INT6",
.start = AB8500_INT_GPIO6R,
.end = AB8500_INT_GPIO41F,
.flags = IORESOURCE_IRQ,
},
{
.name = "GPIO_INT14",
.start = AB9540_INT_GPIO50R,
.end = AB9540_INT_GPIO54R,
.flags = IORESOURCE_IRQ,
},
{
.name = "GPIO_INT15",
.start = AB9540_INT_GPIO50F,
.end = AB9540_INT_GPIO54F,
.flags = IORESOURCE_IRQ,
}
};
static struct resource ab8500_gpadc_resources[] = {
{
.name = "HW_CONV_END",
......@@ -978,6 +946,10 @@ static struct mfd_cell abx500_common_devs[] = {
.name = "ab8500-regulator",
.of_compatible = "stericsson,ab8500-regulator",
},
{
.name = "abx500-clk",
.of_compatible = "stericsson,abx500-clk",
},
{
.name = "ab8500-gpadc",
.of_compatible = "stericsson,ab8500-gpadc",
......@@ -1080,8 +1052,6 @@ static struct mfd_cell ab8500_devs[] = {
{
.name = "ab8500-gpio",
.of_compatible = "stericsson,ab8500-gpio",
.num_resources = ARRAY_SIZE(ab8500_gpio_resources),
.resources = ab8500_gpio_resources,
},
{
.name = "ab8500-usb",
......@@ -1098,8 +1068,6 @@ static struct mfd_cell ab8500_devs[] = {
static struct mfd_cell ab9540_devs[] = {
{
.name = "ab8500-gpio",
.num_resources = ARRAY_SIZE(ab9540_gpio_resources),
.resources = ab9540_gpio_resources,
},
{
.name = "ab9540-usb",
......@@ -1284,7 +1252,7 @@ static int ab8500_probe(struct platform_device *pdev)
int i;
u8 value;
ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
ab8500 = devm_kzalloc(&pdev->dev, sizeof *ab8500, GFP_KERNEL);
if (!ab8500)
return -ENOMEM;
......@@ -1294,10 +1262,8 @@ static int ab8500_probe(struct platform_device *pdev)
ab8500->dev = &pdev->dev;
resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!resource) {
ret = -ENODEV;
goto out_free_ab8500;
}
if (!resource)
return -ENODEV;
ab8500->irq = resource->start;
......@@ -1320,7 +1286,7 @@ static int ab8500_probe(struct platform_device *pdev)
ret = get_register_interruptible(ab8500, AB8500_MISC,
AB8500_IC_NAME_REG, &value);
if (ret < 0)
goto out_free_ab8500;
return ret;
ab8500->version = value;
}
......@@ -1328,7 +1294,7 @@ static int ab8500_probe(struct platform_device *pdev)
ret = get_register_interruptible(ab8500, AB8500_MISC,
AB8500_REV_REG, &value);
if (ret < 0)
goto out_free_ab8500;
return ret;
ab8500->chip_id = value;
......@@ -1345,14 +1311,13 @@ static int ab8500_probe(struct platform_device *pdev)
ab8500->mask_size = AB8500_NUM_IRQ_REGS;
ab8500->irq_reg_offset = ab8500_irq_regoffset;
}
ab8500->mask = kzalloc(ab8500->mask_size, GFP_KERNEL);
ab8500->mask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL);
if (!ab8500->mask)
return -ENOMEM;
ab8500->oldmask = kzalloc(ab8500->mask_size, GFP_KERNEL);
if (!ab8500->oldmask) {
ret = -ENOMEM;
goto out_freemask;
}
ab8500->oldmask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL);
if (!ab8500->oldmask)
return -ENOMEM;
/*
* ab8500 has switched off due to (SWITCH_OFF_STATUS):
* 0x01 Swoff bit programming
......@@ -1406,37 +1371,37 @@ static int ab8500_probe(struct platform_device *pdev)
ret = abx500_register_ops(ab8500->dev, &ab8500_ops);
if (ret)
goto out_freeoldmask;
return ret;
for (i = 0; i < ab8500->mask_size; i++)
ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
ret = ab8500_irq_init(ab8500, np);
if (ret)
goto out_freeoldmask;
return ret;
/* Activate this feature only in ab9540 */
/* till tests are done on ab8500 1p2 or later*/
if (is_ab9540(ab8500)) {
ret = request_threaded_irq(ab8500->irq, NULL,
ab8500_hierarchical_irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
"ab8500", ab8500);
ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
ab8500_hierarchical_irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
"ab8500", ab8500);
}
else {
ret = request_threaded_irq(ab8500->irq, NULL,
ab8500_irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
"ab8500", ab8500);
ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
ab8500_irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
"ab8500", ab8500);
if (ret)
goto out_freeoldmask;
return ret;
}
ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
ARRAY_SIZE(abx500_common_devs), NULL,
ab8500->irq_base, ab8500->domain);
if (ret)
goto out_freeirq;
return ret;
if (is_ab9540(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
......@@ -1447,14 +1412,14 @@ static int ab8500_probe(struct platform_device *pdev)
ARRAY_SIZE(ab8500_devs), NULL,
ab8500->irq_base, ab8500->domain);
if (ret)
goto out_freeirq;
return ret;
if (is_ab9540(ab8500) || is_ab8505(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
ARRAY_SIZE(ab9540_ab8505_devs), NULL,
ab8500->irq_base, ab8500->domain);
if (ret)
goto out_freeirq;
return ret;
if (!no_bm) {
/* Add battery management devices */
......@@ -1475,17 +1440,6 @@ static int ab8500_probe(struct platform_device *pdev)
dev_err(ab8500->dev, "error creating sysfs entries\n");
return ret;
out_freeirq:
free_irq(ab8500->irq, ab8500);
out_freeoldmask:
kfree(ab8500->oldmask);
out_freemask:
kfree(ab8500->mask);
out_free_ab8500:
kfree(ab8500);
return ret;
}
static int ab8500_remove(struct platform_device *pdev)
......@@ -1498,11 +1452,6 @@ static int ab8500_remove(struct platform_device *pdev)
sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
mfd_remove_devices(ab8500->dev);
free_irq(ab8500->irq, ab8500);
kfree(ab8500->oldmask);
kfree(ab8500->mask);
kfree(ab8500);
return 0;
}
......
......@@ -292,6 +292,7 @@ int arizona_dev_init(struct arizona *arizona)
struct device *dev = arizona->dev;
const char *type_name;
unsigned int reg, val;
int (*apply_patch)(struct arizona *) = NULL;
int ret, i;
dev_set_drvdata(arizona->dev, arizona);
......@@ -391,7 +392,7 @@ int arizona_dev_init(struct arizona *arizona)
arizona->type);
arizona->type = WM5102;
}
ret = wm5102_patch(arizona);
apply_patch = wm5102_patch;
break;
#endif
#ifdef CONFIG_MFD_WM5110
......@@ -402,7 +403,7 @@ int arizona_dev_init(struct arizona *arizona)
arizona->type);
arizona->type = WM5110;
}
ret = wm5110_patch(arizona);
apply_patch = wm5110_patch;
break;
#endif
default:
......@@ -412,9 +413,6 @@ int arizona_dev_init(struct arizona *arizona)
dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
if (ret != 0)
dev_err(arizona->dev, "Failed to apply patch: %d\n", ret);
/* If we have a /RESET GPIO we'll already be reset */
if (!arizona->pdata.reset) {
regcache_mark_dirty(arizona->regmap);
......@@ -438,6 +436,15 @@ int arizona_dev_init(struct arizona *arizona)
goto err_reset;
}
if (apply_patch) {
ret = apply_patch(arizona);
if (ret != 0) {
dev_err(arizona->dev, "Failed to apply patch: %d\n",
ret);
goto err_reset;
}
}
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
if (!arizona->pdata.gpio_defaults[i])
continue;
......
......@@ -224,6 +224,7 @@ int arizona_irq_init(struct arizona *arizona)
arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops,
arizona);
if (!arizona->virq) {
dev_err(arizona->dev, "Failed to add core IRQ domain\n");
ret = -EINVAL;
goto err;
}
......
/*
* AS3711 PMIC MFC driver
*
* Copyright (C) 2012 Renesas Electronics Corporation
* Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the version 2 of the GNU General Public License as
* published by the Free Software Foundation
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mfd/as3711.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
enum {
AS3711_REGULATOR,
AS3711_BACKLIGHT,
};
/*
* Ok to have it static: it is only used during probing and multiple I2C devices
* cannot be probed simultaneously. Just make sure to avoid stale data.
*/
static struct mfd_cell as3711_subdevs[] = {
[AS3711_REGULATOR] = {.name = "as3711-regulator",},
[AS3711_BACKLIGHT] = {.name = "as3711-backlight",},
};
static bool as3711_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case AS3711_GPIO_SIGNAL_IN:
case AS3711_INTERRUPT_STATUS_1:
case AS3711_INTERRUPT_STATUS_2:
case AS3711_INTERRUPT_STATUS_3:
case AS3711_CHARGER_STATUS_1:
case AS3711_CHARGER_STATUS_2:
case AS3711_REG_STATUS:
return true;
}
return false;
}
static bool as3711_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case AS3711_INTERRUPT_STATUS_1:
case AS3711_INTERRUPT_STATUS_2:
case AS3711_INTERRUPT_STATUS_3:
return true;
}
return false;
}
static bool as3711_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case AS3711_SD_1_VOLTAGE:
case AS3711_SD_2_VOLTAGE:
case AS3711_SD_3_VOLTAGE:
case AS3711_SD_4_VOLTAGE:
case AS3711_LDO_1_VOLTAGE:
case AS3711_LDO_2_VOLTAGE:
case AS3711_LDO_3_VOLTAGE:
case AS3711_LDO_4_VOLTAGE:
case AS3711_LDO_5_VOLTAGE:
case AS3711_LDO_6_VOLTAGE:
case AS3711_LDO_7_VOLTAGE:
case AS3711_LDO_8_VOLTAGE:
case AS3711_SD_CONTROL:
case AS3711_GPIO_SIGNAL_OUT:
case AS3711_GPIO_SIGNAL_IN:
case AS3711_SD_CONTROL_1:
case AS3711_SD_CONTROL_2:
case AS3711_CURR_CONTROL:
case AS3711_CURR1_VALUE:
case AS3711_CURR2_VALUE:
case AS3711_CURR3_VALUE:
case AS3711_STEPUP_CONTROL_1:
case AS3711_STEPUP_CONTROL_2:
case AS3711_STEPUP_CONTROL_4:
case AS3711_STEPUP_CONTROL_5:
case AS3711_REG_STATUS:
case AS3711_INTERRUPT_STATUS_1:
case AS3711_INTERRUPT_STATUS_2:
case AS3711_INTERRUPT_STATUS_3:
case AS3711_CHARGER_STATUS_1:
case AS3711_CHARGER_STATUS_2:
case AS3711_ASIC_ID_1:
case AS3711_ASIC_ID_2:
return true;
}
return false;
}
static const struct regmap_config as3711_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = as3711_volatile_reg,
.readable_reg = as3711_readable_reg,
.precious_reg = as3711_precious_reg,
.max_register = AS3711_MAX_REGS,
.num_reg_defaults_raw = AS3711_MAX_REGS,
.cache_type = REGCACHE_RBTREE,
};
static int as3711_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct as3711 *as3711;
struct as3711_platform_data *pdata = client->dev.platform_data;
unsigned int id1, id2;
int ret;
if (!pdata)
dev_dbg(&client->dev, "Platform data not found\n");
as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
if (!as3711) {
dev_err(&client->dev, "Memory allocation failed\n");
return -ENOMEM;
}
as3711->dev = &client->dev;
i2c_set_clientdata(client, as3711);
if (client->irq)
dev_notice(&client->dev, "IRQ not supported yet\n");
as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config);
if (IS_ERR(as3711->regmap)) {
ret = PTR_ERR(as3711->regmap);
dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
return ret;
}
ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_1, &id1);
if (!ret)
ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_2, &id2);
if (ret < 0) {
dev_err(&client->dev, "regmap_read() failed: %d\n", ret);
return ret;
}
if (id1 != 0x8b)
return -ENODEV;
dev_info(as3711->dev, "AS3711 detected: %x:%x\n", id1, id2);
/* We can reuse as3711_subdevs[], it will be copied in mfd_add_devices() */
if (pdata) {
as3711_subdevs[AS3711_REGULATOR].platform_data = &pdata->regulator;
as3711_subdevs[AS3711_REGULATOR].pdata_size = sizeof(pdata->regulator);
as3711_subdevs[AS3711_BACKLIGHT].platform_data = &pdata->backlight;
as3711_subdevs[AS3711_BACKLIGHT].pdata_size = sizeof(pdata->backlight);
} else {
as3711_subdevs[AS3711_REGULATOR].platform_data = NULL;
as3711_subdevs[AS3711_REGULATOR].pdata_size = 0;
as3711_subdevs[AS3711_BACKLIGHT].platform_data = NULL;
as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 0;
}
ret = mfd_add_devices(as3711->dev, -1, as3711_subdevs,
ARRAY_SIZE(as3711_subdevs), NULL, 0, NULL);
if (ret < 0)
dev_err(&client->dev, "add mfd devices failed: %d\n", ret);
return ret;
}
static int as3711_i2c_remove(struct i2c_client *client)
{
struct as3711 *as3711 = i2c_get_clientdata(client);
mfd_remove_devices(as3711->dev);
return 0;
}
static const struct i2c_device_id as3711_i2c_id[] = {
{.name = "as3711", .driver_data = 0},
{}
};
MODULE_DEVICE_TABLE(i2c, as3711_i2c_id);
static struct i2c_driver as3711_i2c_driver = {
.driver = {
.name = "as3711",
.owner = THIS_MODULE,
},
.probe = as3711_i2c_probe,
.remove = as3711_i2c_remove,
.id_table = as3711_i2c_id,
};
static int __init as3711_i2c_init(void)
{
return i2c_add_driver(&as3711_i2c_driver);
}
/* Initialise early */
subsys_initcall(as3711_i2c_init);
static void __exit as3711_i2c_exit(void)
{
i2c_del_driver(&as3711_i2c_driver);
}
module_exit(as3711_i2c_exit);
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
MODULE_DESCRIPTION("AS3711 PMIC driver");
MODULE_LICENSE("GPL v2");
......@@ -15,7 +15,6 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/module.h>
......@@ -24,16 +23,6 @@
#include <linux/mfd/da9052/pdata.h>
#include <linux/mfd/da9052/reg.h>
#define DA9052_NUM_IRQ_REGS 4
#define DA9052_IRQ_MASK_POS_1 0x01
#define DA9052_IRQ_MASK_POS_2 0x02
#define DA9052_IRQ_MASK_POS_3 0x04
#define DA9052_IRQ_MASK_POS_4 0x08
#define DA9052_IRQ_MASK_POS_5 0x10
#define DA9052_IRQ_MASK_POS_6 0x20
#define DA9052_IRQ_MASK_POS_7 0x40
#define DA9052_IRQ_MASK_POS_8 0x80
static bool da9052_reg_readable(struct device *dev, unsigned int reg)
{
switch (reg) {
......@@ -425,15 +414,6 @@ int da9052_adc_manual_read(struct da9052 *da9052, unsigned char channel)
}
EXPORT_SYMBOL_GPL(da9052_adc_manual_read);
static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data)
{
struct da9052 *da9052 = irq_data;
complete(&da9052->done);
return IRQ_HANDLED;
}
int da9052_adc_read_temp(struct da9052 *da9052)
{
int tbat;
......@@ -447,74 +427,6 @@ int da9052_adc_read_temp(struct da9052 *da9052)
}
EXPORT_SYMBOL_GPL(da9052_adc_read_temp);
static struct resource da9052_rtc_resource = {
.name = "ALM",
.start = DA9052_IRQ_ALARM,
.end = DA9052_IRQ_ALARM,
.flags = IORESOURCE_IRQ,
};
static struct resource da9052_onkey_resource = {
.name = "ONKEY",
.start = DA9052_IRQ_NONKEY,
.end = DA9052_IRQ_NONKEY,
.flags = IORESOURCE_IRQ,
};
static struct resource da9052_bat_resources[] = {
{
.name = "BATT TEMP",
.start = DA9052_IRQ_TBAT,
.end = DA9052_IRQ_TBAT,
.flags = IORESOURCE_IRQ,
},
{
.name = "DCIN DET",
.start = DA9052_IRQ_DCIN,
.end = DA9052_IRQ_DCIN,
.flags = IORESOURCE_IRQ,
},
{
.name = "DCIN REM",
.start = DA9052_IRQ_DCINREM,
.end = DA9052_IRQ_DCINREM,
.flags = IORESOURCE_IRQ,
},
{
.name = "VBUS DET",
.start = DA9052_IRQ_VBUS,
.end = DA9052_IRQ_VBUS,
.flags = IORESOURCE_IRQ,
},
{
.name = "VBUS REM",
.start = DA9052_IRQ_VBUSREM,
.end = DA9052_IRQ_VBUSREM,
.flags = IORESOURCE_IRQ,
},
{
.name = "CHG END",
.start = DA9052_IRQ_CHGEND,
.end = DA9052_IRQ_CHGEND,
.flags = IORESOURCE_IRQ,
},
};
static struct resource da9052_tsi_resources[] = {
{
.name = "PENDWN",
.start = DA9052_IRQ_PENDOWN,
.end = DA9052_IRQ_PENDOWN,
.flags = IORESOURCE_IRQ,
},
{
.name = "TSIRDY",
.start = DA9052_IRQ_TSIREADY,
.end = DA9052_IRQ_TSIREADY,
.flags = IORESOURCE_IRQ,
},
};
static struct mfd_cell da9052_subdev_info[] = {
{
.name = "da9052-regulator",
......@@ -574,13 +486,9 @@ static struct mfd_cell da9052_subdev_info[] = {
},
{
.name = "da9052-onkey",
.resources = &da9052_onkey_resource,
.num_resources = 1,
},
{
.name = "da9052-rtc",
.resources = &da9052_rtc_resource,
.num_resources = 1,
},
{
.name = "da9052-gpio",
......@@ -602,160 +510,15 @@ static struct mfd_cell da9052_subdev_info[] = {
},
{
.name = "da9052-tsi",
.resources = da9052_tsi_resources,
.num_resources = ARRAY_SIZE(da9052_tsi_resources),
},
{
.name = "da9052-bat",
.resources = da9052_bat_resources,
.num_resources = ARRAY_SIZE(da9052_bat_resources),
},
{
.name = "da9052-watchdog",
},
};
static struct regmap_irq da9052_irqs[] = {
[DA9052_IRQ_DCIN] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_1,
},
[DA9052_IRQ_VBUS] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_2,
},
[DA9052_IRQ_DCINREM] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_3,
},
[DA9052_IRQ_VBUSREM] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_4,
},
[DA9052_IRQ_VDDLOW] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_5,
},
[DA9052_IRQ_ALARM] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_6,
},
[DA9052_IRQ_SEQRDY] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_7,
},
[DA9052_IRQ_COMP1V2] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_8,
},
[DA9052_IRQ_NONKEY] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_1,
},
[DA9052_IRQ_IDFLOAT] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_2,
},
[DA9052_IRQ_IDGND] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_3,
},
[DA9052_IRQ_CHGEND] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_4,
},
[DA9052_IRQ_TBAT] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_5,
},
[DA9052_IRQ_ADC_EOM] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_6,
},
[DA9052_IRQ_PENDOWN] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_7,
},
[DA9052_IRQ_TSIREADY] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_8,
},
[DA9052_IRQ_GPI0] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_1,
},
[DA9052_IRQ_GPI1] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_2,
},
[DA9052_IRQ_GPI2] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_3,
},
[DA9052_IRQ_GPI3] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_4,
},
[DA9052_IRQ_GPI4] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_5,
},
[DA9052_IRQ_GPI5] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_6,
},
[DA9052_IRQ_GPI6] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_7,
},
[DA9052_IRQ_GPI7] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_8,
},
[DA9052_IRQ_GPI8] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_1,
},
[DA9052_IRQ_GPI9] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_2,
},
[DA9052_IRQ_GPI10] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_3,
},
[DA9052_IRQ_GPI11] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_4,
},
[DA9052_IRQ_GPI12] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_5,
},
[DA9052_IRQ_GPI13] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_6,
},
[DA9052_IRQ_GPI14] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_7,
},
[DA9052_IRQ_GPI15] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_8,
},
};
static struct regmap_irq_chip da9052_regmap_irq_chip = {
.name = "da9052_irq",
.status_base = DA9052_EVENT_A_REG,
.mask_base = DA9052_IRQ_MASK_A_REG,
.ack_base = DA9052_EVENT_A_REG,
.num_regs = DA9052_NUM_IRQ_REGS,
.irqs = da9052_irqs,
.num_irqs = ARRAY_SIZE(da9052_irqs),
};
struct regmap_config da9052_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
......@@ -782,45 +545,31 @@ int da9052_device_init(struct da9052 *da9052, u8 chip_id)
da9052->chip_id = chip_id;
if (!pdata || !pdata->irq_base)
da9052->irq_base = -1;
else
da9052->irq_base = pdata->irq_base;
ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
da9052->irq_base, &da9052_regmap_irq_chip,
&da9052->irq_data);
if (ret < 0)
goto regmap_err;
da9052->irq_base = regmap_irq_chip_get_base(da9052->irq_data);
ret = request_threaded_irq(DA9052_IRQ_ADC_EOM, NULL, da9052_auxadc_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"adc irq", da9052);
if (ret != 0)
dev_err(da9052->dev, "DA9052 ADC IRQ failed ret=%d\n", ret);
ret = da9052_irq_init(da9052);
if (ret != 0) {
dev_err(da9052->dev, "da9052_irq_init failed: %d\n", ret);
return ret;
}
ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info,
ARRAY_SIZE(da9052_subdev_info), NULL, 0, NULL);
if (ret)
if (ret) {
dev_err(da9052->dev, "mfd_add_devices failed: %d\n", ret);
goto err;
}
return 0;
err:
free_irq(DA9052_IRQ_ADC_EOM, da9052);
mfd_remove_devices(da9052->dev);
regmap_err:
da9052_irq_exit(da9052);
return ret;
}
void da9052_device_exit(struct da9052 *da9052)
{
free_irq(DA9052_IRQ_ADC_EOM, da9052);
regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data);
mfd_remove_devices(da9052->dev);
da9052_irq_exit(da9052);
}
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
......
/*
* DA9052 interrupt support
*
* Author: Fabio Estevam <fabio.estevam@freescale.com>
* Based on arizona-irq.c, which is:
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mfd/da9052/da9052.h>
#include <linux/mfd/da9052/reg.h>
#define DA9052_NUM_IRQ_REGS 4
#define DA9052_IRQ_MASK_POS_1 0x01
#define DA9052_IRQ_MASK_POS_2 0x02
#define DA9052_IRQ_MASK_POS_3 0x04
#define DA9052_IRQ_MASK_POS_4 0x08
#define DA9052_IRQ_MASK_POS_5 0x10
#define DA9052_IRQ_MASK_POS_6 0x20
#define DA9052_IRQ_MASK_POS_7 0x40
#define DA9052_IRQ_MASK_POS_8 0x80
static struct regmap_irq da9052_irqs[] = {
[DA9052_IRQ_DCIN] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_1,
},
[DA9052_IRQ_VBUS] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_2,
},
[DA9052_IRQ_DCINREM] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_3,
},
[DA9052_IRQ_VBUSREM] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_4,
},
[DA9052_IRQ_VDDLOW] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_5,
},
[DA9052_IRQ_ALARM] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_6,
},
[DA9052_IRQ_SEQRDY] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_7,
},
[DA9052_IRQ_COMP1V2] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_8,
},
[DA9052_IRQ_NONKEY] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_1,
},
[DA9052_IRQ_IDFLOAT] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_2,
},
[DA9052_IRQ_IDGND] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_3,
},
[DA9052_IRQ_CHGEND] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_4,
},
[DA9052_IRQ_TBAT] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_5,
},
[DA9052_IRQ_ADC_EOM] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_6,
},
[DA9052_IRQ_PENDOWN] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_7,
},
[DA9052_IRQ_TSIREADY] = {
.reg_offset = 1,
.mask = DA9052_IRQ_MASK_POS_8,
},
[DA9052_IRQ_GPI0] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_1,
},
[DA9052_IRQ_GPI1] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_2,
},
[DA9052_IRQ_GPI2] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_3,
},
[DA9052_IRQ_GPI3] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_4,
},
[DA9052_IRQ_GPI4] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_5,
},
[DA9052_IRQ_GPI5] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_6,
},
[DA9052_IRQ_GPI6] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_7,
},
[DA9052_IRQ_GPI7] = {
.reg_offset = 2,
.mask = DA9052_IRQ_MASK_POS_8,
},
[DA9052_IRQ_GPI8] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_1,
},
[DA9052_IRQ_GPI9] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_2,
},
[DA9052_IRQ_GPI10] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_3,
},
[DA9052_IRQ_GPI11] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_4,
},
[DA9052_IRQ_GPI12] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_5,
},
[DA9052_IRQ_GPI13] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_6,
},
[DA9052_IRQ_GPI14] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_7,
},
[DA9052_IRQ_GPI15] = {
.reg_offset = 3,
.mask = DA9052_IRQ_MASK_POS_8,
},
};
static struct regmap_irq_chip da9052_regmap_irq_chip = {
.name = "da9052_irq",
.status_base = DA9052_EVENT_A_REG,
.mask_base = DA9052_IRQ_MASK_A_REG,
.ack_base = DA9052_EVENT_A_REG,
.num_regs = DA9052_NUM_IRQ_REGS,
.irqs = da9052_irqs,
.num_irqs = ARRAY_SIZE(da9052_irqs),
};
static int da9052_map_irq(struct da9052 *da9052, int irq)
{
return regmap_irq_get_virq(da9052->irq_data, irq);
}
int da9052_enable_irq(struct da9052 *da9052, int irq)
{
irq = da9052_map_irq(da9052, irq);
if (irq < 0)
return irq;
enable_irq(irq);
return 0;
}
EXPORT_SYMBOL_GPL(da9052_enable_irq);
int da9052_disable_irq(struct da9052 *da9052, int irq)
{
irq = da9052_map_irq(da9052, irq);
if (irq < 0)
return irq;
disable_irq(irq);
return 0;
}
EXPORT_SYMBOL_GPL(da9052_disable_irq);
int da9052_disable_irq_nosync(struct da9052 *da9052, int irq)
{
irq = da9052_map_irq(da9052, irq);
if (irq < 0)
return irq;
disable_irq_nosync(irq);
return 0;
}
EXPORT_SYMBOL_GPL(da9052_disable_irq_nosync);
int da9052_request_irq(struct da9052 *da9052, int irq, char *name,
irq_handler_t handler, void *data)
{
irq = da9052_map_irq(da9052, irq);
if (irq < 0)
return irq;
return request_threaded_irq(irq, NULL, handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
name, data);
}
EXPORT_SYMBOL_GPL(da9052_request_irq);
void da9052_free_irq(struct da9052 *da9052, int irq, void *data)
{
irq = da9052_map_irq(da9052, irq);
if (irq < 0)
return;
free_irq(irq, data);
}
EXPORT_SYMBOL_GPL(da9052_free_irq);
static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data)
{
struct da9052 *da9052 = irq_data;
complete(&da9052->done);
return IRQ_HANDLED;
}
int da9052_irq_init(struct da9052 *da9052)
{
int ret;
ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-1, &da9052_regmap_irq_chip,
&da9052->irq_data);
if (ret < 0) {
dev_err(da9052->dev, "regmap_add_irq_chip failed: %d\n", ret);
goto regmap_err;
}
ret = da9052_request_irq(da9052, DA9052_IRQ_ADC_EOM, "adc-irq",
da9052_auxadc_irq, da9052);
if (ret != 0) {
dev_err(da9052->dev, "DA9052_IRQ_ADC_EOM failed: %d\n", ret);
goto request_irq_err;
}
return 0;
request_irq_err:
regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data);
regmap_err:
return ret;
}
int da9052_irq_exit(struct da9052 *da9052)
{
da9052_free_irq(da9052, DA9052_IRQ_ADC_EOM , da9052);
regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data);
return 0;
}
......@@ -2763,7 +2763,7 @@ static int db8500_irq_init(struct device_node *np)
void __init db8500_prcmu_early_init(void)
{
if (cpu_is_u8500v2()) {
if (cpu_is_u8500v2() || cpu_is_u9540()) {
void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K);
if (tcpm_base != NULL) {
......@@ -2781,7 +2781,11 @@ void __init db8500_prcmu_early_init(void)
iounmap(tcpm_base);
}
tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
if (cpu_is_u9540())
tcdm_base = ioremap_nocache(U8500_PRCMU_TCDM_BASE,
SZ_4K + SZ_8K) + SZ_8K;
else
tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
} else {
pr_err("prcmu: Unsupported chip version\n");
BUG();
......
......@@ -211,7 +211,7 @@ static int jz4740_adc_probe(struct platform_device *pdev)
int ret;
int irq_base;
adc = kmalloc(sizeof(*adc), GFP_KERNEL);
adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL);
if (!adc) {
dev_err(&pdev->dev, "Failed to allocate driver structure\n");
return -ENOMEM;
......@@ -221,30 +221,27 @@ static int jz4740_adc_probe(struct platform_device *pdev)
if (adc->irq < 0) {
ret = adc->irq;
dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
goto err_free;
return ret;
}
irq_base = platform_get_irq(pdev, 1);
if (irq_base < 0) {
ret = irq_base;
dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret);
goto err_free;
dev_err(&pdev->dev, "Failed to get irq base: %d\n", irq_base);
return irq_base;
}
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;
return -ENOENT;
}
/* 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;
return -EBUSY;
}
adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem));
......@@ -301,9 +298,6 @@ static int jz4740_adc_probe(struct platform_device *pdev)
iounmap(adc->base);
err_release_mem_region:
release_mem_region(adc->mem->start, resource_size(adc->mem));
err_free:
kfree(adc);
return ret;
}
......@@ -325,8 +319,6 @@ static int jz4740_adc_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
kfree(adc);
return 0;
}
......
......@@ -734,7 +734,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_err(&dev->dev, "I/O space for ACPI uninitialized\n");
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
lpc_ich_cells[LPC_GPIO].num_resources--;
goto gpe0_done;
}
......@@ -760,7 +760,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n");
ret = -ENODEV;
goto gpio_done;
}
......@@ -810,7 +810,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev,
pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_err(&dev->dev, "I/O space for ACPI uninitialized\n");
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
ret = -ENODEV;
goto wdt_done;
}
......@@ -830,12 +830,15 @@ static int lpc_ich_init_wdt(struct pci_dev *dev,
* we have to read RCBA from PCI Config space 0xf0 and use
* it as base. GCS = RCBA + ICH6_GCS(0x3410).
*/
if (lpc_chipset_info[id->driver_data].iTCO_version == 2) {
if (lpc_chipset_info[id->driver_data].iTCO_version == 1) {
/* Don't register iomem for TCO ver 1 */
lpc_ich_cells[LPC_WDT].num_resources--;
} else {
pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0xffffc000;
if (!(base_addr_cfg & 1)) {
pr_err("RCBA is disabled by hardware/BIOS, "
"device disabled\n");
dev_notice(&dev->dev, "RCBA is disabled by "
"hardware/BIOS, device disabled\n");
ret = -ENODEV;
goto wdt_done;
}
......@@ -871,6 +874,7 @@ static int lpc_ich_probe(struct pci_dev *dev,
* successfully.
*/
if (!cell_added) {
dev_warn(&dev->dev, "No MFD cells added\n");
lpc_ich_restore_config_space(dev);
return -ENODEV;
}
......
......@@ -119,6 +119,11 @@
#define MC13XXX_REVISION_FAB (0x03 << 11)
#define MC13XXX_REVISION_ICIDCODE (0x3f << 13)
#define MC34708_REVISION_REVMETAL (0x07 << 0)
#define MC34708_REVISION_REVFULL (0x07 << 3)
#define MC34708_REVISION_FIN (0x07 << 6)
#define MC34708_REVISION_FAB (0x07 << 9)
#define MC13XXX_ADC1 44
#define MC13XXX_ADC1_ADEN (1 << 0)
#define MC13XXX_ADC1_RAND (1 << 1)
......@@ -410,62 +415,52 @@ static irqreturn_t mc13xxx_irq_thread(int irq, void *data)
return IRQ_RETVAL(handled);
}
static const char *mc13xxx_chipname[] = {
[MC13XXX_ID_MC13783] = "mc13783",
[MC13XXX_ID_MC13892] = "mc13892",
};
#define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask))
static int mc13xxx_identify(struct mc13xxx *mc13xxx)
static void mc13xxx_print_revision(struct mc13xxx *mc13xxx, u32 revision)
{
u32 icid;
u32 revision;
int ret;
/*
* Get the generation ID from register 46, as apparently some older
* IC revisions only have this info at this location. Newer ICs seem to
* have both.
*/
ret = mc13xxx_reg_read(mc13xxx, 46, &icid);
if (ret)
return ret;
dev_info(mc13xxx->dev, "%s: rev: %d.%d, "
"fin: %d, fab: %d, icid: %d/%d\n",
mc13xxx->variant->name,
maskval(revision, MC13XXX_REVISION_REVFULL),
maskval(revision, MC13XXX_REVISION_REVMETAL),
maskval(revision, MC13XXX_REVISION_FIN),
maskval(revision, MC13XXX_REVISION_FAB),
maskval(revision, MC13XXX_REVISION_ICID),
maskval(revision, MC13XXX_REVISION_ICIDCODE));
}
icid = (icid >> 6) & 0x7;
static void mc34708_print_revision(struct mc13xxx *mc13xxx, u32 revision)
{
dev_info(mc13xxx->dev, "%s: rev %d.%d, fin: %d, fab: %d\n",
mc13xxx->variant->name,
maskval(revision, MC34708_REVISION_REVFULL),
maskval(revision, MC34708_REVISION_REVMETAL),
maskval(revision, MC34708_REVISION_FIN),
maskval(revision, MC34708_REVISION_FAB));
}
switch (icid) {
case 2:
mc13xxx->ictype = MC13XXX_ID_MC13783;
break;
case 7:
mc13xxx->ictype = MC13XXX_ID_MC13892;
break;
default:
mc13xxx->ictype = MC13XXX_ID_INVALID;
break;
}
/* These are only exported for mc13xxx-i2c and mc13xxx-spi */
struct mc13xxx_variant mc13xxx_variant_mc13783 = {
.name = "mc13783",
.print_revision = mc13xxx_print_revision,
};
EXPORT_SYMBOL_GPL(mc13xxx_variant_mc13783);
if (mc13xxx->ictype == MC13XXX_ID_MC13783 ||
mc13xxx->ictype == MC13XXX_ID_MC13892) {
ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
dev_info(mc13xxx->dev, "%s: rev: %d.%d, "
"fin: %d, fab: %d, icid: %d/%d\n",
mc13xxx_chipname[mc13xxx->ictype],
maskval(revision, MC13XXX_REVISION_REVFULL),
maskval(revision, MC13XXX_REVISION_REVMETAL),
maskval(revision, MC13XXX_REVISION_FIN),
maskval(revision, MC13XXX_REVISION_FAB),
maskval(revision, MC13XXX_REVISION_ICID),
maskval(revision, MC13XXX_REVISION_ICIDCODE));
}
struct mc13xxx_variant mc13xxx_variant_mc13892 = {
.name = "mc13892",
.print_revision = mc13xxx_print_revision,
};
EXPORT_SYMBOL_GPL(mc13xxx_variant_mc13892);
return (mc13xxx->ictype == MC13XXX_ID_INVALID) ? -ENODEV : 0;
}
struct mc13xxx_variant mc13xxx_variant_mc34708 = {
.name = "mc34708",
.print_revision = mc34708_print_revision,
};
EXPORT_SYMBOL_GPL(mc13xxx_variant_mc34708);
static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
{
return mc13xxx_chipname[mc13xxx->ictype];
return mc13xxx->variant->name;
}
int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
......@@ -653,13 +648,16 @@ int mc13xxx_common_init(struct mc13xxx *mc13xxx,
struct mc13xxx_platform_data *pdata, int irq)
{
int ret;
u32 revision;
mc13xxx_lock(mc13xxx);
ret = mc13xxx_identify(mc13xxx);
ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
if (ret)
goto err_revision;
mc13xxx->variant->print_revision(mc13xxx, revision);
/* mask all irqs */
ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff);
if (ret)
......
......@@ -24,7 +24,10 @@
static const struct i2c_device_id mc13xxx_i2c_device_id[] = {
{
.name = "mc13892",
.driver_data = MC13XXX_ID_MC13892,
.driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892,
}, {
.name = "mc34708",
.driver_data = (kernel_ulong_t)&mc13xxx_variant_mc34708,
}, {
/* sentinel */
}
......@@ -34,7 +37,10 @@ MODULE_DEVICE_TABLE(i2c, mc13xxx_i2c_device_id);
static const struct of_device_id mc13xxx_dt_ids[] = {
{
.compatible = "fsl,mc13892",
.data = (void *) &mc13xxx_i2c_device_id[0],
.data = &mc13xxx_variant_mc13892,
}, {
.compatible = "fsl,mc34708",
.data = &mc13xxx_variant_mc34708,
}, {
/* sentinel */
}
......@@ -76,11 +82,15 @@ static int mc13xxx_i2c_probe(struct i2c_client *client,
return ret;
}
ret = mc13xxx_common_init(mc13xxx, pdata, client->irq);
if (client->dev.of_node) {
const struct of_device_id *of_id =
of_match_device(mc13xxx_dt_ids, &client->dev);
mc13xxx->variant = of_id->data;
} else {
mc13xxx->variant = (void *)id->driver_data;
}
if (ret == 0 && (id->driver_data != mc13xxx->ictype))
dev_warn(mc13xxx->dev,
"device id doesn't match auto detection!\n");
ret = mc13xxx_common_init(mc13xxx, pdata, client->irq);
return ret;
}
......
......@@ -28,10 +28,13 @@
static const struct spi_device_id mc13xxx_device_id[] = {
{
.name = "mc13783",
.driver_data = MC13XXX_ID_MC13783,
.driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13783,
}, {
.name = "mc13892",
.driver_data = MC13XXX_ID_MC13892,
.driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892,
}, {
.name = "mc34708",
.driver_data = (kernel_ulong_t)&mc13xxx_variant_mc34708,
}, {
/* sentinel */
}
......@@ -39,8 +42,9 @@ static const struct spi_device_id mc13xxx_device_id[] = {
MODULE_DEVICE_TABLE(spi, mc13xxx_device_id);
static const struct of_device_id mc13xxx_dt_ids[] = {
{ .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, },
{ .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, },
{ .compatible = "fsl,mc13783", .data = &mc13xxx_variant_mc13783, },
{ .compatible = "fsl,mc13892", .data = &mc13xxx_variant_mc13892, },
{ .compatible = "fsl,mc34708", .data = &mc13xxx_variant_mc34708, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
......@@ -144,19 +148,18 @@ static int mc13xxx_spi_probe(struct spi_device *spi)
return ret;
}
ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq);
if (spi->dev.of_node) {
const struct of_device_id *of_id =
of_match_device(mc13xxx_dt_ids, &spi->dev);
if (ret) {
dev_set_drvdata(&spi->dev, NULL);
mc13xxx->variant = of_id->data;
} else {
const struct spi_device_id *devid =
spi_get_device_id(spi);
if (!devid || devid->driver_data != mc13xxx->ictype)
dev_warn(mc13xxx->dev,
"device id doesn't match auto detection!\n");
const struct spi_device_id *id_entry = spi_get_device_id(spi);
mc13xxx->variant = (void *)id_entry->driver_data;
}
return ret;
return mc13xxx_common_init(mc13xxx, pdata, spi->irq);
}
static int mc13xxx_spi_remove(struct spi_device *spi)
......
......@@ -13,19 +13,25 @@
#include <linux/regmap.h>
#include <linux/mfd/mc13xxx.h>
enum mc13xxx_id {
MC13XXX_ID_MC13783,
MC13XXX_ID_MC13892,
MC13XXX_ID_INVALID,
#define MC13XXX_NUMREGS 0x3f
struct mc13xxx;
struct mc13xxx_variant {
const char *name;
void (*print_revision)(struct mc13xxx *mc13xxx, u32 revision);
};
#define MC13XXX_NUMREGS 0x3f
extern struct mc13xxx_variant
mc13xxx_variant_mc13783,
mc13xxx_variant_mc13892,
mc13xxx_variant_mc34708;
struct mc13xxx {
struct regmap *regmap;
struct device *dev;
enum mc13xxx_id ictype;
const struct mc13xxx_variant *variant;
struct mutex lock;
int irq;
......
......@@ -21,6 +21,10 @@
#include <linux/irqdomain.h>
#include <linux/of.h>
static struct device_type mfd_dev_type = {
.name = "mfd_device",
};
int mfd_cell_enable(struct platform_device *pdev)
{
const struct mfd_cell *cell = mfd_get_cell(pdev);
......@@ -91,6 +95,7 @@ static int mfd_add_device(struct device *parent, int id,
goto fail_device;
pdev->dev.parent = parent;
pdev->dev.type = &mfd_dev_type;
if (parent->of_node && cell->of_compatible) {
for_each_child_of_node(parent->of_node, np) {
......@@ -204,10 +209,16 @@ EXPORT_SYMBOL(mfd_add_devices);
static int mfd_remove_devices_fn(struct device *dev, void *c)
{
struct platform_device *pdev = to_platform_device(dev);
const struct mfd_cell *cell = mfd_get_cell(pdev);
struct platform_device *pdev;
const struct mfd_cell *cell;
atomic_t **usage_count = c;
if (dev->type != &mfd_dev_type)
return 0;
pdev = to_platform_device(dev);
cell = mfd_get_cell(pdev);
/* find the base address of usage_count pointers (for freeing) */
if (!*usage_count || (cell->usage_count < *usage_count))
*usage_count = cell->usage_count;
......
......@@ -345,7 +345,7 @@ int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base)
mutex_init(&rc5t583->irq_lock);
/* Initailize all int register to 0 */
for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) {
for (i = 0; i < RC5T583_MAX_INTERRUPT_EN_REGS; i++) {
ret = rc5t583_write(rc5t583->dev, irq_en_add[i],
rc5t583->irq_en_reg[i]);
if (ret < 0)
......
/*
* Retu MFD driver
*
* Copyright (C) 2004, 2005 Nokia Corporation
*
* Based on code written by Juha Yrjölä, David Weinehall and Mikko Ylinen.
* Rewritten by Aaro Koskinen.
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/mfd/core.h>
#include <linux/mfd/retu.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
/* Registers */
#define RETU_REG_ASICR 0x00 /* ASIC ID and revision */
#define RETU_REG_ASICR_VILMA (1 << 7) /* Bit indicating Vilma */
#define RETU_REG_IDR 0x01 /* Interrupt ID */
#define RETU_REG_IMR 0x02 /* Interrupt mask */
/* Interrupt sources */
#define RETU_INT_PWR 0 /* Power button */
struct retu_dev {
struct regmap *regmap;
struct device *dev;
struct mutex mutex;
struct regmap_irq_chip_data *irq_data;
};
static struct resource retu_pwrbutton_res[] = {
{
.name = "retu-pwrbutton",
.start = RETU_INT_PWR,
.end = RETU_INT_PWR,
.flags = IORESOURCE_IRQ,
},
};
static struct mfd_cell retu_devs[] = {
{
.name = "retu-wdt"
},
{
.name = "retu-pwrbutton",
.resources = retu_pwrbutton_res,
.num_resources = ARRAY_SIZE(retu_pwrbutton_res),
}
};
static struct regmap_irq retu_irqs[] = {
[RETU_INT_PWR] = {
.mask = 1 << RETU_INT_PWR,
}
};
static struct regmap_irq_chip retu_irq_chip = {
.name = "RETU",
.irqs = retu_irqs,
.num_irqs = ARRAY_SIZE(retu_irqs),
.num_regs = 1,
.status_base = RETU_REG_IDR,
.mask_base = RETU_REG_IMR,
.ack_base = RETU_REG_IDR,
};
/* Retu device registered for the power off. */
static struct retu_dev *retu_pm_power_off;
int retu_read(struct retu_dev *rdev, u8 reg)
{
int ret;
int value;
mutex_lock(&rdev->mutex);
ret = regmap_read(rdev->regmap, reg, &value);
mutex_unlock(&rdev->mutex);
return ret ? ret : value;
}
EXPORT_SYMBOL_GPL(retu_read);
int retu_write(struct retu_dev *rdev, u8 reg, u16 data)
{
int ret;
mutex_lock(&rdev->mutex);
ret = regmap_write(rdev->regmap, reg, data);
mutex_unlock(&rdev->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(retu_write);
static void retu_power_off(void)
{
struct retu_dev *rdev = retu_pm_power_off;
int reg;
mutex_lock(&retu_pm_power_off->mutex);
/* Ignore power button state */
regmap_read(rdev->regmap, RETU_REG_CC1, &reg);
regmap_write(rdev->regmap, RETU_REG_CC1, reg | 2);
/* Expire watchdog immediately */
regmap_write(rdev->regmap, RETU_REG_WATCHDOG, 0);
/* Wait for poweroff */
for (;;)
cpu_relax();
mutex_unlock(&retu_pm_power_off->mutex);
}
static int retu_regmap_read(void *context, const void *reg, size_t reg_size,
void *val, size_t val_size)
{
int ret;
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
BUG_ON(reg_size != 1 || val_size != 2);
ret = i2c_smbus_read_word_data(i2c, *(u8 const *)reg);
if (ret < 0)
return ret;
*(u16 *)val = ret;
return 0;
}
static int retu_regmap_write(void *context, const void *data, size_t count)
{
u8 reg;
u16 val;
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
BUG_ON(count != sizeof(reg) + sizeof(val));
memcpy(&reg, data, sizeof(reg));
memcpy(&val, data + sizeof(reg), sizeof(val));
return i2c_smbus_write_word_data(i2c, reg, val);
}
static struct regmap_bus retu_bus = {
.read = retu_regmap_read,
.write = retu_regmap_write,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
static struct regmap_config retu_config = {
.reg_bits = 8,
.val_bits = 16,
};
static int __devinit retu_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct retu_dev *rdev;
int ret;
rdev = devm_kzalloc(&i2c->dev, sizeof(*rdev), GFP_KERNEL);
if (rdev == NULL)
return -ENOMEM;
i2c_set_clientdata(i2c, rdev);
rdev->dev = &i2c->dev;
mutex_init(&rdev->mutex);
rdev->regmap = devm_regmap_init(&i2c->dev, &retu_bus, &i2c->dev,
&retu_config);
if (IS_ERR(rdev->regmap))
return PTR_ERR(rdev->regmap);
ret = retu_read(rdev, RETU_REG_ASICR);
if (ret < 0) {
dev_err(rdev->dev, "could not read Retu revision: %d\n", ret);
return ret;
}
dev_info(rdev->dev, "Retu%s v%d.%d found\n",
(ret & RETU_REG_ASICR_VILMA) ? " & Vilma" : "",
(ret >> 4) & 0x7, ret & 0xf);
/* Mask all RETU interrupts. */
ret = retu_write(rdev, RETU_REG_IMR, 0xffff);
if (ret < 0)
return ret;
ret = regmap_add_irq_chip(rdev->regmap, i2c->irq, IRQF_ONESHOT, -1,
&retu_irq_chip, &rdev->irq_data);
if (ret < 0)
return ret;
ret = mfd_add_devices(rdev->dev, -1, retu_devs, ARRAY_SIZE(retu_devs),
NULL, regmap_irq_chip_get_base(rdev->irq_data),
NULL);
if (ret < 0) {
regmap_del_irq_chip(i2c->irq, rdev->irq_data);
return ret;
}
if (!pm_power_off) {
retu_pm_power_off = rdev;
pm_power_off = retu_power_off;
}
return 0;
}
static int __devexit retu_remove(struct i2c_client *i2c)
{
struct retu_dev *rdev = i2c_get_clientdata(i2c);
if (retu_pm_power_off == rdev) {
pm_power_off = NULL;
retu_pm_power_off = NULL;
}
mfd_remove_devices(rdev->dev);
regmap_del_irq_chip(i2c->irq, rdev->irq_data);
return 0;
}
static const struct i2c_device_id retu_id[] = {
{ "retu-mfd", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, retu_id);
static struct i2c_driver retu_driver = {
.driver = {
.name = "retu-mfd",
.owner = THIS_MODULE,
},
.probe = retu_probe,
.remove = retu_remove,
.id_table = retu_id,
};
module_i2c_driver(retu_driver);
MODULE_DESCRIPTION("Retu MFD driver");
MODULE_AUTHOR("Juha Yrjölä");
MODULE_AUTHOR("David Weinehall");
MODULE_AUTHOR("Mikko Ylinen");
MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
MODULE_LICENSE("GPL");
......@@ -22,6 +22,7 @@
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/highmem.h>
#include <linux/interrupt.h>
......
......@@ -24,67 +24,67 @@
static struct regmap_irq s2mps11_irqs[] = {
[S2MPS11_IRQ_PWRONF] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S2MPS11_IRQ_PWRONF_MASK,
},
[S2MPS11_IRQ_PWRONR] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S2MPS11_IRQ_PWRONR_MASK,
},
[S2MPS11_IRQ_JIGONBF] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S2MPS11_IRQ_JIGONBF_MASK,
},
[S2MPS11_IRQ_JIGONBR] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S2MPS11_IRQ_JIGONBR_MASK,
},
[S2MPS11_IRQ_ACOKBF] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S2MPS11_IRQ_ACOKBF_MASK,
},
[S2MPS11_IRQ_ACOKBR] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S2MPS11_IRQ_ACOKBR_MASK,
},
[S2MPS11_IRQ_PWRON1S] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S2MPS11_IRQ_PWRON1S_MASK,
},
[S2MPS11_IRQ_MRB] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S2MPS11_IRQ_MRB_MASK,
},
[S2MPS11_IRQ_RTC60S] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S2MPS11_IRQ_RTC60S_MASK,
},
[S2MPS11_IRQ_RTCA1] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S2MPS11_IRQ_RTCA1_MASK,
},
[S2MPS11_IRQ_RTCA2] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S2MPS11_IRQ_RTCA2_MASK,
},
[S2MPS11_IRQ_SMPL] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S2MPS11_IRQ_SMPL_MASK,
},
[S2MPS11_IRQ_RTC1S] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S2MPS11_IRQ_RTC1S_MASK,
},
[S2MPS11_IRQ_WTSR] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S2MPS11_IRQ_WTSR_MASK,
},
[S2MPS11_IRQ_INT120C] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S2MPS11_IRQ_INT120C_MASK,
},
[S2MPS11_IRQ_INT140C] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S2MPS11_IRQ_INT140C_MASK,
},
};
......@@ -92,146 +92,146 @@ static struct regmap_irq s2mps11_irqs[] = {
static struct regmap_irq s5m8767_irqs[] = {
[S5M8767_IRQ_PWRR] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8767_IRQ_PWRR_MASK,
},
[S5M8767_IRQ_PWRF] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8767_IRQ_PWRF_MASK,
},
[S5M8767_IRQ_PWR1S] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8767_IRQ_PWR1S_MASK,
},
[S5M8767_IRQ_JIGR] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8767_IRQ_JIGR_MASK,
},
[S5M8767_IRQ_JIGF] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8767_IRQ_JIGF_MASK,
},
[S5M8767_IRQ_LOWBAT2] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8767_IRQ_LOWBAT2_MASK,
},
[S5M8767_IRQ_LOWBAT1] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8767_IRQ_LOWBAT1_MASK,
},
[S5M8767_IRQ_MRB] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S5M8767_IRQ_MRB_MASK,
},
[S5M8767_IRQ_DVSOK2] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S5M8767_IRQ_DVSOK2_MASK,
},
[S5M8767_IRQ_DVSOK3] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S5M8767_IRQ_DVSOK3_MASK,
},
[S5M8767_IRQ_DVSOK4] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S5M8767_IRQ_DVSOK4_MASK,
},
[S5M8767_IRQ_RTC60S] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8767_IRQ_RTC60S_MASK,
},
[S5M8767_IRQ_RTCA1] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8767_IRQ_RTCA1_MASK,
},
[S5M8767_IRQ_RTCA2] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8767_IRQ_RTCA2_MASK,
},
[S5M8767_IRQ_SMPL] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8767_IRQ_SMPL_MASK,
},
[S5M8767_IRQ_RTC1S] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8767_IRQ_RTC1S_MASK,
},
[S5M8767_IRQ_WTSR] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8767_IRQ_WTSR_MASK,
},
};
static struct regmap_irq s5m8763_irqs[] = {
[S5M8763_IRQ_DCINF] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8763_IRQ_DCINF_MASK,
},
[S5M8763_IRQ_DCINR] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8763_IRQ_DCINR_MASK,
},
[S5M8763_IRQ_JIGF] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8763_IRQ_JIGF_MASK,
},
[S5M8763_IRQ_JIGR] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8763_IRQ_JIGR_MASK,
},
[S5M8763_IRQ_PWRONF] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8763_IRQ_PWRONF_MASK,
},
[S5M8763_IRQ_PWRONR] = {
.reg_offset = 1,
.reg_offset = 0,
.mask = S5M8763_IRQ_PWRONR_MASK,
},
[S5M8763_IRQ_WTSREVNT] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S5M8763_IRQ_WTSREVNT_MASK,
},
[S5M8763_IRQ_SMPLEVNT] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S5M8763_IRQ_SMPLEVNT_MASK,
},
[S5M8763_IRQ_ALARM1] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S5M8763_IRQ_ALARM1_MASK,
},
[S5M8763_IRQ_ALARM0] = {
.reg_offset = 2,
.reg_offset = 1,
.mask = S5M8763_IRQ_ALARM0_MASK,
},
[S5M8763_IRQ_ONKEY1S] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8763_IRQ_ONKEY1S_MASK,
},
[S5M8763_IRQ_TOPOFFR] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8763_IRQ_TOPOFFR_MASK,
},
[S5M8763_IRQ_DCINOVPR] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8763_IRQ_DCINOVPR_MASK,
},
[S5M8763_IRQ_CHGRSTF] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8763_IRQ_CHGRSTF_MASK,
},
[S5M8763_IRQ_DONER] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8763_IRQ_DONER_MASK,
},
[S5M8763_IRQ_CHGFAULT] = {
.reg_offset = 3,
.reg_offset = 2,
.mask = S5M8763_IRQ_CHGFAULT_MASK,
},
[S5M8763_IRQ_LOBAT1] = {
.reg_offset = 4,
.reg_offset = 3,
.mask = S5M8763_IRQ_LOBAT1_MASK,
},
[S5M8763_IRQ_LOBAT2] = {
.reg_offset = 4,
.reg_offset = 3,
.mask = S5M8763_IRQ_LOBAT2_MASK,
},
};
......
此差异已折叠。
......@@ -82,11 +82,13 @@ static const struct i2c_device_id stmpe_i2c_id[] = {
MODULE_DEVICE_TABLE(i2c, stmpe_id);
static struct i2c_driver stmpe_i2c_driver = {
.driver.name = "stmpe-i2c",
.driver.owner = THIS_MODULE,
.driver = {
.name = "stmpe-i2c",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.driver.pm = &stmpe_dev_pm_ops,
.pm = &stmpe_dev_pm_ops,
#endif
},
.probe = stmpe_i2c_probe,
.remove = stmpe_i2c_remove,
.id_table = stmpe_i2c_id,
......
此差异已折叠。
/*
* TI Touch Screen / 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/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/mfd/core.h>
#include <linux/pm_runtime.h>
#include <linux/mfd/ti_am335x_tscadc.h>
#include <linux/input/ti_am335x_tsc.h>
#include <linux/platform_data/ti_am335x_adc.h>
static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg)
{
unsigned int val;
regmap_read(tsadc->regmap_tscadc, reg, &val);
return val;
}
static void tscadc_writel(struct ti_tscadc_dev *tsadc, unsigned int reg,
unsigned int val)
{
regmap_write(tsadc->regmap_tscadc, reg, val);
}
static const struct regmap_config tscadc_regmap_config = {
.name = "ti_tscadc",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
};
static void tscadc_idle_config(struct ti_tscadc_dev *config)
{
unsigned int idleconfig;
idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM |
STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN;
tscadc_writel(config, REG_IDLECONFIG, idleconfig);
}
static int __devinit ti_tscadc_probe(struct platform_device *pdev)
{
struct ti_tscadc_dev *tscadc;
struct resource *res;
struct clk *clk;
struct mfd_tscadc_board *pdata = pdev->dev.platform_data;
struct mfd_cell *cell;
int err, ctrl;
int clk_value, clock_rate;
int tsc_wires, adc_channels = 0, total_channels;
if (!pdata) {
dev_err(&pdev->dev, "Could not find platform data\n");
return -EINVAL;
}
if (pdata->adc_init)
adc_channels = pdata->adc_init->adc_channels;
tsc_wires = pdata->tsc_init->wires;
total_channels = tsc_wires + adc_channels;
if (total_channels > 8) {
dev_err(&pdev->dev, "Number of i/p channels more than 8\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no memory resource defined.\n");
return -EINVAL;
}
/* Allocate memory for device */
tscadc = devm_kzalloc(&pdev->dev,
sizeof(struct ti_tscadc_dev), GFP_KERNEL);
if (!tscadc) {
dev_err(&pdev->dev, "failed to allocate memory.\n");
return -ENOMEM;
}
tscadc->dev = &pdev->dev;
err = platform_get_irq(pdev, 0);
if (err < 0) {
dev_err(&pdev->dev, "no irq ID is specified.\n");
goto ret;
} else
tscadc->irq = err;
res = devm_request_mem_region(&pdev->dev,
res->start, resource_size(res), pdev->name);
if (!res) {
dev_err(&pdev->dev, "failed to reserve registers.\n");
return -EBUSY;
}
tscadc->tscadc_base = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (!tscadc->tscadc_base) {
dev_err(&pdev->dev, "failed to map registers.\n");
return -ENOMEM;
}
tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev,
tscadc->tscadc_base, &tscadc_regmap_config);
if (IS_ERR(tscadc->regmap_tscadc)) {
dev_err(&pdev->dev, "regmap init failed\n");
err = PTR_ERR(tscadc->regmap_tscadc);
goto ret;
}
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
/*
* The TSC_ADC_Subsystem has 2 clock domains
* OCP_CLK and ADC_CLK.
* The ADC clock is expected to run at target of 3MHz,
* and expected to capture 12-bit data at a rate of 200 KSPS.
* The TSC_ADC_SS controller design assumes the OCP clock is
* at least 6x faster than the ADC clock.
*/
clk = clk_get(&pdev->dev, "adc_tsc_fck");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get TSC fck\n");
err = PTR_ERR(clk);
goto err_disable_clk;
}
clock_rate = clk_get_rate(clk);
clk_put(clk);
clk_value = clock_rate / ADC_CLK;
if (clk_value < MAX_CLK_DIV) {
dev_err(&pdev->dev, "clock input less than min clock requirement\n");
err = -EINVAL;
goto err_disable_clk;
}
/* TSCADC_CLKDIV needs to be configured to the value minus 1 */
clk_value = clk_value - 1;
tscadc_writel(tscadc, REG_CLKDIV, clk_value);
/* Set the control register bits */
ctrl = CNTRLREG_STEPCONFIGWRT |
CNTRLREG_TSCENB |
CNTRLREG_STEPID |
CNTRLREG_4WIRE;
tscadc_writel(tscadc, REG_CTRL, ctrl);
/* Set register bits for Idle Config Mode */
tscadc_idle_config(tscadc);
/* Enable the TSC module enable bit */
ctrl = tscadc_readl(tscadc, REG_CTRL);
ctrl |= CNTRLREG_TSCSSENB;
tscadc_writel(tscadc, REG_CTRL, ctrl);
/* TSC Cell */
cell = &tscadc->cells[TSC_CELL];
cell->name = "tsc";
cell->platform_data = tscadc;
cell->pdata_size = sizeof(*tscadc);
/* ADC Cell */
cell = &tscadc->cells[ADC_CELL];
cell->name = "tiadc";
cell->platform_data = tscadc;
cell->pdata_size = sizeof(*tscadc);
err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells,
TSCADC_CELLS, NULL, 0, NULL);
if (err < 0)
goto err_disable_clk;
device_init_wakeup(&pdev->dev, true);
platform_set_drvdata(pdev, tscadc);
return 0;
err_disable_clk:
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
ret:
return err;
}
static int __devexit ti_tscadc_remove(struct platform_device *pdev)
{
struct ti_tscadc_dev *tscadc = platform_get_drvdata(pdev);
tscadc_writel(tscadc, REG_SE, 0x00);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
mfd_remove_devices(tscadc->dev);
return 0;
}
#ifdef CONFIG_PM
static int tscadc_suspend(struct device *dev)
{
struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev);
tscadc_writel(tscadc_dev, REG_SE, 0x00);
pm_runtime_put_sync(dev);
return 0;
}
static int tscadc_resume(struct device *dev)
{
struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev);
unsigned int restore, ctrl;
pm_runtime_get_sync(dev);
/* context restore */
ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_TSCENB |
CNTRLREG_STEPID | CNTRLREG_4WIRE;
tscadc_writel(tscadc_dev, REG_CTRL, ctrl);
tscadc_idle_config(tscadc_dev);
tscadc_writel(tscadc_dev, REG_SE, STPENB_STEPENB);
restore = tscadc_readl(tscadc_dev, REG_CTRL);
tscadc_writel(tscadc_dev, REG_CTRL,
(restore | CNTRLREG_TSCSSENB));
return 0;
}
static const struct dev_pm_ops tscadc_pm_ops = {
.suspend = tscadc_suspend,
.resume = tscadc_resume,
};
#define TSCADC_PM_OPS (&tscadc_pm_ops)
#else
#define TSCADC_PM_OPS NULL
#endif
static struct platform_driver ti_tscadc_driver = {
.driver = {
.name = "ti_tscadc",
.owner = THIS_MODULE,
.pm = TSCADC_PM_OPS,
},
.probe = ti_tscadc_probe,
.remove = __devexit_p(ti_tscadc_remove),
};
module_platform_driver(ti_tscadc_driver);
MODULE_DESCRIPTION("TI touchscreen / ADC MFD controller driver");
MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
MODULE_LICENSE("GPL");
......@@ -86,9 +86,9 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct tps6507x_dev *tps6507x;
int ret = 0;
tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL);
tps6507x = devm_kzalloc(&i2c->dev, sizeof(struct tps6507x_dev),
GFP_KERNEL);
if (tps6507x == NULL)
return -ENOMEM;
......@@ -98,19 +98,8 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c,
tps6507x->read_dev = tps6507x_i2c_read_device;
tps6507x->write_dev = tps6507x_i2c_write_device;
ret = mfd_add_devices(tps6507x->dev, -1,
tps6507x_devs, ARRAY_SIZE(tps6507x_devs),
NULL, 0, NULL);
if (ret < 0)
goto err;
return ret;
err:
mfd_remove_devices(tps6507x->dev);
kfree(tps6507x);
return ret;
return mfd_add_devices(tps6507x->dev, -1, tps6507x_devs,
ARRAY_SIZE(tps6507x_devs), NULL, 0, NULL);
}
static int tps6507x_i2c_remove(struct i2c_client *i2c)
......@@ -118,8 +107,6 @@ static int tps6507x_i2c_remove(struct i2c_client *i2c)
struct tps6507x_dev *tps6507x = i2c_get_clientdata(i2c);
mfd_remove_devices(tps6507x->dev);
kfree(tps6507x);
return 0;
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -49,6 +49,8 @@ obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册