forked from Minki/linux
GPIO follow up patch and type change for v3.5 merge window
Primarily device driver additions, features and bug fixes. Not much touching gpio common subsystem support. Should not be scary. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJQx2nHAAoJEEFnBt12D9kBKnIP/0y/0LMUUMVp1LN/UeCyPdq+ rqWdI57a+ToAiGcdSG2INR5fjC8duPP1UOSgQXQFsAdF1qy7vN7ejPqmoAGcVoRW P9O64s8eYQprabx3fSiqAOhav6ZUpxyfri9z/sz8JaTlpJrbiqf1MrxFQ/0oXZa9 KqOFAJvKn+iqWjcpFkmeIvNsFT2lTeURyXhvYWUFig/VVuS335+FZYX0Ic1C69YM tf0Z+a6XO8JnAKgC13GsyJ6ctXA1kg1oKLnLHEekr3Qhkic3MTFKS2dPExzGjnbi NY4ev2SxAq74CFwSJDuhPiPk20FpveHKHLsptFdNpCR9lMG038oRnqAnYyw3gV/w z4GufpIZGK/xemIgHqNHejxS+tcH4Ax1wU++TkmIvsPJDq7uZPX/Gu9/+BMpeKxJ oJJV+mRCKDcjxXcxtrybF9+t8WdVZfW2qSt1K7LRO3eRV2n9Y+20R6iGKXhYxHaj TaQTtXIbc4q5ANg72O+c8htBhy0a2H1O5CtrXwwxBBHHsRachyHT6V9AD+7AKZ6e YElRV+v8dOviuUcj+nbf2riA7KnwtBLYfwdVQzTfbD1Fq8RUvMEjq2XQXYKhrMSw r8gp1sUnFmAVOikJFqVgYN8NyToVEyw1i2LH8skzCUnE0PPi+kT8CtaY+tTMuF3v mBixcMEKKhzsFtYmAqU+ =2XeO -----END PGP SIGNATURE----- Merge tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6 Pull GPIO updates from Grant Likely: "GPIO follow up patch and type change for v3.5 merge window Primarily device driver additions, features and bug fixes. Not much touching gpio common subsystem support. Should not be scary." * tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6: (34 commits) gpio: Provide the STMPE GPIO driver with its own IRQ Domain gpio: add TS-5500 DIO blocks support gpio: pcf857x: use client->irq for gpio_to_irq() gpio: stmpe: Add DT support for stmpe gpio gpio: pl061 depends on ARM gpio/pl061: remove old comment gpio: SPEAr: add spi chipselect control driver gpio: gpio-max710x: Support device tree probing gpio: twl4030: Use only TWL4030_MODULE_LED for LED configuration gpio: tegra: read output value when gpio is set in direction_out gpio: pca953x: Add compatible strings to gpio-pca953x driver gpio: pca953x: Register an IRQ domain gpio: mvebu: Set free callback for gpio_chip gpio: tegra: Drop exporting static functions gpio: tegra: Staticize non-exported symbols gpio: tegra: fix suspend/resume apis gpio-pch: Set parent dev for gpio chip gpio: em: Fix build errors GPIO: clps711x: use platform_device_unregister in gpio_clps711x_init() gpio/tc3589x: convert to use the simple irqdomain ...
This commit is contained in:
commit
b0885d01f9
18
Documentation/devicetree/bindings/gpio/gpio-stmpe.txt
Normal file
18
Documentation/devicetree/bindings/gpio/gpio-stmpe.txt
Normal file
@ -0,0 +1,18 @@
|
||||
STMPE gpio
|
||||
----------
|
||||
|
||||
Required properties:
|
||||
- compatible: "st,stmpe-gpio"
|
||||
|
||||
Optional properties:
|
||||
- st,norequest-mask: bitmask specifying which GPIOs should _not_ be requestable
|
||||
due to different usage (e.g. touch, keypad)
|
||||
|
||||
Node name must be stmpe_gpio and should be child node of stmpe node to which it
|
||||
belongs.
|
||||
|
||||
Example:
|
||||
stmpe_gpio {
|
||||
compatible = "st,stmpe-gpio";
|
||||
st,norequest-mask = <0x20>; //gpio 5 can't be used
|
||||
};
|
50
Documentation/devicetree/bindings/gpio/spear_spics.txt
Normal file
50
Documentation/devicetree/bindings/gpio/spear_spics.txt
Normal file
@ -0,0 +1,50 @@
|
||||
=== ST Microelectronics SPEAr SPI CS Driver ===
|
||||
|
||||
SPEAr platform provides a provision to control chipselects of ARM PL022 Prime
|
||||
Cell spi controller through its system registers, which otherwise remains under
|
||||
PL022 control. If chipselect remain under PL022 control then they would be
|
||||
released as soon as transfer is over and TxFIFO becomes empty. This is not
|
||||
desired by some of the device protocols above spi which expect (multiple)
|
||||
transfers without releasing their chipselects.
|
||||
|
||||
Chipselects can be controlled by software by turning them as GPIOs. SPEAr
|
||||
provides another interface through system registers through which software can
|
||||
directly control each PL022 chipselect. Hence, it is natural for SPEAr to export
|
||||
the control of this interface as gpio.
|
||||
|
||||
Required properties:
|
||||
|
||||
* compatible: should be defined as "st,spear-spics-gpio"
|
||||
* reg: mentioning address range of spics controller
|
||||
* st-spics,peripcfg-reg: peripheral configuration register offset
|
||||
* st-spics,sw-enable-bit: bit offset to enable sw control
|
||||
* st-spics,cs-value-bit: bit offset to drive chipselect low or high
|
||||
* st-spics,cs-enable-mask: chip select number bit mask
|
||||
* st-spics,cs-enable-shift: chip select number program offset
|
||||
* gpio-controller: Marks the device node as gpio controller
|
||||
* #gpio-cells: should be 1 and will mention chip select number
|
||||
|
||||
All the above bit offsets are within peripcfg register.
|
||||
|
||||
Example:
|
||||
-------
|
||||
spics: spics@e0700000{
|
||||
compatible = "st,spear-spics-gpio";
|
||||
reg = <0xe0700000 0x1000>;
|
||||
st-spics,peripcfg-reg = <0x3b0>;
|
||||
st-spics,sw-enable-bit = <12>;
|
||||
st-spics,cs-value-bit = <11>;
|
||||
st-spics,cs-enable-mask = <3>;
|
||||
st-spics,cs-enable-shift = <8>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
|
||||
|
||||
spi0: spi@e0100000 {
|
||||
status = "okay";
|
||||
num-cs = <3>;
|
||||
cs-gpios = <&gpio1 7 0>, <&spics 0>,
|
||||
<&spics 1>;
|
||||
...
|
||||
}
|
@ -366,6 +366,7 @@ config ARCH_CNS3XXX
|
||||
|
||||
config ARCH_CLPS711X
|
||||
bool "Cirrus Logic CLPS711x/EP721x/EP731x-based"
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select ARCH_USES_GETTIMEOFFSET
|
||||
select CLKDEV_LOOKUP
|
||||
select COMMON_CLK
|
||||
|
@ -548,7 +548,6 @@ static struct platform_device fsi_ak4648_device = {
|
||||
/* I2C */
|
||||
static struct pcf857x_platform_data pcf8575_pdata = {
|
||||
.gpio_base = GPIO_PCF8575_BASE,
|
||||
.irq = intcs_evt2irq(0x3260), /* IRQ19 */
|
||||
};
|
||||
|
||||
static struct i2c_board_info i2c0_devices[] = {
|
||||
@ -570,6 +569,7 @@ static struct i2c_board_info i2c1_devices[] = {
|
||||
static struct i2c_board_info i2c3_devices[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("pcf8575", 0x20),
|
||||
.irq = intcs_evt2irq(0x3260), /* IRQ19 */
|
||||
.platform_data = &pcf8575_pdata,
|
||||
},
|
||||
};
|
||||
|
@ -12,6 +12,7 @@ config ARCH_SPEAR13XX
|
||||
bool "ST SPEAr13xx with Device Tree"
|
||||
select ARM_GIC
|
||||
select CPU_V7
|
||||
select GPIO_SPEAR_SPICS
|
||||
select HAVE_SMP
|
||||
select MIGHT_HAVE_CACHE_L2X0
|
||||
select PINCTRL
|
||||
|
@ -90,11 +90,26 @@ config GPIO_DA9052
|
||||
help
|
||||
Say yes here to enable the GPIO driver for the DA9052 chip.
|
||||
|
||||
config GPIO_DA9055
|
||||
tristate "Dialog Semiconductor DA9055 GPIO"
|
||||
depends on MFD_DA9055
|
||||
help
|
||||
Say yes here to enable the GPIO driver for the DA9055 chip.
|
||||
|
||||
The Dialog DA9055 PMIC chip has 3 GPIO pins that can be
|
||||
be controller by this driver.
|
||||
|
||||
If driver is built as a module it will be called gpio-da9055.
|
||||
|
||||
config GPIO_MAX730X
|
||||
tristate
|
||||
|
||||
comment "Memory mapped GPIO drivers:"
|
||||
|
||||
config GPIO_CLPS711X
|
||||
def_bool y
|
||||
depends on ARCH_CLPS711X
|
||||
|
||||
config GPIO_GENERIC_PLATFORM
|
||||
tristate "Generic memory-mapped GPIO controller support (MMIO platform device)"
|
||||
select GPIO_GENERIC
|
||||
@ -174,7 +189,7 @@ config GPIO_MXS
|
||||
|
||||
config GPIO_PL061
|
||||
bool "PrimeCell PL061 GPIO support"
|
||||
depends on ARM_AMBA
|
||||
depends on ARM && ARM_AMBA
|
||||
select GENERIC_IRQ_CHIP
|
||||
help
|
||||
Say yes here to support the PrimeCell PL061 GPIO device
|
||||
@ -185,6 +200,13 @@ config GPIO_PXA
|
||||
help
|
||||
Say yes here to support the PXA GPIO device
|
||||
|
||||
config GPIO_SPEAR_SPICS
|
||||
bool "ST SPEAr13xx SPI Chip Select as GPIO support"
|
||||
depends on PLAT_SPEAR
|
||||
select GENERIC_IRQ_CHIP
|
||||
help
|
||||
Say yes here to support ST SPEAr SPI Chip Select as GPIO device
|
||||
|
||||
config GPIO_STA2X11
|
||||
bool "STA2x11/ConneXt GPIO support"
|
||||
depends on MFD_STA2X11
|
||||
@ -193,6 +215,14 @@ config GPIO_STA2X11
|
||||
Say yes here to support the STA2x11/ConneXt GPIO device.
|
||||
The GPIO module has 128 GPIO pins with alternate functions.
|
||||
|
||||
config GPIO_TS5500
|
||||
tristate "TS-5500 DIO blocks and compatibles"
|
||||
help
|
||||
This driver supports Digital I/O exposed by pin blocks found on some
|
||||
Technologic Systems platforms. It includes, but is not limited to, 3
|
||||
blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
|
||||
LCD port.
|
||||
|
||||
config GPIO_VT8500
|
||||
bool "VIA/Wondermedia SoC GPIO Support"
|
||||
depends on ARCH_VT8500
|
||||
|
@ -17,8 +17,10 @@ obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
|
||||
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
|
||||
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
|
||||
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
|
||||
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
|
||||
obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
|
||||
obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
|
||||
obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
|
||||
obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o
|
||||
obj-$(CONFIG_GPIO_EM) += gpio-em.o
|
||||
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
|
||||
@ -58,6 +60,7 @@ obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o
|
||||
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
|
||||
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
|
||||
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
|
||||
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
|
||||
obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
|
||||
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
|
||||
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
|
||||
@ -69,6 +72,7 @@ obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o
|
||||
obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
|
||||
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
|
||||
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
|
||||
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
|
||||
|
199
drivers/gpio/gpio-clps711x.c
Normal file
199
drivers/gpio/gpio-clps711x.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* CLPS711X GPIO driver
|
||||
*
|
||||
* Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
|
||||
*
|
||||
* 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/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#define CLPS711X_GPIO_PORTS 5
|
||||
#define CLPS711X_GPIO_NAME "gpio-clps711x"
|
||||
|
||||
struct clps711x_gpio {
|
||||
struct gpio_chip chip[CLPS711X_GPIO_PORTS];
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static void __iomem *clps711x_ports[] = {
|
||||
CLPS711X_VIRT_BASE + PADR,
|
||||
CLPS711X_VIRT_BASE + PBDR,
|
||||
CLPS711X_VIRT_BASE + PCDR,
|
||||
CLPS711X_VIRT_BASE + PDDR,
|
||||
CLPS711X_VIRT_BASE + PEDR,
|
||||
};
|
||||
|
||||
static void __iomem *clps711x_pdirs[] = {
|
||||
CLPS711X_VIRT_BASE + PADDR,
|
||||
CLPS711X_VIRT_BASE + PBDDR,
|
||||
CLPS711X_VIRT_BASE + PCDDR,
|
||||
CLPS711X_VIRT_BASE + PDDDR,
|
||||
CLPS711X_VIRT_BASE + PEDDR,
|
||||
};
|
||||
|
||||
#define clps711x_port(x) clps711x_ports[x->base / 8]
|
||||
#define clps711x_pdir(x) clps711x_pdirs[x->base / 8]
|
||||
|
||||
static int gpio_clps711x_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
return !!(readb(clps711x_port(chip)) & (1 << offset));
|
||||
}
|
||||
|
||||
static void gpio_clps711x_set(struct gpio_chip *chip, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
int tmp;
|
||||
unsigned long flags;
|
||||
struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
|
||||
|
||||
spin_lock_irqsave(&gpio->lock, flags);
|
||||
tmp = readb(clps711x_port(chip)) & ~(1 << offset);
|
||||
if (value)
|
||||
tmp |= 1 << offset;
|
||||
writeb(tmp, clps711x_port(chip));
|
||||
spin_unlock_irqrestore(&gpio->lock, flags);
|
||||
}
|
||||
|
||||
static int gpio_clps711x_dir_in(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
int tmp;
|
||||
unsigned long flags;
|
||||
struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
|
||||
|
||||
spin_lock_irqsave(&gpio->lock, flags);
|
||||
tmp = readb(clps711x_pdir(chip)) & ~(1 << offset);
|
||||
writeb(tmp, clps711x_pdir(chip));
|
||||
spin_unlock_irqrestore(&gpio->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_clps711x_dir_out(struct gpio_chip *chip, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
int tmp;
|
||||
unsigned long flags;
|
||||
struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
|
||||
|
||||
spin_lock_irqsave(&gpio->lock, flags);
|
||||
tmp = readb(clps711x_pdir(chip)) | (1 << offset);
|
||||
writeb(tmp, clps711x_pdir(chip));
|
||||
tmp = readb(clps711x_port(chip)) & ~(1 << offset);
|
||||
if (value)
|
||||
tmp |= 1 << offset;
|
||||
writeb(tmp, clps711x_port(chip));
|
||||
spin_unlock_irqrestore(&gpio->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_clps711x_dir_in_inv(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
int tmp;
|
||||
unsigned long flags;
|
||||
struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
|
||||
|
||||
spin_lock_irqsave(&gpio->lock, flags);
|
||||
tmp = readb(clps711x_pdir(chip)) | (1 << offset);
|
||||
writeb(tmp, clps711x_pdir(chip));
|
||||
spin_unlock_irqrestore(&gpio->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_clps711x_dir_out_inv(struct gpio_chip *chip, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
int tmp;
|
||||
unsigned long flags;
|
||||
struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
|
||||
|
||||
spin_lock_irqsave(&gpio->lock, flags);
|
||||
tmp = readb(clps711x_pdir(chip)) & ~(1 << offset);
|
||||
writeb(tmp, clps711x_pdir(chip));
|
||||
tmp = readb(clps711x_port(chip)) & ~(1 << offset);
|
||||
if (value)
|
||||
tmp |= 1 << offset;
|
||||
writeb(tmp, clps711x_port(chip));
|
||||
spin_unlock_irqrestore(&gpio->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct {
|
||||
char *name;
|
||||
int nr;
|
||||
int inv_dir;
|
||||
} clps711x_gpio_ports[] __initconst = {
|
||||
{ "PORTA", 8, 0, },
|
||||
{ "PORTB", 8, 0, },
|
||||
{ "PORTC", 8, 0, },
|
||||
{ "PORTD", 8, 1, },
|
||||
{ "PORTE", 3, 0, },
|
||||
};
|
||||
|
||||
static int __init gpio_clps711x_init(void)
|
||||
{
|
||||
int i;
|
||||
struct platform_device *pdev;
|
||||
struct clps711x_gpio *gpio;
|
||||
|
||||
pdev = platform_device_alloc(CLPS711X_GPIO_NAME, 0);
|
||||
if (!pdev) {
|
||||
pr_err("Cannot create platform device: %s\n",
|
||||
CLPS711X_GPIO_NAME);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_device_add(pdev);
|
||||
|
||||
gpio = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_gpio),
|
||||
GFP_KERNEL);
|
||||
if (!gpio) {
|
||||
dev_err(&pdev->dev, "GPIO allocating memory error\n");
|
||||
platform_device_unregister(pdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, gpio);
|
||||
|
||||
spin_lock_init(&gpio->lock);
|
||||
|
||||
for (i = 0; i < CLPS711X_GPIO_PORTS; i++) {
|
||||
gpio->chip[i].owner = THIS_MODULE;
|
||||
gpio->chip[i].dev = &pdev->dev;
|
||||
gpio->chip[i].label = clps711x_gpio_ports[i].name;
|
||||
gpio->chip[i].base = i * 8;
|
||||
gpio->chip[i].ngpio = clps711x_gpio_ports[i].nr;
|
||||
gpio->chip[i].get = gpio_clps711x_get;
|
||||
gpio->chip[i].set = gpio_clps711x_set;
|
||||
if (!clps711x_gpio_ports[i].inv_dir) {
|
||||
gpio->chip[i].direction_input = gpio_clps711x_dir_in;
|
||||
gpio->chip[i].direction_output = gpio_clps711x_dir_out;
|
||||
} else {
|
||||
gpio->chip[i].direction_input = gpio_clps711x_dir_in_inv;
|
||||
gpio->chip[i].direction_output = gpio_clps711x_dir_out_inv;
|
||||
}
|
||||
WARN_ON(gpiochip_add(&gpio->chip[i]));
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "GPIO driver initialized\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(gpio_clps711x_init);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
|
||||
MODULE_DESCRIPTION("CLPS711X GPIO driver");
|
204
drivers/gpio/gpio-da9055.c
Normal file
204
drivers/gpio/gpio-da9055.c
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* GPIO Driver for Dialog DA9055 PMICs.
|
||||
*
|
||||
* Copyright(c) 2012 Dialog Semiconductor Ltd.
|
||||
*
|
||||
* Author: David Dajun Chen <dchen@diasemi.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <linux/mfd/da9055/core.h>
|
||||
#include <linux/mfd/da9055/reg.h>
|
||||
#include <linux/mfd/da9055/pdata.h>
|
||||
|
||||
#define DA9055_VDD_IO 0x0
|
||||
#define DA9055_PUSH_PULL 0x3
|
||||
#define DA9055_ACT_LOW 0x0
|
||||
#define DA9055_GPI 0x1
|
||||
#define DA9055_PORT_MASK 0x3
|
||||
#define DA9055_PORT_SHIFT(offset) (4 * (offset % 2))
|
||||
|
||||
#define DA9055_INPUT DA9055_GPI
|
||||
#define DA9055_OUTPUT DA9055_PUSH_PULL
|
||||
#define DA9055_IRQ_GPI0 3
|
||||
|
||||
struct da9055_gpio {
|
||||
struct da9055 *da9055;
|
||||
struct gpio_chip gp;
|
||||
};
|
||||
|
||||
static inline struct da9055_gpio *to_da9055_gpio(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct da9055_gpio, gp);
|
||||
}
|
||||
|
||||
static int da9055_gpio_get(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct da9055_gpio *gpio = to_da9055_gpio(gc);
|
||||
int gpio_direction = 0;
|
||||
int ret;
|
||||
|
||||
/* Get GPIO direction */
|
||||
ret = da9055_reg_read(gpio->da9055, (offset >> 1) + DA9055_REG_GPIO0_1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gpio_direction = ret & (DA9055_PORT_MASK) << DA9055_PORT_SHIFT(offset);
|
||||
gpio_direction >>= DA9055_PORT_SHIFT(offset);
|
||||
switch (gpio_direction) {
|
||||
case DA9055_INPUT:
|
||||
ret = da9055_reg_read(gpio->da9055, DA9055_REG_STATUS_B);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
case DA9055_OUTPUT:
|
||||
ret = da9055_reg_read(gpio->da9055, DA9055_REG_GPIO_MODE0_2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret & (1 << offset);
|
||||
|
||||
}
|
||||
|
||||
static void da9055_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
|
||||
{
|
||||
struct da9055_gpio *gpio = to_da9055_gpio(gc);
|
||||
|
||||
da9055_reg_update(gpio->da9055,
|
||||
DA9055_REG_GPIO_MODE0_2,
|
||||
1 << offset,
|
||||
value << offset);
|
||||
}
|
||||
|
||||
static int da9055_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct da9055_gpio *gpio = to_da9055_gpio(gc);
|
||||
unsigned char reg_byte;
|
||||
|
||||
reg_byte = (DA9055_ACT_LOW | DA9055_GPI)
|
||||
<< DA9055_PORT_SHIFT(offset);
|
||||
|
||||
return da9055_reg_update(gpio->da9055, (offset >> 1) +
|
||||
DA9055_REG_GPIO0_1,
|
||||
DA9055_PORT_MASK <<
|
||||
DA9055_PORT_SHIFT(offset),
|
||||
reg_byte);
|
||||
}
|
||||
|
||||
static int da9055_gpio_direction_output(struct gpio_chip *gc,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
struct da9055_gpio *gpio = to_da9055_gpio(gc);
|
||||
unsigned char reg_byte;
|
||||
int ret;
|
||||
|
||||
reg_byte = (DA9055_VDD_IO | DA9055_PUSH_PULL)
|
||||
<< DA9055_PORT_SHIFT(offset);
|
||||
|
||||
ret = da9055_reg_update(gpio->da9055, (offset >> 1) +
|
||||
DA9055_REG_GPIO0_1,
|
||||
DA9055_PORT_MASK <<
|
||||
DA9055_PORT_SHIFT(offset),
|
||||
reg_byte);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
da9055_gpio_set(gc, offset, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da9055_gpio_to_irq(struct gpio_chip *gc, u32 offset)
|
||||
{
|
||||
struct da9055_gpio *gpio = to_da9055_gpio(gc);
|
||||
struct da9055 *da9055 = gpio->da9055;
|
||||
|
||||
return regmap_irq_get_virq(da9055->irq_data,
|
||||
DA9055_IRQ_GPI0 + offset);
|
||||
}
|
||||
|
||||
static struct gpio_chip reference_gp __devinitdata = {
|
||||
.label = "da9055-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
.get = da9055_gpio_get,
|
||||
.set = da9055_gpio_set,
|
||||
.direction_input = da9055_gpio_direction_input,
|
||||
.direction_output = da9055_gpio_direction_output,
|
||||
.to_irq = da9055_gpio_to_irq,
|
||||
.can_sleep = 1,
|
||||
.ngpio = 3,
|
||||
.base = -1,
|
||||
};
|
||||
|
||||
static int __devinit da9055_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct da9055_gpio *gpio;
|
||||
struct da9055_pdata *pdata;
|
||||
int ret;
|
||||
|
||||
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
|
||||
if (gpio == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
gpio->da9055 = dev_get_drvdata(pdev->dev.parent);
|
||||
pdata = gpio->da9055->dev->platform_data;
|
||||
|
||||
gpio->gp = reference_gp;
|
||||
if (pdata && pdata->gpio_base)
|
||||
gpio->gp.base = pdata->gpio_base;
|
||||
|
||||
ret = gpiochip_add(&gpio->gp);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, gpio);
|
||||
|
||||
return 0;
|
||||
|
||||
err_mem:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit da9055_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct da9055_gpio *gpio = platform_get_drvdata(pdev);
|
||||
|
||||
return gpiochip_remove(&gpio->gp);
|
||||
}
|
||||
|
||||
static struct platform_driver da9055_gpio_driver = {
|
||||
.probe = da9055_gpio_probe,
|
||||
.remove = __devexit_p(da9055_gpio_remove),
|
||||
.driver = {
|
||||
.name = "da9055-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init da9055_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&da9055_gpio_driver);
|
||||
}
|
||||
subsys_initcall(da9055_gpio_init);
|
||||
|
||||
static void __exit da9055_gpio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&da9055_gpio_driver);
|
||||
}
|
||||
module_exit(da9055_gpio_exit);
|
||||
|
||||
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
|
||||
MODULE_DESCRIPTION("DA9055 GPIO Device Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:da9055-gpio");
|
@ -35,7 +35,6 @@
|
||||
struct em_gio_priv {
|
||||
void __iomem *base0;
|
||||
void __iomem *base1;
|
||||
unsigned int irq_base;
|
||||
spinlock_t sense_lock;
|
||||
struct platform_device *pdev;
|
||||
struct gpio_chip gpio_chip;
|
||||
@ -214,7 +213,7 @@ static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset,
|
||||
|
||||
static int em_gio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
return irq_find_mapping(gpio_to_priv(chip)->irq_domain, offset);
|
||||
return irq_create_mapping(gpio_to_priv(chip)->irq_domain, offset);
|
||||
}
|
||||
|
||||
static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int virq,
|
||||
@ -234,40 +233,6 @@ static struct irq_domain_ops em_gio_irq_domain_ops = {
|
||||
.map = em_gio_irq_domain_map,
|
||||
};
|
||||
|
||||
static int __devinit em_gio_irq_domain_init(struct em_gio_priv *p)
|
||||
{
|
||||
struct platform_device *pdev = p->pdev;
|
||||
struct gpio_em_config *pdata = pdev->dev.platform_data;
|
||||
|
||||
p->irq_base = irq_alloc_descs(pdata->irq_base, 0,
|
||||
pdata->number_of_pins, numa_node_id());
|
||||
if (p->irq_base < 0) {
|
||||
dev_err(&pdev->dev, "cannot get irq_desc\n");
|
||||
return p->irq_base;
|
||||
}
|
||||
pr_debug("gio: hw base = %d, nr = %d, sw base = %d\n",
|
||||
pdata->gpio_base, pdata->number_of_pins, p->irq_base);
|
||||
|
||||
p->irq_domain = irq_domain_add_legacy(pdev->dev.of_node,
|
||||
pdata->number_of_pins,
|
||||
p->irq_base, 0,
|
||||
&em_gio_irq_domain_ops, p);
|
||||
if (!p->irq_domain) {
|
||||
irq_free_descs(p->irq_base, pdata->number_of_pins);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void em_gio_irq_domain_cleanup(struct em_gio_priv *p)
|
||||
{
|
||||
struct gpio_em_config *pdata = p->pdev->dev.platform_data;
|
||||
|
||||
irq_free_descs(p->irq_base, pdata->number_of_pins);
|
||||
/* FIXME: irq domain wants to be freed! */
|
||||
}
|
||||
|
||||
static int __devinit em_gio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_em_config *pdata = pdev->dev.platform_data;
|
||||
@ -334,8 +299,11 @@ static int __devinit em_gio_probe(struct platform_device *pdev)
|
||||
irq_chip->irq_set_type = em_gio_irq_set_type;
|
||||
irq_chip->flags = IRQCHIP_SKIP_SET_WAKE;
|
||||
|
||||
ret = em_gio_irq_domain_init(p);
|
||||
if (ret) {
|
||||
p->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
|
||||
pdata->number_of_pins,
|
||||
&em_gio_irq_domain_ops, p);
|
||||
if (!p->irq_domain) {
|
||||
ret = -ENXIO;
|
||||
dev_err(&pdev->dev, "cannot initialize irq domain\n");
|
||||
goto err3;
|
||||
}
|
||||
@ -364,7 +332,7 @@ err6:
|
||||
err5:
|
||||
free_irq(irq[0]->start, pdev);
|
||||
err4:
|
||||
em_gio_irq_domain_cleanup(p);
|
||||
irq_domain_remove(p->irq_domain);
|
||||
err3:
|
||||
iounmap(p->base1);
|
||||
err2:
|
||||
@ -390,7 +358,7 @@ static int __devexit em_gio_remove(struct platform_device *pdev)
|
||||
|
||||
free_irq(irq[1]->start, pdev);
|
||||
free_irq(irq[0]->start, pdev);
|
||||
em_gio_irq_domain_cleanup(p);
|
||||
irq_domain_remove(p->irq_domain);
|
||||
iounmap(p->base1);
|
||||
iounmap(p->base0);
|
||||
kfree(p);
|
||||
|
@ -167,10 +167,6 @@ int __devinit __max730x_probe(struct max7301 *ts)
|
||||
int i, ret;
|
||||
|
||||
pdata = dev->platform_data;
|
||||
if (!pdata || !pdata->base) {
|
||||
dev_err(dev, "incorrect or missing platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_init(&ts->lock);
|
||||
dev_set_drvdata(dev, ts);
|
||||
@ -178,7 +174,12 @@ int __devinit __max730x_probe(struct max7301 *ts)
|
||||
/* Power up the chip and disable IRQ output */
|
||||
ts->write(dev, 0x04, 0x01);
|
||||
|
||||
ts->input_pullup_active = pdata->input_pullup_active;
|
||||
if (pdata) {
|
||||
ts->input_pullup_active = pdata->input_pullup_active;
|
||||
ts->chip.base = pdata->base;
|
||||
} else {
|
||||
ts->chip.base = -1;
|
||||
}
|
||||
ts->chip.label = dev->driver->name;
|
||||
|
||||
ts->chip.direction_input = max7301_direction_input;
|
||||
@ -186,7 +187,6 @@ int __devinit __max730x_probe(struct max7301 *ts)
|
||||
ts->chip.direction_output = max7301_direction_output;
|
||||
ts->chip.set = max7301_set;
|
||||
|
||||
ts->chip.base = pdata->base;
|
||||
ts->chip.ngpio = PIN_NUMBER;
|
||||
ts->chip.can_sleep = 1;
|
||||
ts->chip.dev = dev;
|
||||
|
@ -168,12 +168,12 @@ static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip)
|
||||
* Functions implementing the gpio_chip methods
|
||||
*/
|
||||
|
||||
int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin)
|
||||
static int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin)
|
||||
{
|
||||
return pinctrl_request_gpio(chip->base + pin);
|
||||
}
|
||||
|
||||
void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin)
|
||||
static void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin)
|
||||
{
|
||||
pinctrl_free_gpio(chip->base + pin);
|
||||
}
|
||||
@ -546,6 +546,7 @@ static int __devinit mvebu_gpio_probe(struct platform_device *pdev)
|
||||
mvchip->chip.label = dev_name(&pdev->dev);
|
||||
mvchip->chip.dev = &pdev->dev;
|
||||
mvchip->chip.request = mvebu_gpio_request;
|
||||
mvchip->chip.free = mvebu_gpio_free;
|
||||
mvchip->chip.direction_input = mvebu_gpio_direction_input;
|
||||
mvchip->chip.get = mvebu_gpio_get;
|
||||
mvchip->chip.direction_output = mvebu_gpio_direction_output;
|
||||
@ -673,8 +674,8 @@ static int __devinit mvebu_gpio_probe(struct platform_device *pdev)
|
||||
IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE);
|
||||
|
||||
/* Setup irq domain on top of the generic chip. */
|
||||
mvchip->domain = irq_domain_add_legacy(np, mvchip->chip.ngpio,
|
||||
mvchip->irqbase, 0,
|
||||
mvchip->domain = irq_domain_add_simple(np, mvchip->chip.ngpio,
|
||||
mvchip->irqbase,
|
||||
&irq_domain_simple_ops,
|
||||
mvchip);
|
||||
if (!mvchip->domain) {
|
||||
|
@ -1105,7 +1105,7 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
bank = devm_kzalloc(&pdev->dev, sizeof(struct gpio_bank), GFP_KERNEL);
|
||||
bank = devm_kzalloc(dev, sizeof(struct gpio_bank), GFP_KERNEL);
|
||||
if (!bank) {
|
||||
dev_err(dev, "Memory alloc failed\n");
|
||||
return -ENOMEM;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/pca953x.h>
|
||||
#include <linux/slab.h>
|
||||
@ -83,6 +84,7 @@ struct pca953x_chip {
|
||||
u32 irq_trig_raise;
|
||||
u32 irq_trig_fall;
|
||||
int irq_base;
|
||||
struct irq_domain *domain;
|
||||
#endif
|
||||
|
||||
struct i2c_client *client;
|
||||
@ -333,14 +335,14 @@ static void pca953x_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
|
||||
|
||||
chip->irq_mask &= ~(1 << (d->irq - chip->irq_base));
|
||||
chip->irq_mask &= ~(1 << d->hwirq);
|
||||
}
|
||||
|
||||
static void pca953x_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
|
||||
|
||||
chip->irq_mask |= 1 << (d->irq - chip->irq_base);
|
||||
chip->irq_mask |= 1 << d->hwirq;
|
||||
}
|
||||
|
||||
static void pca953x_irq_bus_lock(struct irq_data *d)
|
||||
@ -372,8 +374,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
|
||||
static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
|
||||
u32 level = d->irq - chip->irq_base;
|
||||
u32 mask = 1 << level;
|
||||
u32 mask = 1 << d->hwirq;
|
||||
|
||||
if (!(type & IRQ_TYPE_EDGE_BOTH)) {
|
||||
dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
|
||||
@ -454,7 +455,7 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid)
|
||||
|
||||
do {
|
||||
level = __ffs(pending);
|
||||
handle_nested_irq(level + chip->irq_base);
|
||||
handle_nested_irq(irq_find_mapping(chip->domain, level));
|
||||
|
||||
pending &= ~(1 << level);
|
||||
} while (pending);
|
||||
@ -499,6 +500,17 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
|
||||
if (chip->irq_base < 0)
|
||||
goto out_failed;
|
||||
|
||||
chip->domain = irq_domain_add_legacy(client->dev.of_node,
|
||||
chip->gpio_chip.ngpio,
|
||||
chip->irq_base,
|
||||
0,
|
||||
&irq_domain_simple_ops,
|
||||
NULL);
|
||||
if (!chip->domain) {
|
||||
ret = -ENODEV;
|
||||
goto out_irqdesc_free;
|
||||
}
|
||||
|
||||
for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) {
|
||||
int irq = lvl + chip->irq_base;
|
||||
|
||||
@ -521,7 +533,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to request irq %d\n",
|
||||
client->irq);
|
||||
goto out_failed;
|
||||
goto out_irqdesc_free;
|
||||
}
|
||||
|
||||
chip->gpio_chip.to_irq = pca953x_gpio_to_irq;
|
||||
@ -529,6 +541,8 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
|
||||
|
||||
return 0;
|
||||
|
||||
out_irqdesc_free:
|
||||
irq_free_descs(chip->irq_base, chip->gpio_chip.ngpio);
|
||||
out_failed:
|
||||
chip->irq_base = -1;
|
||||
return ret;
|
||||
@ -751,9 +765,38 @@ static int pca953x_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pca953x_dt_ids[] = {
|
||||
{ .compatible = "nxp,pca9534", },
|
||||
{ .compatible = "nxp,pca9535", },
|
||||
{ .compatible = "nxp,pca9536", },
|
||||
{ .compatible = "nxp,pca9537", },
|
||||
{ .compatible = "nxp,pca9538", },
|
||||
{ .compatible = "nxp,pca9539", },
|
||||
{ .compatible = "nxp,pca9554", },
|
||||
{ .compatible = "nxp,pca9555", },
|
||||
{ .compatible = "nxp,pca9556", },
|
||||
{ .compatible = "nxp,pca9557", },
|
||||
{ .compatible = "nxp,pca9574", },
|
||||
{ .compatible = "nxp,pca9575", },
|
||||
|
||||
{ .compatible = "maxim,max7310", },
|
||||
{ .compatible = "maxim,max7312", },
|
||||
{ .compatible = "maxim,max7313", },
|
||||
{ .compatible = "maxim,max7315", },
|
||||
|
||||
{ .compatible = "ti,pca6107", },
|
||||
{ .compatible = "ti,tca6408", },
|
||||
{ .compatible = "ti,tca6416", },
|
||||
{ .compatible = "ti,tca6424", },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, pca953x_dt_ids);
|
||||
|
||||
static struct i2c_driver pca953x_driver = {
|
||||
.driver = {
|
||||
.name = "pca953x",
|
||||
.of_match_table = pca953x_dt_ids,
|
||||
},
|
||||
.probe = pca953x_probe,
|
||||
.remove = pca953x_remove,
|
||||
|
@ -223,11 +223,11 @@ static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio)
|
||||
|
||||
static int pcf857x_irq_domain_init(struct pcf857x *gpio,
|
||||
struct pcf857x_platform_data *pdata,
|
||||
struct device *dev)
|
||||
struct i2c_client *client)
|
||||
{
|
||||
int status;
|
||||
|
||||
gpio->irq_domain = irq_domain_add_linear(dev->of_node,
|
||||
gpio->irq_domain = irq_domain_add_linear(client->dev.of_node,
|
||||
gpio->chip.ngpio,
|
||||
&pcf857x_irq_domain_ops,
|
||||
NULL);
|
||||
@ -235,15 +235,15 @@ static int pcf857x_irq_domain_init(struct pcf857x *gpio,
|
||||
goto fail;
|
||||
|
||||
/* enable real irq */
|
||||
status = request_irq(pdata->irq, pcf857x_irq_demux, 0,
|
||||
dev_name(dev), gpio);
|
||||
status = request_irq(client->irq, pcf857x_irq_demux, 0,
|
||||
dev_name(&client->dev), gpio);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
/* enable gpio_to_irq() */
|
||||
INIT_WORK(&gpio->work, pcf857x_irq_demux_work);
|
||||
gpio->chip.to_irq = pcf857x_to_irq;
|
||||
gpio->irq = pdata->irq;
|
||||
gpio->irq = client->irq;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -285,8 +285,8 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||
gpio->chip.ngpio = id->driver_data;
|
||||
|
||||
/* enable gpio_to_irq() if platform has settings */
|
||||
if (pdata && pdata->irq) {
|
||||
status = pcf857x_irq_domain_init(gpio, pdata, &client->dev);
|
||||
if (pdata && client->irq) {
|
||||
status = pcf857x_irq_domain_init(gpio, pdata, client);
|
||||
if (status < 0) {
|
||||
dev_err(&client->dev, "irq_domain init failed\n");
|
||||
goto fail;
|
||||
@ -368,15 +368,6 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
/* NOTE: these chips can issue "some pin-changed" IRQs, which we
|
||||
* don't yet even try to use. Among other issues, the relevant
|
||||
* genirq state isn't available to modular drivers; and most irq
|
||||
* methods can't be called from sleeping contexts.
|
||||
*/
|
||||
|
||||
dev_info(&client->dev, "%s\n",
|
||||
client->irq ? " (irq ignored)" : "");
|
||||
|
||||
/* Let platform code set up the GPIOs and their users.
|
||||
* Now is the first time anyone could use them.
|
||||
*/
|
||||
@ -388,13 +379,15 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||
dev_warn(&client->dev, "setup --> %d\n", status);
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "probed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
dev_dbg(&client->dev, "probe error %d for '%s'\n",
|
||||
status, client->name);
|
||||
|
||||
if (pdata && pdata->irq)
|
||||
if (pdata && client->irq)
|
||||
pcf857x_irq_domain_cleanup(gpio);
|
||||
|
||||
kfree(gpio);
|
||||
@ -418,7 +411,7 @@ static int pcf857x_remove(struct i2c_client *client)
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata && pdata->irq)
|
||||
if (pdata && client->irq)
|
||||
pcf857x_irq_domain_cleanup(gpio);
|
||||
|
||||
status = gpiochip_remove(&gpio->chip);
|
||||
|
@ -215,6 +215,7 @@ static void pch_gpio_setup(struct pch_gpio *chip)
|
||||
struct gpio_chip *gpio = &chip->gpio;
|
||||
|
||||
gpio->label = dev_name(chip->dev);
|
||||
gpio->dev = chip->dev;
|
||||
gpio->owner = THIS_MODULE;
|
||||
gpio->direction_input = pch_gpio_direction_input;
|
||||
gpio->get = pch_gpio_get;
|
||||
|
@ -48,12 +48,7 @@ struct pl061_context_save_regs {
|
||||
#endif
|
||||
|
||||
struct pl061_gpio {
|
||||
/* Each of the two spinlocks protects a different set of hardware
|
||||
* regiters and data structurs. This decouples the code of the IRQ from
|
||||
* the GPIO code. This also makes the case of a GPIO routine call from
|
||||
* the IRQ code simpler.
|
||||
*/
|
||||
spinlock_t lock; /* GPIO registers */
|
||||
spinlock_t lock;
|
||||
|
||||
void __iomem *base;
|
||||
int irq_base;
|
||||
@ -216,39 +211,34 @@ static void __init pl061_init_gc(struct pl061_gpio *chip, int irq_base)
|
||||
IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0);
|
||||
}
|
||||
|
||||
static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
|
||||
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
struct pl061_platform_data *pdata;
|
||||
struct device *dev = &adev->dev;
|
||||
struct pl061_platform_data *pdata = dev->platform_data;
|
||||
struct pl061_gpio *chip;
|
||||
int ret, irq, i;
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (chip == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata = dev->dev.platform_data;
|
||||
if (pdata) {
|
||||
chip->gc.base = pdata->gpio_base;
|
||||
chip->irq_base = pdata->irq_base;
|
||||
} else if (dev->dev.of_node) {
|
||||
} else if (adev->dev.of_node) {
|
||||
chip->gc.base = -1;
|
||||
chip->irq_base = 0;
|
||||
} else {
|
||||
ret = -ENODEV;
|
||||
goto free_mem;
|
||||
}
|
||||
} else
|
||||
return -ENODEV;
|
||||
|
||||
if (!request_mem_region(dev->res.start,
|
||||
resource_size(&dev->res), "pl061")) {
|
||||
ret = -EBUSY;
|
||||
goto free_mem;
|
||||
}
|
||||
if (!devm_request_mem_region(dev, adev->res.start,
|
||||
resource_size(&adev->res), "pl061"))
|
||||
return -EBUSY;
|
||||
|
||||
chip->base = ioremap(dev->res.start, resource_size(&dev->res));
|
||||
if (chip->base == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto release_region;
|
||||
}
|
||||
chip->base = devm_ioremap(dev, adev->res.start,
|
||||
resource_size(&adev->res));
|
||||
if (chip->base == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&chip->lock);
|
||||
|
||||
@ -258,13 +248,13 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
|
||||
chip->gc.set = pl061_set_value;
|
||||
chip->gc.to_irq = pl061_to_irq;
|
||||
chip->gc.ngpio = PL061_GPIO_NR;
|
||||
chip->gc.label = dev_name(&dev->dev);
|
||||
chip->gc.dev = &dev->dev;
|
||||
chip->gc.label = dev_name(dev);
|
||||
chip->gc.dev = dev;
|
||||
chip->gc.owner = THIS_MODULE;
|
||||
|
||||
ret = gpiochip_add(&chip->gc);
|
||||
if (ret)
|
||||
goto iounmap;
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* irq_chip support
|
||||
@ -276,11 +266,10 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
|
||||
pl061_init_gc(chip, chip->irq_base);
|
||||
|
||||
writeb(0, chip->base + GPIOIE); /* disable irqs */
|
||||
irq = dev->irq[0];
|
||||
if (irq < 0) {
|
||||
ret = -ENODEV;
|
||||
goto iounmap;
|
||||
}
|
||||
irq = adev->irq[0];
|
||||
if (irq < 0)
|
||||
return -ENODEV;
|
||||
|
||||
irq_set_chained_handler(irq, pl061_irq_handler);
|
||||
irq_set_handler_data(irq, chip);
|
||||
|
||||
@ -294,18 +283,9 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
|
||||
}
|
||||
}
|
||||
|
||||
amba_set_drvdata(dev, chip);
|
||||
amba_set_drvdata(adev, chip);
|
||||
|
||||
return 0;
|
||||
|
||||
iounmap:
|
||||
iounmap(chip->base);
|
||||
release_region:
|
||||
release_mem_region(dev->res.start, resource_size(&dev->res));
|
||||
free_mem:
|
||||
kfree(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
217
drivers/gpio/gpio-spear-spics.c
Normal file
217
drivers/gpio/gpio-spear-spics.c
Normal file
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* SPEAr platform SPI chipselect abstraction over gpiolib
|
||||
*
|
||||
* Copyright (C) 2012 ST Microelectronics
|
||||
* Shiraz Hashim <shiraz.hashim@st.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* maximum chipselects */
|
||||
#define NUM_OF_GPIO 4
|
||||
|
||||
/*
|
||||
* Provision is available on some SPEAr SoCs to control ARM PL022 spi cs
|
||||
* through system registers. This register lies outside spi (pl022)
|
||||
* address space into system registers.
|
||||
*
|
||||
* It provides control for spi chip select lines so that any chipselect
|
||||
* (out of 4 possible chipselects in pl022) can be made low to select
|
||||
* the particular slave.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct spear_spics - represents spi chip select control
|
||||
* @base: base address
|
||||
* @perip_cfg: configuration register
|
||||
* @sw_enable_bit: bit to enable s/w control over chipselects
|
||||
* @cs_value_bit: bit to program high or low chipselect
|
||||
* @cs_enable_mask: mask to select bits required to select chipselect
|
||||
* @cs_enable_shift: bit pos of cs_enable_mask
|
||||
* @use_count: use count of a spi controller cs lines
|
||||
* @last_off: stores last offset caller of set_value()
|
||||
* @chip: gpio_chip abstraction
|
||||
*/
|
||||
struct spear_spics {
|
||||
void __iomem *base;
|
||||
u32 perip_cfg;
|
||||
u32 sw_enable_bit;
|
||||
u32 cs_value_bit;
|
||||
u32 cs_enable_mask;
|
||||
u32 cs_enable_shift;
|
||||
unsigned long use_count;
|
||||
int last_off;
|
||||
struct gpio_chip chip;
|
||||
};
|
||||
|
||||
/* gpio framework specific routines */
|
||||
static int spics_get_value(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
struct spear_spics *spics = container_of(chip, struct spear_spics,
|
||||
chip);
|
||||
u32 tmp;
|
||||
|
||||
/* select chip select from register */
|
||||
tmp = readl_relaxed(spics->base + spics->perip_cfg);
|
||||
if (spics->last_off != offset) {
|
||||
spics->last_off = offset;
|
||||
tmp &= ~(spics->cs_enable_mask << spics->cs_enable_shift);
|
||||
tmp |= offset << spics->cs_enable_shift;
|
||||
}
|
||||
|
||||
/* toggle chip select line */
|
||||
tmp &= ~(0x1 << spics->cs_value_bit);
|
||||
tmp |= value << spics->cs_value_bit;
|
||||
writel_relaxed(tmp, spics->base + spics->perip_cfg);
|
||||
}
|
||||
|
||||
static int spics_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int spics_direction_output(struct gpio_chip *chip, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
spics_set_value(chip, offset, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spics_request(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct spear_spics *spics = container_of(chip, struct spear_spics,
|
||||
chip);
|
||||
u32 tmp;
|
||||
|
||||
if (!spics->use_count++) {
|
||||
tmp = readl_relaxed(spics->base + spics->perip_cfg);
|
||||
tmp |= 0x1 << spics->sw_enable_bit;
|
||||
tmp |= 0x1 << spics->cs_value_bit;
|
||||
writel_relaxed(tmp, spics->base + spics->perip_cfg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spics_free(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct spear_spics *spics = container_of(chip, struct spear_spics,
|
||||
chip);
|
||||
u32 tmp;
|
||||
|
||||
if (!--spics->use_count) {
|
||||
tmp = readl_relaxed(spics->base + spics->perip_cfg);
|
||||
tmp &= ~(0x1 << spics->sw_enable_bit);
|
||||
writel_relaxed(tmp, spics->base + spics->perip_cfg);
|
||||
}
|
||||
}
|
||||
|
||||
static int spics_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct spear_spics *spics;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "invalid IORESOURCE_MEM\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spics = devm_kzalloc(&pdev->dev, sizeof(*spics), GFP_KERNEL);
|
||||
if (!spics) {
|
||||
dev_err(&pdev->dev, "memory allocation fail\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spics->base = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!spics->base) {
|
||||
dev_err(&pdev->dev, "request and ioremap fail\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "st-spics,peripcfg-reg",
|
||||
&spics->perip_cfg))
|
||||
goto err_dt_data;
|
||||
if (of_property_read_u32(np, "st-spics,sw-enable-bit",
|
||||
&spics->sw_enable_bit))
|
||||
goto err_dt_data;
|
||||
if (of_property_read_u32(np, "st-spics,cs-value-bit",
|
||||
&spics->cs_value_bit))
|
||||
goto err_dt_data;
|
||||
if (of_property_read_u32(np, "st-spics,cs-enable-mask",
|
||||
&spics->cs_enable_mask))
|
||||
goto err_dt_data;
|
||||
if (of_property_read_u32(np, "st-spics,cs-enable-shift",
|
||||
&spics->cs_enable_shift))
|
||||
goto err_dt_data;
|
||||
|
||||
platform_set_drvdata(pdev, spics);
|
||||
|
||||
spics->chip.ngpio = NUM_OF_GPIO;
|
||||
spics->chip.base = -1;
|
||||
spics->chip.request = spics_request;
|
||||
spics->chip.free = spics_free;
|
||||
spics->chip.direction_input = spics_direction_input;
|
||||
spics->chip.direction_output = spics_direction_output;
|
||||
spics->chip.get = spics_get_value;
|
||||
spics->chip.set = spics_set_value;
|
||||
spics->chip.label = dev_name(&pdev->dev);
|
||||
spics->chip.dev = &pdev->dev;
|
||||
spics->chip.owner = THIS_MODULE;
|
||||
spics->last_off = -1;
|
||||
|
||||
ret = gpiochip_add(&spics->chip);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to add gpio chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "spear spics registered\n");
|
||||
return 0;
|
||||
|
||||
err_dt_data:
|
||||
dev_err(&pdev->dev, "DT probe failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct of_device_id spics_gpio_of_match[] = {
|
||||
{ .compatible = "st,spear-spics-gpio" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spics_gpio_of_match);
|
||||
|
||||
static struct platform_driver spics_gpio_driver = {
|
||||
.probe = spics_gpio_probe,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "spear-spics-gpio",
|
||||
.of_match_table = spics_gpio_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init spics_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&spics_gpio_driver);
|
||||
}
|
||||
subsys_initcall(spics_gpio_init);
|
||||
|
||||
MODULE_AUTHOR("Shiraz Hashim <shiraz.hashim@st.com>");
|
||||
MODULE_DESCRIPTION("ST Microlectronics SPEAr SPI Chip Select Abstraction");
|
||||
MODULE_LICENSE("GPL");
|
@ -11,7 +11,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/stmpe.h>
|
||||
|
||||
/*
|
||||
@ -28,6 +30,7 @@ struct stmpe_gpio {
|
||||
struct stmpe *stmpe;
|
||||
struct device *dev;
|
||||
struct mutex irq_lock;
|
||||
struct irq_domain *domain;
|
||||
|
||||
int irq_base;
|
||||
unsigned norequest_mask;
|
||||
@ -103,7 +106,7 @@ static int stmpe_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
|
||||
|
||||
return stmpe_gpio->irq_base + offset;
|
||||
return irq_create_mapping(stmpe_gpio->domain, offset);
|
||||
}
|
||||
|
||||
static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||
@ -132,7 +135,7 @@ static struct gpio_chip template_chip = {
|
||||
static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
|
||||
int offset = d->irq - stmpe_gpio->irq_base;
|
||||
int offset = d->hwirq;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
@ -199,7 +202,7 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d)
|
||||
static void stmpe_gpio_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
|
||||
int offset = d->irq - stmpe_gpio->irq_base;
|
||||
int offset = d->hwirq;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
@ -209,7 +212,7 @@ static void stmpe_gpio_irq_mask(struct irq_data *d)
|
||||
static void stmpe_gpio_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
|
||||
int offset = d->irq - stmpe_gpio->irq_base;
|
||||
int offset = d->hwirq;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
@ -251,8 +254,9 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
|
||||
while (stat) {
|
||||
int bit = __ffs(stat);
|
||||
int line = bank * 8 + bit;
|
||||
int virq = irq_find_mapping(stmpe_gpio->domain, line);
|
||||
|
||||
handle_nested_irq(stmpe_gpio->irq_base + line);
|
||||
handle_nested_irq(virq);
|
||||
stat &= ~(1 << bit);
|
||||
}
|
||||
|
||||
@ -267,43 +271,61 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
struct stmpe_gpio *stmpe_gpio = d->host_data;
|
||||
|
||||
if (!stmpe_gpio)
|
||||
return -EINVAL;
|
||||
|
||||
irq_set_chip_data(hwirq, stmpe_gpio);
|
||||
irq_set_chip_and_handler(hwirq, &stmpe_gpio_irq_chip,
|
||||
handle_simple_irq);
|
||||
irq_set_nested_thread(hwirq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(hwirq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(hwirq);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int virq)
|
||||
{
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(virq, 0);
|
||||
#endif
|
||||
irq_set_chip_and_handler(virq, NULL, NULL);
|
||||
irq_set_chip_data(virq, NULL);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops stmpe_gpio_irq_simple_ops = {
|
||||
.unmap = stmpe_gpio_irq_unmap,
|
||||
.map = stmpe_gpio_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio)
|
||||
{
|
||||
int base = stmpe_gpio->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
|
||||
irq_set_chip_data(irq, stmpe_gpio);
|
||||
irq_set_chip_and_handler(irq, &stmpe_gpio_irq_chip,
|
||||
handle_simple_irq);
|
||||
irq_set_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(irq);
|
||||
#endif
|
||||
stmpe_gpio->domain = irq_domain_add_simple(NULL,
|
||||
stmpe_gpio->chip.ngpio, base,
|
||||
&stmpe_gpio_irq_simple_ops, stmpe_gpio);
|
||||
if (!stmpe_gpio->domain) {
|
||||
dev_err(stmpe_gpio->dev, "failed to create irqdomain\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio)
|
||||
{
|
||||
int base = stmpe_gpio->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, 0);
|
||||
#endif
|
||||
irq_set_chip_and_handler(irq, NULL, NULL);
|
||||
irq_set_chip_data(irq, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct stmpe_gpio_platform_data *pdata;
|
||||
struct stmpe_gpio *stmpe_gpio;
|
||||
int ret;
|
||||
@ -321,13 +343,17 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
|
||||
|
||||
stmpe_gpio->dev = &pdev->dev;
|
||||
stmpe_gpio->stmpe = stmpe;
|
||||
stmpe_gpio->norequest_mask = pdata ? pdata->norequest_mask : 0;
|
||||
|
||||
stmpe_gpio->chip = template_chip;
|
||||
stmpe_gpio->chip.ngpio = stmpe->num_gpios;
|
||||
stmpe_gpio->chip.dev = &pdev->dev;
|
||||
stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1;
|
||||
|
||||
if (pdata)
|
||||
stmpe_gpio->norequest_mask = pdata->norequest_mask;
|
||||
else if (np)
|
||||
of_property_read_u32(np, "st,norequest-mask",
|
||||
&stmpe_gpio->norequest_mask);
|
||||
|
||||
if (irq >= 0)
|
||||
stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
|
||||
else
|
||||
@ -348,7 +374,7 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
|
||||
IRQF_ONESHOT, "stmpe-gpio", stmpe_gpio);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
|
||||
goto out_removeirq;
|
||||
goto out_disable;
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,9 +394,6 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
|
||||
out_freeirq:
|
||||
if (irq >= 0)
|
||||
free_irq(irq, stmpe_gpio);
|
||||
out_removeirq:
|
||||
if (irq >= 0)
|
||||
stmpe_gpio_irq_remove(stmpe_gpio);
|
||||
out_disable:
|
||||
stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
|
||||
out_free:
|
||||
@ -398,10 +421,9 @@ static int __devexit stmpe_gpio_remove(struct platform_device *pdev)
|
||||
|
||||
stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
|
||||
|
||||
if (irq >= 0) {
|
||||
if (irq >= 0)
|
||||
free_irq(irq, stmpe_gpio);
|
||||
stmpe_gpio_irq_remove(stmpe_gpio);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(stmpe_gpio);
|
||||
|
||||
|
@ -292,17 +292,15 @@ static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio,
|
||||
{
|
||||
int base = tc3589x_gpio->irq_base;
|
||||
|
||||
if (base) {
|
||||
tc3589x_gpio->domain = irq_domain_add_legacy(
|
||||
NULL, tc3589x_gpio->chip.ngpio, base,
|
||||
0, &tc3589x_irq_ops, tc3589x_gpio);
|
||||
}
|
||||
else {
|
||||
tc3589x_gpio->domain = irq_domain_add_linear(
|
||||
np, tc3589x_gpio->chip.ngpio,
|
||||
&tc3589x_irq_ops, tc3589x_gpio);
|
||||
}
|
||||
|
||||
/*
|
||||
* If this results in a linear domain, irq_create_mapping() will
|
||||
* take care of allocating IRQ descriptors at runtime. When a base
|
||||
* is provided, the IRQ descriptors will be allocated when the
|
||||
* domain is instantiated.
|
||||
*/
|
||||
tc3589x_gpio->domain = irq_domain_add_simple(np,
|
||||
tc3589x_gpio->chip.ngpio, base, &tc3589x_irq_ops,
|
||||
tc3589x_gpio);
|
||||
if (!tc3589x_gpio->domain) {
|
||||
dev_err(tc3589x_gpio->dev, "Failed to create irqdomain\n");
|
||||
return -ENOSYS;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#include <asm/mach/irq.h>
|
||||
|
||||
@ -64,7 +65,7 @@ struct tegra_gpio_bank {
|
||||
int bank;
|
||||
int irq;
|
||||
spinlock_t lvl_lock[4];
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
u32 cnf[4];
|
||||
u32 out[4];
|
||||
u32 oe[4];
|
||||
@ -109,20 +110,18 @@ static void tegra_gpio_enable(int gpio)
|
||||
{
|
||||
tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_gpio_enable);
|
||||
|
||||
static void tegra_gpio_disable(int gpio)
|
||||
{
|
||||
tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_gpio_disable);
|
||||
|
||||
int tegra_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||
static int tegra_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
return pinctrl_request_gpio(offset);
|
||||
}
|
||||
|
||||
void tegra_gpio_free(struct gpio_chip *chip, unsigned offset)
|
||||
static void tegra_gpio_free(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
pinctrl_free_gpio(offset);
|
||||
tegra_gpio_disable(offset);
|
||||
@ -135,6 +134,11 @@ static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
|
||||
static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
/* If gpio is in output mode then read from the out value */
|
||||
if ((tegra_gpio_readl(GPIO_OE(offset)) >> GPIO_BIT(offset)) & 1)
|
||||
return (tegra_gpio_readl(GPIO_OUT(offset)) >>
|
||||
GPIO_BIT(offset)) & 0x1;
|
||||
|
||||
return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1;
|
||||
}
|
||||
|
||||
@ -285,8 +289,8 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
|
||||
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void tegra_gpio_resume(void)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra_gpio_resume(struct device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
int b;
|
||||
@ -308,9 +312,10 @@ void tegra_gpio_resume(void)
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tegra_gpio_suspend(void)
|
||||
static int tegra_gpio_suspend(struct device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
int b;
|
||||
@ -330,6 +335,7 @@ void tegra_gpio_suspend(void)
|
||||
}
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_gpio_wake_enable(struct irq_data *d, unsigned int enable)
|
||||
@ -345,11 +351,15 @@ static struct irq_chip tegra_gpio_irq_chip = {
|
||||
.irq_mask = tegra_gpio_irq_mask,
|
||||
.irq_unmask = tegra_gpio_irq_unmask,
|
||||
.irq_set_type = tegra_gpio_irq_set_type,
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.irq_set_wake = tegra_gpio_wake_enable,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops tegra_gpio_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume)
|
||||
};
|
||||
|
||||
struct tegra_gpio_soc_config {
|
||||
u32 bank_stride;
|
||||
u32 upper_offset;
|
||||
@ -380,7 +390,6 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct tegra_gpio_soc_config *config;
|
||||
int irq_base;
|
||||
struct resource *res;
|
||||
struct tegra_gpio_bank *bank;
|
||||
int gpio;
|
||||
@ -417,14 +426,11 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
irq_base = irq_alloc_descs(-1, 0, tegra_gpio_chip.ngpio, 0);
|
||||
if (irq_base < 0) {
|
||||
dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
irq_domain = irq_domain_add_legacy(pdev->dev.of_node,
|
||||
tegra_gpio_chip.ngpio, irq_base, 0,
|
||||
irq_domain = irq_domain_add_linear(pdev->dev.of_node,
|
||||
tegra_gpio_chip.ngpio,
|
||||
&irq_domain_simple_ops, NULL);
|
||||
if (!irq_domain)
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < tegra_gpio_bank_count; i++) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
|
||||
@ -464,7 +470,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
|
||||
gpiochip_add(&tegra_gpio_chip);
|
||||
|
||||
for (gpio = 0; gpio < tegra_gpio_chip.ngpio; gpio++) {
|
||||
int irq = irq_find_mapping(irq_domain, gpio);
|
||||
int irq = irq_create_mapping(irq_domain, gpio);
|
||||
/* No validity check; all Tegra GPIOs are valid IRQs */
|
||||
|
||||
bank = &tegra_gpio_banks[GPIO_BANK(gpio)];
|
||||
@ -493,6 +499,7 @@ static struct platform_driver tegra_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "tegra-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tegra_gpio_pm_ops,
|
||||
.of_match_table = tegra_gpio_of_match,
|
||||
},
|
||||
.probe = tegra_gpio_probe,
|
||||
|
466
drivers/gpio/gpio-ts5500.c
Normal file
466
drivers/gpio/gpio-ts5500.c
Normal file
@ -0,0 +1,466 @@
|
||||
/*
|
||||
* Digital I/O driver for Technologic Systems TS-5500
|
||||
*
|
||||
* Copyright (c) 2012 Savoir-faire Linux Inc.
|
||||
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||
*
|
||||
* Technologic Systems platforms have pin blocks, exposing several Digital
|
||||
* Input/Output lines (DIO). This driver aims to support single pin blocks.
|
||||
* In that sense, the support is not limited to the TS-5500 blocks.
|
||||
* Actually, the following platforms have DIO support:
|
||||
*
|
||||
* TS-5500:
|
||||
* Documentation: http://wiki.embeddedarm.com/wiki/TS-5500
|
||||
* Blocks: DIO1, DIO2 and LCD port.
|
||||
*
|
||||
* TS-5600:
|
||||
* Documentation: http://wiki.embeddedarm.com/wiki/TS-5600
|
||||
* Blocks: LCD port (identical to TS-5500 LCD).
|
||||
*
|
||||
* 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/bitops.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/gpio-ts5500.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* List of supported Technologic Systems platforms DIO blocks */
|
||||
enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD };
|
||||
|
||||
struct ts5500_priv {
|
||||
const struct ts5500_dio *pinout;
|
||||
struct gpio_chip gpio_chip;
|
||||
spinlock_t lock;
|
||||
bool strap;
|
||||
u8 hwirq;
|
||||
};
|
||||
|
||||
/*
|
||||
* Hex 7D is used to control several blocks (e.g. DIO2 and LCD port).
|
||||
* This flag ensures that the region has been requested by this driver.
|
||||
*/
|
||||
static bool hex7d_reserved;
|
||||
|
||||
/*
|
||||
* This structure is used to describe capabilities of DIO lines,
|
||||
* such as available directions and connected interrupt (if any).
|
||||
*/
|
||||
struct ts5500_dio {
|
||||
const u8 value_addr;
|
||||
const u8 value_mask;
|
||||
const u8 control_addr;
|
||||
const u8 control_mask;
|
||||
const bool no_input;
|
||||
const bool no_output;
|
||||
const u8 irq;
|
||||
};
|
||||
|
||||
#define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit) \
|
||||
{ \
|
||||
.value_addr = vaddr, \
|
||||
.value_mask = BIT(vbit), \
|
||||
.control_addr = caddr, \
|
||||
.control_mask = BIT(cbit), \
|
||||
}
|
||||
|
||||
#define TS5500_DIO_IN(addr, bit) \
|
||||
{ \
|
||||
.value_addr = addr, \
|
||||
.value_mask = BIT(bit), \
|
||||
.no_output = true, \
|
||||
}
|
||||
|
||||
#define TS5500_DIO_IN_IRQ(addr, bit, _irq) \
|
||||
{ \
|
||||
.value_addr = addr, \
|
||||
.value_mask = BIT(bit), \
|
||||
.no_output = true, \
|
||||
.irq = _irq, \
|
||||
}
|
||||
|
||||
#define TS5500_DIO_OUT(addr, bit) \
|
||||
{ \
|
||||
.value_addr = addr, \
|
||||
.value_mask = BIT(bit), \
|
||||
.no_input = true, \
|
||||
}
|
||||
|
||||
/*
|
||||
* Input/Output DIO lines are programmed in groups of 4. Their values are
|
||||
* available through 4 consecutive bits in a value port, whereas the direction
|
||||
* of these 4 lines is driven by only 1 bit in a control port.
|
||||
*/
|
||||
#define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit) \
|
||||
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit), \
|
||||
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit), \
|
||||
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit), \
|
||||
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit)
|
||||
|
||||
/*
|
||||
* TS-5500 DIO1 block
|
||||
*
|
||||
* value control dir hw
|
||||
* addr bit addr bit in out irq name pin offset
|
||||
*
|
||||
* 0x7b 0 0x7a 0 x x DIO1_0 1 0
|
||||
* 0x7b 1 0x7a 0 x x DIO1_1 3 1
|
||||
* 0x7b 2 0x7a 0 x x DIO1_2 5 2
|
||||
* 0x7b 3 0x7a 0 x x DIO1_3 7 3
|
||||
* 0x7b 4 0x7a 1 x x DIO1_4 9 4
|
||||
* 0x7b 5 0x7a 1 x x DIO1_5 11 5
|
||||
* 0x7b 6 0x7a 1 x x DIO1_6 13 6
|
||||
* 0x7b 7 0x7a 1 x x DIO1_7 15 7
|
||||
* 0x7c 0 0x7a 5 x x DIO1_8 4 8
|
||||
* 0x7c 1 0x7a 5 x x DIO1_9 6 9
|
||||
* 0x7c 2 0x7a 5 x x DIO1_10 8 10
|
||||
* 0x7c 3 0x7a 5 x x DIO1_11 10 11
|
||||
* 0x7c 4 x DIO1_12 12 12
|
||||
* 0x7c 5 x 7 DIO1_13 14 13
|
||||
*/
|
||||
static const struct ts5500_dio ts5500_dio1[] = {
|
||||
TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0),
|
||||
TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1),
|
||||
TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5),
|
||||
TS5500_DIO_IN(0x7c, 4),
|
||||
TS5500_DIO_IN_IRQ(0x7c, 5, 7),
|
||||
};
|
||||
|
||||
/*
|
||||
* TS-5500 DIO2 block
|
||||
*
|
||||
* value control dir hw
|
||||
* addr bit addr bit in out irq name pin offset
|
||||
*
|
||||
* 0x7e 0 0x7d 0 x x DIO2_0 1 0
|
||||
* 0x7e 1 0x7d 0 x x DIO2_1 3 1
|
||||
* 0x7e 2 0x7d 0 x x DIO2_2 5 2
|
||||
* 0x7e 3 0x7d 0 x x DIO2_3 7 3
|
||||
* 0x7e 4 0x7d 1 x x DIO2_4 9 4
|
||||
* 0x7e 5 0x7d 1 x x DIO2_5 11 5
|
||||
* 0x7e 6 0x7d 1 x x DIO2_6 13 6
|
||||
* 0x7e 7 0x7d 1 x x DIO2_7 15 7
|
||||
* 0x7f 0 0x7d 5 x x DIO2_8 4 8
|
||||
* 0x7f 1 0x7d 5 x x DIO2_9 6 9
|
||||
* 0x7f 2 0x7d 5 x x DIO2_10 8 10
|
||||
* 0x7f 3 0x7d 5 x x DIO2_11 10 11
|
||||
* 0x7f 4 x 6 DIO2_13 14 12
|
||||
*/
|
||||
static const struct ts5500_dio ts5500_dio2[] = {
|
||||
TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0),
|
||||
TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1),
|
||||
TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5),
|
||||
TS5500_DIO_IN_IRQ(0x7f, 4, 6),
|
||||
};
|
||||
|
||||
/*
|
||||
* TS-5500 LCD port used as DIO block
|
||||
* TS-5600 LCD port is identical
|
||||
*
|
||||
* value control dir hw
|
||||
* addr bit addr bit in out irq name pin offset
|
||||
*
|
||||
* 0x72 0 0x7d 2 x x LCD_0 8 0
|
||||
* 0x72 1 0x7d 2 x x LCD_1 7 1
|
||||
* 0x72 2 0x7d 2 x x LCD_2 10 2
|
||||
* 0x72 3 0x7d 2 x x LCD_3 9 3
|
||||
* 0x72 4 0x7d 3 x x LCD_4 12 4
|
||||
* 0x72 5 0x7d 3 x x LCD_5 11 5
|
||||
* 0x72 6 0x7d 3 x x LCD_6 14 6
|
||||
* 0x72 7 0x7d 3 x x LCD_7 13 7
|
||||
* 0x73 0 x LCD_EN 5 8
|
||||
* 0x73 6 x LCD_WR 6 9
|
||||
* 0x73 7 x 1 LCD_RS 3 10
|
||||
*/
|
||||
static const struct ts5500_dio ts5500_lcd[] = {
|
||||
TS5500_DIO_GROUP(0x72, 0, 0x7d, 2),
|
||||
TS5500_DIO_GROUP(0x72, 4, 0x7d, 3),
|
||||
TS5500_DIO_OUT(0x73, 0),
|
||||
TS5500_DIO_IN(0x73, 6),
|
||||
TS5500_DIO_IN_IRQ(0x73, 7, 1),
|
||||
};
|
||||
|
||||
static inline struct ts5500_priv *ts5500_gc_to_priv(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct ts5500_priv, gpio_chip);
|
||||
}
|
||||
|
||||
static inline void ts5500_set_mask(u8 mask, u8 addr)
|
||||
{
|
||||
u8 val = inb(addr);
|
||||
val |= mask;
|
||||
outb(val, addr);
|
||||
}
|
||||
|
||||
static inline void ts5500_clear_mask(u8 mask, u8 addr)
|
||||
{
|
||||
u8 val = inb(addr);
|
||||
val &= ~mask;
|
||||
outb(val, addr);
|
||||
}
|
||||
|
||||
static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
|
||||
const struct ts5500_dio line = priv->pinout[offset];
|
||||
unsigned long flags;
|
||||
|
||||
if (line.no_input)
|
||||
return -ENXIO;
|
||||
|
||||
if (line.no_output)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ts5500_clear_mask(line.control_mask, line.control_addr);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
|
||||
const struct ts5500_dio line = priv->pinout[offset];
|
||||
|
||||
return !!(inb(line.value_addr) & line.value_mask);
|
||||
}
|
||||
|
||||
static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val)
|
||||
{
|
||||
struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
|
||||
const struct ts5500_dio line = priv->pinout[offset];
|
||||
unsigned long flags;
|
||||
|
||||
if (line.no_output)
|
||||
return -ENXIO;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (!line.no_input)
|
||||
ts5500_set_mask(line.control_mask, line.control_addr);
|
||||
|
||||
if (val)
|
||||
ts5500_set_mask(line.value_mask, line.value_addr);
|
||||
else
|
||||
ts5500_clear_mask(line.value_mask, line.value_addr);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
|
||||
{
|
||||
struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
|
||||
const struct ts5500_dio line = priv->pinout[offset];
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (val)
|
||||
ts5500_set_mask(line.value_mask, line.value_addr);
|
||||
else
|
||||
ts5500_clear_mask(line.value_mask, line.value_addr);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
|
||||
const struct ts5500_dio *block = priv->pinout;
|
||||
const struct ts5500_dio line = block[offset];
|
||||
|
||||
/* Only one pin is connected to an interrupt */
|
||||
if (line.irq)
|
||||
return line.irq;
|
||||
|
||||
/* As this pin is input-only, we may strap it to another in/out pin */
|
||||
if (priv->strap)
|
||||
return priv->hwirq;
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int ts5500_enable_irq(struct ts5500_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (priv->hwirq == 7)
|
||||
ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
|
||||
else if (priv->hwirq == 6)
|
||||
ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
|
||||
else if (priv->hwirq == 1)
|
||||
ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
|
||||
else
|
||||
ret = -EINVAL;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ts5500_disable_irq(struct ts5500_priv *priv)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (priv->hwirq == 7)
|
||||
ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
|
||||
else if (priv->hwirq == 6)
|
||||
ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
|
||||
else if (priv->hwirq == 1)
|
||||
ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
|
||||
else
|
||||
dev_err(priv->gpio_chip.dev, "invalid hwirq %d\n", priv->hwirq);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static int __devinit ts5500_dio_probe(struct platform_device *pdev)
|
||||
{
|
||||
enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
|
||||
struct ts5500_dio_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct device *dev = &pdev->dev;
|
||||
const char *name = dev_name(dev);
|
||||
struct ts5500_priv *priv;
|
||||
struct resource *res;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing IRQ resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
priv->hwirq = res->start;
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
priv->gpio_chip.owner = THIS_MODULE;
|
||||
priv->gpio_chip.label = name;
|
||||
priv->gpio_chip.dev = dev;
|
||||
priv->gpio_chip.direction_input = ts5500_gpio_input;
|
||||
priv->gpio_chip.direction_output = ts5500_gpio_output;
|
||||
priv->gpio_chip.get = ts5500_gpio_get;
|
||||
priv->gpio_chip.set = ts5500_gpio_set;
|
||||
priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
|
||||
priv->gpio_chip.base = -1;
|
||||
if (pdata) {
|
||||
priv->gpio_chip.base = pdata->base;
|
||||
priv->strap = pdata->strap;
|
||||
}
|
||||
|
||||
switch (block) {
|
||||
case TS5500_DIO1:
|
||||
priv->pinout = ts5500_dio1;
|
||||
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1);
|
||||
|
||||
if (!devm_request_region(dev, 0x7a, 3, name)) {
|
||||
dev_err(dev, "failed to request %s ports\n", name);
|
||||
return -EBUSY;
|
||||
}
|
||||
break;
|
||||
case TS5500_DIO2:
|
||||
priv->pinout = ts5500_dio2;
|
||||
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2);
|
||||
|
||||
if (!devm_request_region(dev, 0x7e, 2, name)) {
|
||||
dev_err(dev, "failed to request %s ports\n", name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (hex7d_reserved)
|
||||
break;
|
||||
|
||||
if (!devm_request_region(dev, 0x7d, 1, name)) {
|
||||
dev_err(dev, "failed to request %s 7D\n", name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
hex7d_reserved = true;
|
||||
break;
|
||||
case TS5500_LCD:
|
||||
case TS5600_LCD:
|
||||
priv->pinout = ts5500_lcd;
|
||||
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd);
|
||||
|
||||
if (!devm_request_region(dev, 0x72, 2, name)) {
|
||||
dev_err(dev, "failed to request %s ports\n", name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!hex7d_reserved) {
|
||||
if (!devm_request_region(dev, 0x7d, 1, name)) {
|
||||
dev_err(dev, "failed to request %s 7D\n", name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
hex7d_reserved = true;
|
||||
}
|
||||
|
||||
/* Ensure usage of LCD port as DIO */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ts5500_clear_mask(BIT(4), 0x7d);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = gpiochip_add(&priv->gpio_chip);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register the gpio chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ts5500_enable_irq(priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid interrupt %d\n", priv->hwirq);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
cleanup:
|
||||
if (gpiochip_remove(&priv->gpio_chip))
|
||||
dev_err(dev, "failed to remove gpio chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ts5500_dio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ts5500_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
ts5500_disable_irq(priv);
|
||||
return gpiochip_remove(&priv->gpio_chip);
|
||||
}
|
||||
|
||||
static struct platform_device_id ts5500_dio_ids[] = {
|
||||
{ "ts5500-dio1", TS5500_DIO1 },
|
||||
{ "ts5500-dio2", TS5500_DIO2 },
|
||||
{ "ts5500-dio-lcd", TS5500_LCD },
|
||||
{ "ts5600-dio-lcd", TS5600_LCD },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, ts5500_dio_ids);
|
||||
|
||||
static struct platform_driver ts5500_dio_driver = {
|
||||
.driver = {
|
||||
.name = "ts5500-dio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ts5500_dio_probe,
|
||||
.remove = __devexit_p(ts5500_dio_remove),
|
||||
.id_table = ts5500_dio_ids,
|
||||
};
|
||||
|
||||
module_platform_driver(ts5500_dio_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
|
||||
MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver");
|
@ -88,11 +88,15 @@ static inline int gpio_twl4030_write(u8 address, u8 data)
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* LED register offsets (use TWL4030_MODULE_{LED,PWMA,PWMB}))
|
||||
* LED register offsets from TWL_MODULE_LED base
|
||||
* PWMs A and B are dedicated to LEDs A and B, respectively.
|
||||
*/
|
||||
|
||||
#define TWL4030_LED_LEDEN 0x0
|
||||
#define TWL4030_LED_LEDEN_REG 0x00
|
||||
#define TWL4030_PWMAON_REG 0x01
|
||||
#define TWL4030_PWMAOFF_REG 0x02
|
||||
#define TWL4030_PWMBON_REG 0x03
|
||||
#define TWL4030_PWMBOFF_REG 0x04
|
||||
|
||||
/* LEDEN bits */
|
||||
#define LEDEN_LEDAON BIT(0)
|
||||
@ -104,9 +108,6 @@ static inline int gpio_twl4030_write(u8 address, u8 data)
|
||||
#define LEDEN_PWM_LENGTHA BIT(6)
|
||||
#define LEDEN_PWM_LENGTHB BIT(7)
|
||||
|
||||
#define TWL4030_PWMx_PWMxON 0x0
|
||||
#define TWL4030_PWMx_PWMxOFF 0x1
|
||||
|
||||
#define PWMxON_LENGTH BIT(7)
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
@ -145,7 +146,7 @@ static void twl4030_led_set_value(int led, int value)
|
||||
else
|
||||
cached_leden |= mask;
|
||||
status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
|
||||
TWL4030_LED_LEDEN);
|
||||
TWL4030_LED_LEDEN_REG);
|
||||
mutex_unlock(&gpio_lock);
|
||||
}
|
||||
|
||||
@ -216,33 +217,33 @@ static int twl_request(struct gpio_chip *chip, unsigned offset)
|
||||
if (offset >= TWL4030_GPIO_MAX) {
|
||||
u8 ledclr_mask = LEDEN_LEDAON | LEDEN_LEDAEXT
|
||||
| LEDEN_LEDAPWM | LEDEN_PWM_LENGTHA;
|
||||
u8 module = TWL4030_MODULE_PWMA;
|
||||
u8 reg = TWL4030_PWMAON_REG;
|
||||
|
||||
offset -= TWL4030_GPIO_MAX;
|
||||
if (offset) {
|
||||
ledclr_mask <<= 1;
|
||||
module = TWL4030_MODULE_PWMB;
|
||||
reg = TWL4030_PWMBON_REG;
|
||||
}
|
||||
|
||||
/* initialize PWM to always-drive */
|
||||
status = twl_i2c_write_u8(module, 0x7f,
|
||||
TWL4030_PWMx_PWMxOFF);
|
||||
/* Configure PWM OFF register first */
|
||||
status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg + 1);
|
||||
if (status < 0)
|
||||
goto done;
|
||||
status = twl_i2c_write_u8(module, 0x7f,
|
||||
TWL4030_PWMx_PWMxON);
|
||||
|
||||
/* Followed by PWM ON register */
|
||||
status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg);
|
||||
if (status < 0)
|
||||
goto done;
|
||||
|
||||
/* init LED to not-driven (high) */
|
||||
module = TWL4030_MODULE_LED;
|
||||
status = twl_i2c_read_u8(module, &cached_leden,
|
||||
TWL4030_LED_LEDEN);
|
||||
status = twl_i2c_read_u8(TWL4030_MODULE_LED, &cached_leden,
|
||||
TWL4030_LED_LEDEN_REG);
|
||||
if (status < 0)
|
||||
goto done;
|
||||
cached_leden &= ~ledclr_mask;
|
||||
status = twl_i2c_write_u8(module, cached_leden,
|
||||
TWL4030_LED_LEDEN);
|
||||
status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
|
||||
TWL4030_LED_LEDEN_REG);
|
||||
if (status < 0)
|
||||
goto done;
|
||||
|
||||
|
@ -96,6 +96,7 @@ static struct vt8500_gpio_data wm8505_data = {
|
||||
VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12),
|
||||
VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16),
|
||||
VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22),
|
||||
VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6),
|
||||
},
|
||||
};
|
||||
|
||||
@ -115,6 +116,7 @@ static struct vt8500_gpio_data wm8650_data = {
|
||||
VT8500_BANK(0x58, 0x98, 0xD8, 0x18, 32),
|
||||
VT8500_BANK(0x5C, 0x9C, 0xDC, 0x1C, 32),
|
||||
VT8500_BANK(0x7C, 0xBC, 0xFC, 0x3C, 32),
|
||||
VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -191,6 +191,32 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* caller ensures gpio is valid and requested, chip->get_direction may sleep */
|
||||
static int gpio_get_direction(unsigned gpio)
|
||||
{
|
||||
struct gpio_chip *chip;
|
||||
struct gpio_desc *desc = &gpio_desc[gpio];
|
||||
int status = -EINVAL;
|
||||
|
||||
chip = gpio_to_chip(gpio);
|
||||
gpio -= chip->base;
|
||||
|
||||
if (!chip->get_direction)
|
||||
return status;
|
||||
|
||||
status = chip->get_direction(chip, gpio);
|
||||
if (status > 0) {
|
||||
/* GPIOF_DIR_IN, or other positive */
|
||||
status = 1;
|
||||
clear_bit(FLAG_IS_OUT, &desc->flags);
|
||||
}
|
||||
if (status == 0) {
|
||||
/* GPIOF_DIR_OUT */
|
||||
set_bit(FLAG_IS_OUT, &desc->flags);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIO_SYSFS
|
||||
|
||||
/* lock protects against unexport_gpio() being called while
|
||||
@ -223,6 +249,7 @@ static ssize_t gpio_direction_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct gpio_desc *desc = dev_get_drvdata(dev);
|
||||
unsigned gpio = desc - gpio_desc;
|
||||
ssize_t status;
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
@ -230,6 +257,7 @@ static ssize_t gpio_direction_show(struct device *dev,
|
||||
if (!test_bit(FLAG_EXPORT, &desc->flags))
|
||||
status = -EIO;
|
||||
else
|
||||
gpio_get_direction(gpio);
|
||||
status = sprintf(buf, "%s\n",
|
||||
test_bit(FLAG_IS_OUT, &desc->flags)
|
||||
? "out" : "in");
|
||||
@ -704,8 +732,9 @@ int gpio_export(unsigned gpio, bool direction_may_change)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct gpio_desc *desc;
|
||||
int status = -EINVAL;
|
||||
int status;
|
||||
const char *ioname = NULL;
|
||||
struct device *dev;
|
||||
|
||||
/* can't export until sysfs is available ... */
|
||||
if (!gpio_class.p) {
|
||||
@ -713,59 +742,66 @@ int gpio_export(unsigned gpio, bool direction_may_change)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (!gpio_is_valid(gpio))
|
||||
goto done;
|
||||
if (!gpio_is_valid(gpio)) {
|
||||
pr_debug("%s: gpio %d is not valid\n", __func__, gpio);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
desc = &gpio_desc[gpio];
|
||||
if (test_bit(FLAG_REQUESTED, &desc->flags)
|
||||
&& !test_bit(FLAG_EXPORT, &desc->flags)) {
|
||||
status = 0;
|
||||
if (!desc->chip->direction_input
|
||||
|| !desc->chip->direction_output)
|
||||
direction_may_change = false;
|
||||
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
|
||||
test_bit(FLAG_EXPORT, &desc->flags)) {
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
pr_debug("%s: gpio %d unavailable (requested=%d, exported=%d)\n",
|
||||
__func__, gpio,
|
||||
test_bit(FLAG_REQUESTED, &desc->flags),
|
||||
test_bit(FLAG_EXPORT, &desc->flags));
|
||||
status = -EPERM;
|
||||
goto fail_unlock;
|
||||
}
|
||||
|
||||
if (!desc->chip->direction_input || !desc->chip->direction_output)
|
||||
direction_may_change = false;
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
if (desc->chip->names && desc->chip->names[gpio - desc->chip->base])
|
||||
ioname = desc->chip->names[gpio - desc->chip->base];
|
||||
|
||||
if (status == 0) {
|
||||
struct device *dev;
|
||||
|
||||
dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
|
||||
desc, ioname ? ioname : "gpio%u", gpio);
|
||||
if (!IS_ERR(dev)) {
|
||||
status = sysfs_create_group(&dev->kobj,
|
||||
&gpio_attr_group);
|
||||
|
||||
if (!status && direction_may_change)
|
||||
status = device_create_file(dev,
|
||||
&dev_attr_direction);
|
||||
|
||||
if (!status && gpio_to_irq(gpio) >= 0
|
||||
&& (direction_may_change
|
||||
|| !test_bit(FLAG_IS_OUT,
|
||||
&desc->flags)))
|
||||
status = device_create_file(dev,
|
||||
&dev_attr_edge);
|
||||
|
||||
if (status != 0)
|
||||
device_unregister(dev);
|
||||
} else
|
||||
status = PTR_ERR(dev);
|
||||
if (status == 0)
|
||||
set_bit(FLAG_EXPORT, &desc->flags);
|
||||
dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
|
||||
desc, ioname ? ioname : "gpio%u", gpio);
|
||||
if (IS_ERR(dev)) {
|
||||
status = PTR_ERR(dev);
|
||||
goto fail_unlock;
|
||||
}
|
||||
|
||||
mutex_unlock(&sysfs_lock);
|
||||
|
||||
done:
|
||||
status = sysfs_create_group(&dev->kobj, &gpio_attr_group);
|
||||
if (status)
|
||||
pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
|
||||
goto fail_unregister_device;
|
||||
|
||||
if (direction_may_change) {
|
||||
status = device_create_file(dev, &dev_attr_direction);
|
||||
if (status)
|
||||
goto fail_unregister_device;
|
||||
}
|
||||
|
||||
if (gpio_to_irq(gpio) >= 0 && (direction_may_change ||
|
||||
!test_bit(FLAG_IS_OUT, &desc->flags))) {
|
||||
status = device_create_file(dev, &dev_attr_edge);
|
||||
if (status)
|
||||
goto fail_unregister_device;
|
||||
}
|
||||
|
||||
set_bit(FLAG_EXPORT, &desc->flags);
|
||||
mutex_unlock(&sysfs_lock);
|
||||
return 0;
|
||||
|
||||
fail_unregister_device:
|
||||
device_unregister(dev);
|
||||
fail_unlock:
|
||||
mutex_unlock(&sysfs_lock);
|
||||
pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpio_export);
|
||||
@ -1075,6 +1111,7 @@ int gpiochip_add(struct gpio_chip *chip)
|
||||
* inputs (often with pullups enabled) so power
|
||||
* usage is minimized. Linux code should set the
|
||||
* gpio direction first thing; but until it does,
|
||||
* and in case chip->get_direction is not set,
|
||||
* we may expose the wrong direction in sysfs.
|
||||
*/
|
||||
gpio_desc[id].flags = !chip->direction_input
|
||||
@ -1304,9 +1341,15 @@ int gpio_request(unsigned gpio, const char *label)
|
||||
desc_set_label(desc, NULL);
|
||||
module_put(chip->owner);
|
||||
clear_bit(FLAG_REQUESTED, &desc->flags);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (chip->get_direction) {
|
||||
/* chip->get_direction may sleep */
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
gpio_get_direction(gpio);
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
}
|
||||
done:
|
||||
if (status)
|
||||
pr_debug("gpio_request: gpio-%d (%s) status %d\n",
|
||||
@ -1842,6 +1885,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||
if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
|
||||
continue;
|
||||
|
||||
gpio_get_direction(gpio);
|
||||
is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
|
||||
seq_printf(s, " gpio-%-3d (%-20.20s) %s %s",
|
||||
gpio, gdesc->label,
|
||||
|
@ -294,12 +294,14 @@ static struct resource stmpe_gpio_resources[] = {
|
||||
|
||||
static struct mfd_cell stmpe_gpio_cell = {
|
||||
.name = "stmpe-gpio",
|
||||
.of_compatible = "st,stmpe-gpio",
|
||||
.resources = stmpe_gpio_resources,
|
||||
.num_resources = ARRAY_SIZE(stmpe_gpio_resources),
|
||||
};
|
||||
|
||||
static struct mfd_cell stmpe_gpio_cell_noirq = {
|
||||
.name = "stmpe-gpio",
|
||||
.of_compatible = "st,stmpe-gpio",
|
||||
/* gpio cell resources consist of an irq only so no resources here */
|
||||
};
|
||||
|
||||
|
@ -57,6 +57,8 @@ struct device_node;
|
||||
* enabling module power and clock; may sleep
|
||||
* @free: optional hook for chip-specific deactivation, such as
|
||||
* disabling module power and clock; may sleep
|
||||
* @get_direction: returns direction for signal "offset", 0=out, 1=in,
|
||||
* (same as GPIOF_DIR_XXX), or negative error
|
||||
* @direction_input: configures signal "offset" as input, or returns error
|
||||
* @get: returns value for signal "offset"; for output signals this
|
||||
* returns either the value actually sensed, or zero
|
||||
@ -101,7 +103,8 @@ struct gpio_chip {
|
||||
unsigned offset);
|
||||
void (*free)(struct gpio_chip *chip,
|
||||
unsigned offset);
|
||||
|
||||
int (*get_direction)(struct gpio_chip *chip,
|
||||
unsigned offset);
|
||||
int (*direction_input)(struct gpio_chip *chip,
|
||||
unsigned offset);
|
||||
int (*get)(struct gpio_chip *chip,
|
||||
|
@ -10,7 +10,6 @@
|
||||
* @setup: optional callback issued once the GPIOs are valid
|
||||
* @teardown: optional callback issued before the GPIOs are invalidated
|
||||
* @context: optional parameter passed to setup() and teardown()
|
||||
* @irq: optional interrupt number
|
||||
*
|
||||
* In addition to the I2C_BOARD_INFO() state appropriate to each chip,
|
||||
* the i2c_board_info used with the pcf875x driver must provide its
|
||||
@ -40,8 +39,6 @@ struct pcf857x_platform_data {
|
||||
int gpio, unsigned ngpio,
|
||||
void *context);
|
||||
void *context;
|
||||
|
||||
int irq;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_PCF857X_H */
|
||||
|
27
include/linux/platform_data/gpio-ts5500.h
Normal file
27
include/linux/platform_data/gpio-ts5500.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* GPIO (DIO) header for Technologic Systems TS-5500
|
||||
*
|
||||
* Copyright (c) 2012 Savoir-faire Linux Inc.
|
||||
* Vivien Didelot <vivien.didelot@savoirfairelinux.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.
|
||||
*/
|
||||
|
||||
#ifndef _PDATA_GPIO_TS5500_H
|
||||
#define _PDATA_GPIO_TS5500_H
|
||||
|
||||
/**
|
||||
* struct ts5500_dio_platform_data - TS-5500 pin block configuration
|
||||
* @base: The GPIO base number to use.
|
||||
* @strap: The only pin connected to an interrupt in a block is input-only.
|
||||
* If you need a bidirectional line which can trigger an IRQ, you
|
||||
* may strap it with an in/out pin. This flag indicates this case.
|
||||
*/
|
||||
struct ts5500_dio_platform_data {
|
||||
int base;
|
||||
bool strap;
|
||||
};
|
||||
|
||||
#endif /* _PDATA_GPIO_TS5500_H */
|
Loading…
Reference in New Issue
Block a user