So this is the LW GPIO patch stack for v3.7:
- refactoring from Thierry Redding at Arnd Bergmann's request to use the seq_file iterator interface in gpiolib. - A new driver for Avionic Design's N-bit GPIO expander. - Two instances of mutexes replaced by spinlocks from Axel Lin to code that is supposed to be fastpath compliant. - IRQ demuxer and gpio_to_irq() support for pcf857x by Kuninori Morimoto. - Dynamic GPIO numbers, device tree support, daisy chaining and some other fixes for the 74x164 driver by Maxime Ripard. - IRQ domain and device tree support for the tc3589x driver by Lee Jones. - Some conversion to use managed resources devm_* code. - Some instances of clk_prepare() or clk_prepare_enable() added to support the new, stricter common clock framework. - Some for_each_set_bit() simplifications. - Then a lot of fixes as we fixed up all of the above tripping over our own shoelaces and that kind of thing. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJQafMcAAoJEEEQszewGV1znvsP/2CZusf05+XiCHZO7LGsBo+h WrVNB/XxrpgW7EEqpTPJCh2leM/hXs1uOYoQq0v8ICEJLwdIox8jYsnPL7NUWpN4 mcM9YCqH65Ljt07Ec25bTRzkIy881nYfvK7nUo7DZH8sq2eX53Vxqkr/IS3ZKQVj T1Kd7GHmfje2FrnL5O0owT3zNHE9VmHm1Ct9DRCRP/U2i8CSAFERJgBsoslrut13 Cnvkvwbj9Q2LQy+kIBt3PIlKb37u1Uucqa8uExvMV9cSAzG7X9h++wSgm2RioSsR mYrCRn19qzz7EATh4yUKHe56mx0KNl+/0vapqBuziTy+r0oi40VEoiSzKyfSnhFy MYgiVVnWIH9dhOTP/0QSbrYsPEeT/ZlcJKp6uu1o1MR6z3f3058Sc6FTCzeGcW88 Ayh0kT0e8iLsQ+tRmjFEEEALLKSL9Q7StH2az7Awkve3L9JCVUVXaJwynjcSIC29 8sbAV+ENTYaKOGV8uMRH+s/WpKN6w9G2gZ/qhlXem9r4/Rd529wGBlH1or8A7uLf cNuaSquN3TM5O1i26K3+rEKp5Sd7+RIMpQCMsuiEpQd1UnbB2Z1X4xjjLYwKqHFj fVQtu7s7tQ/o/hYzTn9hP4Cqj4SDB+EaDmAqq0yJH2DNsUtvLLug62ekatvvejc/ 6HFuiVOYkigSRbl7sDoV =jbUM -----END PGP SIGNATURE----- Merge tag 'gpio-for-v3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio Pull GPIO changes from Linus Walleij: "So this is the LW GPIO patch stack for v3.7: - refactoring from Thierry Redding at Arnd Bergmann's request to use the seq_file iterator interface in gpiolib. - A new driver for Avionic Design's N-bit GPIO expander. - Two instances of mutexes replaced by spinlocks from Axel Lin to code that is supposed to be fastpath compliant. - IRQ demuxer and gpio_to_irq() support for pcf857x by Kuninori Morimoto. - Dynamic GPIO numbers, device tree support, daisy chaining and some other fixes for the 74x164 driver by Maxime Ripard. - IRQ domain and device tree support for the tc3589x driver by Lee Jones. - Some conversion to use managed resources devm_* code. - Some instances of clk_prepare() or clk_prepare_enable() added to support the new, stricter common clock framework. - Some for_each_set_bit() simplifications. - Then a lot of fixes as we fixed up all of the above tripping over our own shoelaces and that kind of thing." * tag 'gpio-for-v3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (34 commits) gpio: pcf857x: select IRQ_DOMAIN gpio: Document device_node's det_debounce gpio-lpc32xx: Add GPI_28 gpio: adnp: dt: Reference generic interrupt binding gpio: Add Avionic Design N-bit GPIO expander support gpio: pxa: using for_each_set_bit to simplify the code gpio_msm: using for_each_set_bit to simplify the code gpio: Enable the tc3298x GPIO expander driver for Device Tree gpio: Provide the tc3589x GPIO expander driver with an IRQ domain ARM: shmobile: kzm9g: use gpio-keys instead of gpio-keys-polled gpio: pcf857x: fixup smatch WARNING gpio: 74x164: Add support for the daisy-chaining gpio: 74x164: dts: Add documentation for the dt binding dt: Fix incorrect reference in gpio-led documentation gpio: 74x164: Add device tree support gpio: 74x164: Use dynamic gpio number assignment if no pdata is present gpio: 74x164: Use devm_kzalloc gpio: 74x164: Use module_spi_driver boiler plate function gpio: sx150x: Use irq_data_get_irq_chip_data() at appropriate places gpio: em: Use irq_data_get_irq_chip_data() at appropriate places ...
This commit is contained in:
commit
dff8360a4a
|
@ -0,0 +1,22 @@
|
||||||
|
* Generic 8-bits shift register GPIO driver
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : Should be "fairchild,74hc595"
|
||||||
|
- reg : chip select number
|
||||||
|
- gpio-controller : Marks the device node as a gpio controller.
|
||||||
|
- #gpio-cells : Should be two. The first cell is the pin number and
|
||||||
|
the second cell is used to specify the gpio polarity:
|
||||||
|
0 = active high
|
||||||
|
1 = active low
|
||||||
|
- registers-number: Number of daisy-chained shift registers
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
gpio5: gpio5@0 {
|
||||||
|
compatible = "fairchild,74hc595";
|
||||||
|
reg = <0>;
|
||||||
|
gpio-controller;
|
||||||
|
#gpio-cells = <2>;
|
||||||
|
registers-number = <4>;
|
||||||
|
spi-max-frequency = <100000>;
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
Avionic Design N-bit GPIO expander bindings
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be "ad,gpio-adnp"
|
||||||
|
- reg: The I2C slave address for this device.
|
||||||
|
- interrupt-parent: phandle of the parent interrupt controller.
|
||||||
|
- interrupts: Interrupt specifier for the controllers interrupt.
|
||||||
|
- #gpio-cells: Should be 2. The first cell is the GPIO number and the
|
||||||
|
second cell is used to specify optional parameters:
|
||||||
|
- bit 0: polarity (0: normal, 1: inverted)
|
||||||
|
- gpio-controller: Marks the device as a GPIO controller
|
||||||
|
- nr-gpios: The number of pins supported by the controller.
|
||||||
|
|
||||||
|
The GPIO expander can optionally be used as an interrupt controller, in
|
||||||
|
which case it uses the default two cell specifier as described in
|
||||||
|
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
gpioext: gpio-controller@41 {
|
||||||
|
compatible = "ad,gpio-adnp";
|
||||||
|
reg = <0x41>;
|
||||||
|
|
||||||
|
interrupt-parent = <&gpio>;
|
||||||
|
interrupts = <160 1>;
|
||||||
|
|
||||||
|
gpio-controller;
|
||||||
|
#gpio-cells = <2>;
|
||||||
|
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <2>;
|
||||||
|
|
||||||
|
nr-gpios = <64>;
|
||||||
|
};
|
|
@ -8,7 +8,7 @@ node's name represents the name of the corresponding LED.
|
||||||
|
|
||||||
LED sub-node properties:
|
LED sub-node properties:
|
||||||
- gpios : Should specify the LED's GPIO, see "gpios property" in
|
- gpios : Should specify the LED's GPIO, see "gpios property" in
|
||||||
Documentation/devicetree/gpio.txt. Active low LEDs should be
|
Documentation/devicetree/bindings/gpio/gpio.txt. Active low LEDs should be
|
||||||
indicated using flags in the GPIO specifier.
|
indicated using flags in the GPIO specifier.
|
||||||
- label : (optional) The label for this LED. If omitted, the label is
|
- label : (optional) The label for this LED. If omitted, the label is
|
||||||
taken from the node name (excluding the unit address).
|
taken from the node name (excluding the unit address).
|
||||||
|
|
|
@ -23,7 +23,6 @@ CONFIG_MODULE_UNLOAD=y
|
||||||
# CONFIG_IOSCHED_DEADLINE is not set
|
# CONFIG_IOSCHED_DEADLINE is not set
|
||||||
# CONFIG_IOSCHED_CFQ is not set
|
# CONFIG_IOSCHED_CFQ is not set
|
||||||
CONFIG_ARCH_SHMOBILE=y
|
CONFIG_ARCH_SHMOBILE=y
|
||||||
CONFIG_KEYBOARD_GPIO_POLLED=y
|
|
||||||
CONFIG_ARCH_SH73A0=y
|
CONFIG_ARCH_SH73A0=y
|
||||||
CONFIG_MACH_KZM9G=y
|
CONFIG_MACH_KZM9G=y
|
||||||
CONFIG_MEMORY_START=0x41000000
|
CONFIG_MEMORY_START=0x41000000
|
||||||
|
@ -71,6 +70,7 @@ CONFIG_INPUT_SPARSEKMAP=y
|
||||||
# CONFIG_INPUT_MOUSEDEV is not set
|
# CONFIG_INPUT_MOUSEDEV is not set
|
||||||
CONFIG_INPUT_EVDEV=y
|
CONFIG_INPUT_EVDEV=y
|
||||||
# CONFIG_KEYBOARD_ATKBD is not set
|
# CONFIG_KEYBOARD_ATKBD is not set
|
||||||
|
CONFIG_KEYBOARD_GPIO=y
|
||||||
# CONFIG_INPUT_MOUSE is not set
|
# CONFIG_INPUT_MOUSE is not set
|
||||||
CONFIG_INPUT_TOUCHSCREEN=y
|
CONFIG_INPUT_TOUCHSCREEN=y
|
||||||
CONFIG_TOUCHSCREEN_ST1232=y
|
CONFIG_TOUCHSCREEN_ST1232=y
|
||||||
|
|
|
@ -482,12 +482,10 @@ static struct gpio_keys_button gpio_buttons[] = {
|
||||||
static struct gpio_keys_platform_data gpio_key_info = {
|
static struct gpio_keys_platform_data gpio_key_info = {
|
||||||
.buttons = gpio_buttons,
|
.buttons = gpio_buttons,
|
||||||
.nbuttons = ARRAY_SIZE(gpio_buttons),
|
.nbuttons = ARRAY_SIZE(gpio_buttons),
|
||||||
.poll_interval = 250, /* poling at this point */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device gpio_keys_device = {
|
static struct platform_device gpio_keys_device = {
|
||||||
/* gpio-pcf857x.c driver doesn't support gpio_to_irq() */
|
.name = "gpio-keys",
|
||||||
.name = "gpio-keys-polled",
|
|
||||||
.dev = {
|
.dev = {
|
||||||
.platform_data = &gpio_key_info,
|
.platform_data = &gpio_key_info,
|
||||||
},
|
},
|
||||||
|
@ -550,6 +548,7 @@ static struct platform_device fsi_ak4648_device = {
|
||||||
/* I2C */
|
/* I2C */
|
||||||
static struct pcf857x_platform_data pcf8575_pdata = {
|
static struct pcf857x_platform_data pcf8575_pdata = {
|
||||||
.gpio_base = GPIO_PCF8575_BASE,
|
.gpio_base = GPIO_PCF8575_BASE,
|
||||||
|
.irq = intcs_evt2irq(0x3260), /* IRQ19 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct i2c_board_info i2c0_devices[] = {
|
static struct i2c_board_info i2c0_devices[] = {
|
||||||
|
|
|
@ -82,7 +82,7 @@ config GPIO_GENERIC
|
||||||
|
|
||||||
config GPIO_DA9052
|
config GPIO_DA9052
|
||||||
tristate "Dialog DA9052 GPIO"
|
tristate "Dialog DA9052 GPIO"
|
||||||
depends on PMIC_DA9052 && BROKEN
|
depends on PMIC_DA9052
|
||||||
help
|
help
|
||||||
Say yes here to enable the GPIO driver for the DA9052 chip.
|
Say yes here to enable the GPIO driver for the DA9052 chip.
|
||||||
|
|
||||||
|
@ -330,6 +330,7 @@ config GPIO_PCA953X_IRQ
|
||||||
config GPIO_PCF857X
|
config GPIO_PCF857X
|
||||||
tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
|
tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
select IRQ_DOMAIN
|
||||||
help
|
help
|
||||||
Say yes here to provide access to most "quasi-bidirectional" I2C
|
Say yes here to provide access to most "quasi-bidirectional" I2C
|
||||||
GPIO expanders used for additional digital outputs or inputs.
|
GPIO expanders used for additional digital outputs or inputs.
|
||||||
|
@ -450,6 +451,17 @@ config GPIO_ADP5588_IRQ
|
||||||
Say yes here to enable the adp5588 to be used as an interrupt
|
Say yes here to enable the adp5588 to be used as an interrupt
|
||||||
controller. It requires the driver to be built in the kernel.
|
controller. It requires the driver to be built in the kernel.
|
||||||
|
|
||||||
|
config GPIO_ADNP
|
||||||
|
tristate "Avionic Design N-bit GPIO expander"
|
||||||
|
depends on I2C && OF
|
||||||
|
help
|
||||||
|
This option enables support for N GPIOs found on Avionic Design
|
||||||
|
I2C GPIO expanders. The register space will be extended by powers
|
||||||
|
of two, so the controller will need to accomodate for that. For
|
||||||
|
example: if a controller provides 48 pins, 6 registers will be
|
||||||
|
enough to represent all pins, but the driver will assume a
|
||||||
|
register layout for 64 pins (8 registers).
|
||||||
|
|
||||||
comment "PCI GPIO expanders:"
|
comment "PCI GPIO expanders:"
|
||||||
|
|
||||||
config GPIO_CS5535
|
config GPIO_CS5535
|
||||||
|
|
|
@ -10,6 +10,7 @@ obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
|
||||||
|
|
||||||
obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
|
obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
|
||||||
obj-$(CONFIG_GPIO_AB8500) += gpio-ab8500.o
|
obj-$(CONFIG_GPIO_AB8500) += gpio-ab8500.o
|
||||||
|
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
|
||||||
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
|
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
|
||||||
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
|
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
|
||||||
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
|
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
|
||||||
|
|
|
@ -14,14 +14,18 @@
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/74x164.h>
|
#include <linux/spi/74x164.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
#define GEN_74X164_NUMBER_GPIOS 8
|
||||||
|
|
||||||
struct gen_74x164_chip {
|
struct gen_74x164_chip {
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
|
u8 *buffer;
|
||||||
struct gpio_chip gpio_chip;
|
struct gpio_chip gpio_chip;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
u8 port_config;
|
u32 registers;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
|
static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
|
||||||
|
@ -31,17 +35,47 @@ static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
|
||||||
|
|
||||||
static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
|
static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
|
||||||
{
|
{
|
||||||
return spi_write(chip->spi,
|
struct spi_message message;
|
||||||
&chip->port_config, sizeof(chip->port_config));
|
struct spi_transfer *msg_buf;
|
||||||
|
int i, ret = 0;
|
||||||
|
|
||||||
|
msg_buf = kzalloc(chip->registers * sizeof(struct spi_transfer),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!msg_buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spi_message_init(&message);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since the registers are chained, every byte sent will make
|
||||||
|
* the previous byte shift to the next register in the
|
||||||
|
* chain. Thus, the first byte send will end up in the last
|
||||||
|
* register at the end of the transfer. So, to have a logical
|
||||||
|
* numbering, send the bytes in reverse order so that the last
|
||||||
|
* byte of the buffer will end up in the last register.
|
||||||
|
*/
|
||||||
|
for (i = chip->registers - 1; i >= 0; i--) {
|
||||||
|
msg_buf[i].tx_buf = chip->buffer +i;
|
||||||
|
msg_buf[i].len = sizeof(u8);
|
||||||
|
spi_message_add_tail(msg_buf + i, &message);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = spi_sync(chip->spi, &message);
|
||||||
|
|
||||||
|
kfree(msg_buf);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
|
static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
|
||||||
{
|
{
|
||||||
struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
|
struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
|
||||||
|
u8 bank = offset / 8;
|
||||||
|
u8 pin = offset % 8;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
mutex_lock(&chip->lock);
|
||||||
ret = (chip->port_config >> offset) & 0x1;
|
ret = (chip->buffer[bank] >> pin) & 0x1;
|
||||||
mutex_unlock(&chip->lock);
|
mutex_unlock(&chip->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -51,12 +85,14 @@ static void gen_74x164_set_value(struct gpio_chip *gc,
|
||||||
unsigned offset, int val)
|
unsigned offset, int val)
|
||||||
{
|
{
|
||||||
struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
|
struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
|
||||||
|
u8 bank = offset / 8;
|
||||||
|
u8 pin = offset % 8;
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
mutex_lock(&chip->lock);
|
||||||
if (val)
|
if (val)
|
||||||
chip->port_config |= (1 << offset);
|
chip->buffer[bank] |= (1 << pin);
|
||||||
else
|
else
|
||||||
chip->port_config &= ~(1 << offset);
|
chip->buffer[bank] &= ~(1 << pin);
|
||||||
|
|
||||||
__gen_74x164_write_config(chip);
|
__gen_74x164_write_config(chip);
|
||||||
mutex_unlock(&chip->lock);
|
mutex_unlock(&chip->lock);
|
||||||
|
@ -75,9 +111,8 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
|
||||||
struct gen_74x164_chip_platform_data *pdata;
|
struct gen_74x164_chip_platform_data *pdata;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pdata = spi->dev.platform_data;
|
if (!spi->dev.of_node) {
|
||||||
if (!pdata || !pdata->base) {
|
dev_err(&spi->dev, "No device tree data available.\n");
|
||||||
dev_dbg(&spi->dev, "incorrect or missing platform data\n");
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +125,16 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL);
|
||||||
if (!chip)
|
if (!chip)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pdata = spi->dev.platform_data;
|
||||||
|
if (pdata && pdata->base)
|
||||||
|
chip->gpio_chip.base = pdata->base;
|
||||||
|
else
|
||||||
|
chip->gpio_chip.base = -1;
|
||||||
|
|
||||||
mutex_init(&chip->lock);
|
mutex_init(&chip->lock);
|
||||||
|
|
||||||
dev_set_drvdata(&spi->dev, chip);
|
dev_set_drvdata(&spi->dev, chip);
|
||||||
|
@ -104,8 +145,20 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
|
||||||
chip->gpio_chip.direction_output = gen_74x164_direction_output;
|
chip->gpio_chip.direction_output = gen_74x164_direction_output;
|
||||||
chip->gpio_chip.get = gen_74x164_get_value;
|
chip->gpio_chip.get = gen_74x164_get_value;
|
||||||
chip->gpio_chip.set = gen_74x164_set_value;
|
chip->gpio_chip.set = gen_74x164_set_value;
|
||||||
chip->gpio_chip.base = pdata->base;
|
|
||||||
chip->gpio_chip.ngpio = 8;
|
if (of_property_read_u32(spi->dev.of_node, "registers-number", &chip->registers)) {
|
||||||
|
dev_err(&spi->dev, "Missing registers-number property in the DT.\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit_destroy;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;
|
||||||
|
chip->buffer = devm_kzalloc(&spi->dev, chip->gpio_chip.ngpio, GFP_KERNEL);
|
||||||
|
if (!chip->buffer) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto exit_destroy;
|
||||||
|
}
|
||||||
|
|
||||||
chip->gpio_chip.can_sleep = 1;
|
chip->gpio_chip.can_sleep = 1;
|
||||||
chip->gpio_chip.dev = &spi->dev;
|
chip->gpio_chip.dev = &spi->dev;
|
||||||
chip->gpio_chip.owner = THIS_MODULE;
|
chip->gpio_chip.owner = THIS_MODULE;
|
||||||
|
@ -125,7 +178,6 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
|
||||||
exit_destroy:
|
exit_destroy:
|
||||||
dev_set_drvdata(&spi->dev, NULL);
|
dev_set_drvdata(&spi->dev, NULL);
|
||||||
mutex_destroy(&chip->lock);
|
mutex_destroy(&chip->lock);
|
||||||
kfree(chip);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,36 +193,31 @@ static int __devexit gen_74x164_remove(struct spi_device *spi)
|
||||||
dev_set_drvdata(&spi->dev, NULL);
|
dev_set_drvdata(&spi->dev, NULL);
|
||||||
|
|
||||||
ret = gpiochip_remove(&chip->gpio_chip);
|
ret = gpiochip_remove(&chip->gpio_chip);
|
||||||
if (!ret) {
|
if (!ret)
|
||||||
mutex_destroy(&chip->lock);
|
mutex_destroy(&chip->lock);
|
||||||
kfree(chip);
|
else
|
||||||
} else
|
|
||||||
dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n",
|
dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n",
|
||||||
ret);
|
ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id gen_74x164_dt_ids[] = {
|
||||||
|
{ .compatible = "fairchild,74hc595" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, gen_74x164_dt_ids);
|
||||||
|
|
||||||
static struct spi_driver gen_74x164_driver = {
|
static struct spi_driver gen_74x164_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "74x164",
|
.name = "74x164",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = of_match_ptr(gen_74x164_dt_ids),
|
||||||
},
|
},
|
||||||
.probe = gen_74x164_probe,
|
.probe = gen_74x164_probe,
|
||||||
.remove = __devexit_p(gen_74x164_remove),
|
.remove = __devexit_p(gen_74x164_remove),
|
||||||
};
|
};
|
||||||
|
module_spi_driver(gen_74x164_driver);
|
||||||
static int __init gen_74x164_init(void)
|
|
||||||
{
|
|
||||||
return spi_register_driver(&gen_74x164_driver);
|
|
||||||
}
|
|
||||||
subsys_initcall(gen_74x164_init);
|
|
||||||
|
|
||||||
static void __exit gen_74x164_exit(void)
|
|
||||||
{
|
|
||||||
spi_unregister_driver(&gen_74x164_driver);
|
|
||||||
}
|
|
||||||
module_exit(gen_74x164_exit);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
|
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
|
||||||
MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
|
MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
|
||||||
|
|
|
@ -0,0 +1,611 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2012 Avionic Design GmbH
|
||||||
|
*
|
||||||
|
* 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/gpio.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define GPIO_DDR(gpio) (0x00 << (gpio)->reg_shift)
|
||||||
|
#define GPIO_PLR(gpio) (0x01 << (gpio)->reg_shift)
|
||||||
|
#define GPIO_IER(gpio) (0x02 << (gpio)->reg_shift)
|
||||||
|
#define GPIO_ISR(gpio) (0x03 << (gpio)->reg_shift)
|
||||||
|
#define GPIO_PTR(gpio) (0x04 << (gpio)->reg_shift)
|
||||||
|
|
||||||
|
struct adnp {
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct gpio_chip gpio;
|
||||||
|
unsigned int reg_shift;
|
||||||
|
|
||||||
|
struct mutex i2c_lock;
|
||||||
|
|
||||||
|
struct irq_domain *domain;
|
||||||
|
struct mutex irq_lock;
|
||||||
|
|
||||||
|
u8 *irq_enable;
|
||||||
|
u8 *irq_level;
|
||||||
|
u8 *irq_rise;
|
||||||
|
u8 *irq_fall;
|
||||||
|
u8 *irq_high;
|
||||||
|
u8 *irq_low;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct adnp *to_adnp(struct gpio_chip *chip)
|
||||||
|
{
|
||||||
|
return container_of(chip, struct adnp, gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adnp_read(struct adnp *adnp, unsigned offset, uint8_t *value)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = i2c_smbus_read_byte_data(adnp->client, offset);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(adnp->gpio.dev, "%s failed: %d\n",
|
||||||
|
"i2c_smbus_read_byte_data()", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = err;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adnp_write(struct adnp *adnp, unsigned offset, uint8_t value)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = i2c_smbus_write_byte_data(adnp->client, offset, value);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(adnp->gpio.dev, "%s failed: %d\n",
|
||||||
|
"i2c_smbus_write_byte_data()", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adnp_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = to_adnp(chip);
|
||||||
|
unsigned int reg = offset >> adnp->reg_shift;
|
||||||
|
unsigned int pos = offset & 7;
|
||||||
|
u8 value;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &value);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return (value & BIT(pos)) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __adnp_gpio_set(struct adnp *adnp, unsigned offset, int value)
|
||||||
|
{
|
||||||
|
unsigned int reg = offset >> adnp->reg_shift;
|
||||||
|
unsigned int pos = offset & 7;
|
||||||
|
int err;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &val);
|
||||||
|
if (err < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
val |= BIT(pos);
|
||||||
|
else
|
||||||
|
val &= ~BIT(pos);
|
||||||
|
|
||||||
|
adnp_write(adnp, GPIO_PLR(adnp) + reg, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adnp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = to_adnp(chip);
|
||||||
|
|
||||||
|
mutex_lock(&adnp->i2c_lock);
|
||||||
|
__adnp_gpio_set(adnp, offset, value);
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = to_adnp(chip);
|
||||||
|
unsigned int reg = offset >> adnp->reg_shift;
|
||||||
|
unsigned int pos = offset & 7;
|
||||||
|
u8 value;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&adnp->i2c_lock);
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
value &= ~BIT(pos);
|
||||||
|
|
||||||
|
err = adnp_write(adnp, GPIO_DDR(adnp) + reg, value);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (err & BIT(pos))
|
||||||
|
err = -EACCES;
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adnp_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
|
||||||
|
int value)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = to_adnp(chip);
|
||||||
|
unsigned int reg = offset >> adnp->reg_shift;
|
||||||
|
unsigned int pos = offset & 7;
|
||||||
|
int err;
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
mutex_lock(&adnp->i2c_lock);
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
val |= BIT(pos);
|
||||||
|
|
||||||
|
err = adnp_write(adnp, GPIO_DDR(adnp) + reg, val);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!(val & BIT(pos))) {
|
||||||
|
err = -EPERM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
__adnp_gpio_set(adnp, offset, value);
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = to_adnp(chip);
|
||||||
|
unsigned int num_regs = 1 << adnp->reg_shift, i, j;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
for (i = 0; i < num_regs; i++) {
|
||||||
|
u8 ddr, plr, ier, isr;
|
||||||
|
|
||||||
|
mutex_lock(&adnp->i2c_lock);
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_DDR(adnp) + i, &ddr);
|
||||||
|
if (err < 0) {
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_PLR(adnp) + i, &plr);
|
||||||
|
if (err < 0) {
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier);
|
||||||
|
if (err < 0) {
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr);
|
||||||
|
if (err < 0) {
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
unsigned int bit = (i << adnp->reg_shift) + j;
|
||||||
|
const char *direction = "input ";
|
||||||
|
const char *level = "low ";
|
||||||
|
const char *interrupt = "disabled";
|
||||||
|
const char *pending = "";
|
||||||
|
|
||||||
|
if (ddr & BIT(j))
|
||||||
|
direction = "output";
|
||||||
|
|
||||||
|
if (plr & BIT(j))
|
||||||
|
level = "high";
|
||||||
|
|
||||||
|
if (ier & BIT(j))
|
||||||
|
interrupt = "enabled ";
|
||||||
|
|
||||||
|
if (isr & BIT(j))
|
||||||
|
pending = "pending";
|
||||||
|
|
||||||
|
seq_printf(s, "%2u: %s %s IRQ %s %s\n", bit,
|
||||||
|
direction, level, interrupt, pending);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios)
|
||||||
|
{
|
||||||
|
struct gpio_chip *chip = &adnp->gpio;
|
||||||
|
|
||||||
|
adnp->reg_shift = get_count_order(num_gpios) - 3;
|
||||||
|
|
||||||
|
chip->direction_input = adnp_gpio_direction_input;
|
||||||
|
chip->direction_output = adnp_gpio_direction_output;
|
||||||
|
chip->get = adnp_gpio_get;
|
||||||
|
chip->set = adnp_gpio_set;
|
||||||
|
chip->can_sleep = 1;
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_DEBUG_FS))
|
||||||
|
chip->dbg_show = adnp_gpio_dbg_show;
|
||||||
|
|
||||||
|
chip->base = -1;
|
||||||
|
chip->ngpio = num_gpios;
|
||||||
|
chip->label = adnp->client->name;
|
||||||
|
chip->dev = &adnp->client->dev;
|
||||||
|
chip->of_node = chip->dev->of_node;
|
||||||
|
chip->owner = THIS_MODULE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t adnp_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = data;
|
||||||
|
unsigned int num_regs, i;
|
||||||
|
|
||||||
|
num_regs = 1 << adnp->reg_shift;
|
||||||
|
|
||||||
|
for (i = 0; i < num_regs; i++) {
|
||||||
|
unsigned int base = i << adnp->reg_shift, bit;
|
||||||
|
u8 changed, level, isr, ier;
|
||||||
|
unsigned long pending;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&adnp->i2c_lock);
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level);
|
||||||
|
if (err < 0) {
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr);
|
||||||
|
if (err < 0) {
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier);
|
||||||
|
if (err < 0) {
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
|
||||||
|
/* determine pins that changed levels */
|
||||||
|
changed = level ^ adnp->irq_level[i];
|
||||||
|
|
||||||
|
/* compute edge-triggered interrupts */
|
||||||
|
pending = changed & ((adnp->irq_fall[i] & ~level) |
|
||||||
|
(adnp->irq_rise[i] & level));
|
||||||
|
|
||||||
|
/* add in level-triggered interrupts */
|
||||||
|
pending |= (adnp->irq_high[i] & level) |
|
||||||
|
(adnp->irq_low[i] & ~level);
|
||||||
|
|
||||||
|
/* mask out non-pending and disabled interrupts */
|
||||||
|
pending &= isr & ier;
|
||||||
|
|
||||||
|
for_each_set_bit(bit, &pending, 8) {
|
||||||
|
unsigned int virq;
|
||||||
|
virq = irq_find_mapping(adnp->domain, base + bit);
|
||||||
|
handle_nested_irq(virq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adnp_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = to_adnp(chip);
|
||||||
|
return irq_create_mapping(adnp->domain, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adnp_irq_mask(struct irq_data *data)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = irq_data_get_irq_chip_data(data);
|
||||||
|
unsigned int reg = data->hwirq >> adnp->reg_shift;
|
||||||
|
unsigned int pos = data->hwirq & 7;
|
||||||
|
|
||||||
|
adnp->irq_enable[reg] &= ~BIT(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adnp_irq_unmask(struct irq_data *data)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = irq_data_get_irq_chip_data(data);
|
||||||
|
unsigned int reg = data->hwirq >> adnp->reg_shift;
|
||||||
|
unsigned int pos = data->hwirq & 7;
|
||||||
|
|
||||||
|
adnp->irq_enable[reg] |= BIT(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adnp_irq_set_type(struct irq_data *data, unsigned int type)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = irq_data_get_irq_chip_data(data);
|
||||||
|
unsigned int reg = data->hwirq >> adnp->reg_shift;
|
||||||
|
unsigned int pos = data->hwirq & 7;
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_EDGE_RISING)
|
||||||
|
adnp->irq_rise[reg] |= BIT(pos);
|
||||||
|
else
|
||||||
|
adnp->irq_rise[reg] &= ~BIT(pos);
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||||
|
adnp->irq_fall[reg] |= BIT(pos);
|
||||||
|
else
|
||||||
|
adnp->irq_fall[reg] &= ~BIT(pos);
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_LEVEL_HIGH)
|
||||||
|
adnp->irq_high[reg] |= BIT(pos);
|
||||||
|
else
|
||||||
|
adnp->irq_high[reg] &= ~BIT(pos);
|
||||||
|
|
||||||
|
if (type & IRQ_TYPE_LEVEL_LOW)
|
||||||
|
adnp->irq_low[reg] |= BIT(pos);
|
||||||
|
else
|
||||||
|
adnp->irq_low[reg] &= ~BIT(pos);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adnp_irq_bus_lock(struct irq_data *data)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = irq_data_get_irq_chip_data(data);
|
||||||
|
|
||||||
|
mutex_lock(&adnp->irq_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adnp_irq_bus_unlock(struct irq_data *data)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = irq_data_get_irq_chip_data(data);
|
||||||
|
unsigned int num_regs = 1 << adnp->reg_shift, i;
|
||||||
|
|
||||||
|
mutex_lock(&adnp->i2c_lock);
|
||||||
|
|
||||||
|
for (i = 0; i < num_regs; i++)
|
||||||
|
adnp_write(adnp, GPIO_IER(adnp) + i, adnp->irq_enable[i]);
|
||||||
|
|
||||||
|
mutex_unlock(&adnp->i2c_lock);
|
||||||
|
mutex_unlock(&adnp->irq_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip adnp_irq_chip = {
|
||||||
|
.name = "gpio-adnp",
|
||||||
|
.irq_mask = adnp_irq_mask,
|
||||||
|
.irq_unmask = adnp_irq_unmask,
|
||||||
|
.irq_set_type = adnp_irq_set_type,
|
||||||
|
.irq_bus_lock = adnp_irq_bus_lock,
|
||||||
|
.irq_bus_sync_unlock = adnp_irq_bus_unlock,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int adnp_irq_map(struct irq_domain *domain, unsigned int irq,
|
||||||
|
irq_hw_number_t hwirq)
|
||||||
|
{
|
||||||
|
irq_set_chip_data(irq, domain->host_data);
|
||||||
|
irq_set_chip(irq, &adnp_irq_chip);
|
||||||
|
irq_set_nested_thread(irq, true);
|
||||||
|
|
||||||
|
#ifdef CONFIG_ARM
|
||||||
|
set_irq_flags(irq, IRQF_VALID);
|
||||||
|
#else
|
||||||
|
irq_set_noprobe(irq);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct irq_domain_ops adnp_irq_domain_ops = {
|
||||||
|
.map = adnp_irq_map,
|
||||||
|
.xlate = irq_domain_xlate_twocell,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int adnp_irq_setup(struct adnp *adnp)
|
||||||
|
{
|
||||||
|
unsigned int num_regs = 1 << adnp->reg_shift, i;
|
||||||
|
struct gpio_chip *chip = &adnp->gpio;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_init(&adnp->irq_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate memory to keep track of the current level and trigger
|
||||||
|
* modes of the interrupts. To avoid multiple allocations, a single
|
||||||
|
* large buffer is allocated and pointers are setup to point at the
|
||||||
|
* corresponding offsets. For consistency, the layout of the buffer
|
||||||
|
* is chosen to match the register layout of the hardware in that
|
||||||
|
* each segment contains the corresponding bits for all interrupts.
|
||||||
|
*/
|
||||||
|
adnp->irq_enable = devm_kzalloc(chip->dev, num_regs * 6, GFP_KERNEL);
|
||||||
|
if (!adnp->irq_enable)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
adnp->irq_level = adnp->irq_enable + (num_regs * 1);
|
||||||
|
adnp->irq_rise = adnp->irq_enable + (num_regs * 2);
|
||||||
|
adnp->irq_fall = adnp->irq_enable + (num_regs * 3);
|
||||||
|
adnp->irq_high = adnp->irq_enable + (num_regs * 4);
|
||||||
|
adnp->irq_low = adnp->irq_enable + (num_regs * 5);
|
||||||
|
|
||||||
|
for (i = 0; i < num_regs; i++) {
|
||||||
|
/*
|
||||||
|
* Read the initial level of all pins to allow the emulation
|
||||||
|
* of edge triggered interrupts.
|
||||||
|
*/
|
||||||
|
err = adnp_read(adnp, GPIO_PLR(adnp) + i, &adnp->irq_level[i]);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* disable all interrupts */
|
||||||
|
err = adnp_write(adnp, GPIO_IER(adnp) + i, 0);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
adnp->irq_enable[i] = 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
adnp->domain = irq_domain_add_linear(chip->of_node, chip->ngpio,
|
||||||
|
&adnp_irq_domain_ops, adnp);
|
||||||
|
|
||||||
|
err = request_threaded_irq(adnp->client->irq, NULL, adnp_irq,
|
||||||
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||||
|
dev_name(chip->dev), adnp);
|
||||||
|
if (err != 0) {
|
||||||
|
dev_err(chip->dev, "can't request IRQ#%d: %d\n",
|
||||||
|
adnp->client->irq, err);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->to_irq = adnp_gpio_to_irq;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
irq_domain_remove(adnp->domain);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adnp_irq_teardown(struct adnp *adnp)
|
||||||
|
{
|
||||||
|
unsigned int irq, i;
|
||||||
|
|
||||||
|
free_irq(adnp->client->irq, adnp);
|
||||||
|
|
||||||
|
for (i = 0; i < adnp->gpio.ngpio; i++) {
|
||||||
|
irq = irq_find_mapping(adnp->domain, i);
|
||||||
|
if (irq > 0)
|
||||||
|
irq_dispose_mapping(irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_domain_remove(adnp->domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __devinit int adnp_i2c_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct device_node *np = client->dev.of_node;
|
||||||
|
struct adnp *adnp;
|
||||||
|
u32 num_gpios;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = of_property_read_u32(np, "nr-gpios", &num_gpios);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
client->irq = irq_of_parse_and_map(np, 0);
|
||||||
|
if (!client->irq)
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
adnp = devm_kzalloc(&client->dev, sizeof(*adnp), GFP_KERNEL);
|
||||||
|
if (!adnp)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(&adnp->i2c_lock);
|
||||||
|
adnp->client = client;
|
||||||
|
|
||||||
|
err = adnp_gpio_setup(adnp, num_gpios);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (of_find_property(np, "interrupt-controller", NULL)) {
|
||||||
|
err = adnp_irq_setup(adnp);
|
||||||
|
if (err < 0)
|
||||||
|
goto teardown;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gpiochip_add(&adnp->gpio);
|
||||||
|
if (err < 0)
|
||||||
|
goto teardown;
|
||||||
|
|
||||||
|
i2c_set_clientdata(client, adnp);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
teardown:
|
||||||
|
if (of_find_property(np, "interrupt-controller", NULL))
|
||||||
|
adnp_irq_teardown(adnp);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __devexit int adnp_i2c_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct adnp *adnp = i2c_get_clientdata(client);
|
||||||
|
struct device_node *np = client->dev.of_node;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = gpiochip_remove(&adnp->gpio);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(&client->dev, "%s failed: %d\n", "gpiochip_remove()",
|
||||||
|
err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (of_find_property(np, "interrupt-controller", NULL))
|
||||||
|
adnp_irq_teardown(adnp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id adnp_i2c_id[] __devinitconst = {
|
||||||
|
{ "gpio-adnp" },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, adnp_i2c_id);
|
||||||
|
|
||||||
|
static const struct of_device_id adnp_of_match[] __devinitconst = {
|
||||||
|
{ .compatible = "ad,gpio-adnp", },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, adnp_of_match);
|
||||||
|
|
||||||
|
static struct i2c_driver adnp_i2c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "gpio-adnp",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = of_match_ptr(adnp_of_match),
|
||||||
|
},
|
||||||
|
.probe = adnp_i2c_probe,
|
||||||
|
.remove = __devexit_p(adnp_i2c_remove),
|
||||||
|
.id_table = adnp_i2c_id,
|
||||||
|
};
|
||||||
|
module_i2c_driver(adnp_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Avionic Design N-bit GPIO expander");
|
||||||
|
MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -483,19 +483,7 @@ static struct i2c_driver adp5588_gpio_driver = {
|
||||||
.id_table = adp5588_gpio_id,
|
.id_table = adp5588_gpio_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init adp5588_gpio_init(void)
|
module_i2c_driver(adp5588_gpio_driver);
|
||||||
{
|
|
||||||
return i2c_add_driver(&adp5588_gpio_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(adp5588_gpio_init);
|
|
||||||
|
|
||||||
static void __exit adp5588_gpio_exit(void)
|
|
||||||
{
|
|
||||||
i2c_del_driver(&adp5588_gpio_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_exit(adp5588_gpio_exit);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||||
MODULE_DESCRIPTION("GPIO ADP5588 Driver");
|
MODULE_DESCRIPTION("GPIO ADP5588 Driver");
|
||||||
|
|
|
@ -310,7 +310,7 @@ static int bt8xxgpio_resume(struct pci_dev *pdev)
|
||||||
#define bt8xxgpio_resume NULL
|
#define bt8xxgpio_resume NULL
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
static struct pci_device_id bt8xxgpio_pci_tbl[] = {
|
static DEFINE_PCI_DEVICE_TABLE(bt8xxgpio_pci_tbl) = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) },
|
||||||
|
|
|
@ -207,7 +207,7 @@ static int __devinit da9052_gpio_probe(struct platform_device *pdev)
|
||||||
struct da9052_pdata *pdata;
|
struct da9052_pdata *pdata;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
|
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
|
||||||
if (gpio == NULL)
|
if (gpio == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -221,28 +221,19 @@ static int __devinit da9052_gpio_probe(struct platform_device *pdev)
|
||||||
ret = gpiochip_add(&gpio->gp);
|
ret = gpiochip_add(&gpio->gp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
|
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
|
||||||
goto err_mem;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, gpio);
|
platform_set_drvdata(pdev, gpio);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_mem:
|
|
||||||
kfree(gpio);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devexit da9052_gpio_remove(struct platform_device *pdev)
|
static int __devexit da9052_gpio_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct da9052_gpio *gpio = platform_get_drvdata(pdev);
|
struct da9052_gpio *gpio = platform_get_drvdata(pdev);
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = gpiochip_remove(&gpio->gp);
|
return gpiochip_remove(&gpio->gp);
|
||||||
if (ret == 0)
|
|
||||||
kfree(gpio);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver da9052_gpio_driver = {
|
static struct platform_driver da9052_gpio_driver = {
|
||||||
|
|
|
@ -366,7 +366,7 @@ static int __init davinci_gpio_irq_setup(void)
|
||||||
PTR_ERR(clk));
|
PTR_ERR(clk));
|
||||||
return PTR_ERR(clk);
|
return PTR_ERR(clk);
|
||||||
}
|
}
|
||||||
clk_enable(clk);
|
clk_prepare_enable(clk);
|
||||||
|
|
||||||
/* Arrange gpio_to_irq() support, handling either direct IRQs or
|
/* Arrange gpio_to_irq() support, handling either direct IRQs or
|
||||||
* banked IRQs. Having GPIOs in the first GPIO bank use direct
|
* banked IRQs. Having GPIOs in the first GPIO bank use direct
|
||||||
|
|
|
@ -85,22 +85,16 @@ static inline void em_gio_write(struct em_gio_priv *p, int offs,
|
||||||
iowrite32(value, p->base1 + (offs - GIO_IDT0));
|
iowrite32(value, p->base1 + (offs - GIO_IDT0));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct em_gio_priv *irq_to_priv(struct irq_data *d)
|
|
||||||
{
|
|
||||||
struct irq_chip *chip = irq_data_get_irq_chip(d);
|
|
||||||
return container_of(chip, struct em_gio_priv, irq_chip);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void em_gio_irq_disable(struct irq_data *d)
|
static void em_gio_irq_disable(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct em_gio_priv *p = irq_to_priv(d);
|
struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
em_gio_write(p, GIO_IDS, BIT(irqd_to_hwirq(d)));
|
em_gio_write(p, GIO_IDS, BIT(irqd_to_hwirq(d)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void em_gio_irq_enable(struct irq_data *d)
|
static void em_gio_irq_enable(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct em_gio_priv *p = irq_to_priv(d);
|
struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
em_gio_write(p, GIO_IEN, BIT(irqd_to_hwirq(d)));
|
em_gio_write(p, GIO_IEN, BIT(irqd_to_hwirq(d)));
|
||||||
}
|
}
|
||||||
|
@ -118,7 +112,7 @@ static unsigned char em_gio_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
|
||||||
static int em_gio_irq_set_type(struct irq_data *d, unsigned int type)
|
static int em_gio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||||
{
|
{
|
||||||
unsigned char value = em_gio_sense_table[type & IRQ_TYPE_SENSE_MASK];
|
unsigned char value = em_gio_sense_table[type & IRQ_TYPE_SENSE_MASK];
|
||||||
struct em_gio_priv *p = irq_to_priv(d);
|
struct em_gio_priv *p = irq_data_get_irq_chip_data(d);
|
||||||
unsigned int reg, offset, shift;
|
unsigned int reg, offset, shift;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long tmp;
|
unsigned long tmp;
|
||||||
|
|
|
@ -113,7 +113,8 @@ static const char *gpi_p3_names[LPC32XX_GPI_P3_MAX] = {
|
||||||
NULL, NULL, NULL, "gpi15",
|
NULL, NULL, NULL, "gpi15",
|
||||||
"gpi16", "gpi17", "gpi18", "gpi19",
|
"gpi16", "gpi17", "gpi18", "gpi19",
|
||||||
"gpi20", "gpi21", "gpi22", "gpi23",
|
"gpi20", "gpi21", "gpi22", "gpi23",
|
||||||
"gpi24", "gpi25", "gpi26", "gpi27"
|
"gpi24", "gpi25", "gpi26", "gpi27",
|
||||||
|
"gpi28"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *gpo_p3_names[LPC32XX_GPO_P3_MAX] = {
|
static const char *gpo_p3_names[LPC32XX_GPO_P3_MAX] = {
|
||||||
|
|
|
@ -91,10 +91,9 @@ static int mc9s08dz60_direction_output(struct gpio_chip *gc,
|
||||||
static int mc9s08dz60_probe(struct i2c_client *client,
|
static int mc9s08dz60_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
|
||||||
struct mc9s08dz60 *mc9s;
|
struct mc9s08dz60 *mc9s;
|
||||||
|
|
||||||
mc9s = kzalloc(sizeof(*mc9s), GFP_KERNEL);
|
mc9s = devm_kzalloc(&client->dev, sizeof(*mc9s), GFP_KERNEL);
|
||||||
if (!mc9s)
|
if (!mc9s)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -110,30 +109,16 @@ static int mc9s08dz60_probe(struct i2c_client *client,
|
||||||
mc9s->client = client;
|
mc9s->client = client;
|
||||||
i2c_set_clientdata(client, mc9s);
|
i2c_set_clientdata(client, mc9s);
|
||||||
|
|
||||||
ret = gpiochip_add(&mc9s->chip);
|
return gpiochip_add(&mc9s->chip);
|
||||||
if (ret)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
kfree(mc9s);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mc9s08dz60_remove(struct i2c_client *client)
|
static int mc9s08dz60_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct mc9s08dz60 *mc9s;
|
struct mc9s08dz60 *mc9s;
|
||||||
int ret;
|
|
||||||
|
|
||||||
mc9s = i2c_get_clientdata(client);
|
mc9s = i2c_get_clientdata(client);
|
||||||
|
|
||||||
ret = gpiochip_remove(&mc9s->chip);
|
return gpiochip_remove(&mc9s->chip);
|
||||||
if (!ret)
|
|
||||||
kfree(mc9s);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id mc9s08dz60_id[] = {
|
static const struct i2c_device_id mc9s08dz60_id[] = {
|
||||||
|
|
|
@ -87,8 +87,7 @@ struct ioh_gpio_reg_data {
|
||||||
* @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM
|
* @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM
|
||||||
* @ch: Indicate GPIO channel
|
* @ch: Indicate GPIO channel
|
||||||
* @irq_base: Save base of IRQ number for interrupt
|
* @irq_base: Save base of IRQ number for interrupt
|
||||||
* @spinlock: Used for register access protection in
|
* @spinlock: Used for register access protection
|
||||||
* interrupt context ioh_irq_type and PM;
|
|
||||||
*/
|
*/
|
||||||
struct ioh_gpio {
|
struct ioh_gpio {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
@ -97,7 +96,6 @@ struct ioh_gpio {
|
||||||
struct gpio_chip gpio;
|
struct gpio_chip gpio;
|
||||||
struct ioh_gpio_reg_data ioh_gpio_reg;
|
struct ioh_gpio_reg_data ioh_gpio_reg;
|
||||||
u32 gpio_use_sel;
|
u32 gpio_use_sel;
|
||||||
struct mutex lock;
|
|
||||||
int ch;
|
int ch;
|
||||||
int irq_base;
|
int irq_base;
|
||||||
spinlock_t spinlock;
|
spinlock_t spinlock;
|
||||||
|
@ -109,8 +107,9 @@ static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
|
||||||
{
|
{
|
||||||
u32 reg_val;
|
u32 reg_val;
|
||||||
struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
|
struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
spin_lock_irqsave(&chip->spinlock, flags);
|
||||||
reg_val = ioread32(&chip->reg->regs[chip->ch].po);
|
reg_val = ioread32(&chip->reg->regs[chip->ch].po);
|
||||||
if (val)
|
if (val)
|
||||||
reg_val |= (1 << nr);
|
reg_val |= (1 << nr);
|
||||||
|
@ -118,7 +117,7 @@ static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
|
||||||
reg_val &= ~(1 << nr);
|
reg_val &= ~(1 << nr);
|
||||||
|
|
||||||
iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
|
iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
|
||||||
mutex_unlock(&chip->lock);
|
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr)
|
static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr)
|
||||||
|
@ -134,8 +133,9 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
||||||
struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
|
struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
|
||||||
u32 pm;
|
u32 pm;
|
||||||
u32 reg_val;
|
u32 reg_val;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
spin_lock_irqsave(&chip->spinlock, flags);
|
||||||
pm = ioread32(&chip->reg->regs[chip->ch].pm) &
|
pm = ioread32(&chip->reg->regs[chip->ch].pm) &
|
||||||
((1 << num_ports[chip->ch]) - 1);
|
((1 << num_ports[chip->ch]) - 1);
|
||||||
pm |= (1 << nr);
|
pm |= (1 << nr);
|
||||||
|
@ -148,7 +148,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
||||||
reg_val &= ~(1 << nr);
|
reg_val &= ~(1 << nr);
|
||||||
iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
|
iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
|
||||||
|
|
||||||
mutex_unlock(&chip->lock);
|
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -157,13 +157,14 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
|
||||||
{
|
{
|
||||||
struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
|
struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
|
||||||
u32 pm;
|
u32 pm;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
spin_lock_irqsave(&chip->spinlock, flags);
|
||||||
pm = ioread32(&chip->reg->regs[chip->ch].pm) &
|
pm = ioread32(&chip->reg->regs[chip->ch].pm) &
|
||||||
((1 << num_ports[chip->ch]) - 1);
|
((1 << num_ports[chip->ch]) - 1);
|
||||||
pm &= ~(1 << nr);
|
pm &= ~(1 << nr);
|
||||||
iowrite32(pm, &chip->reg->regs[chip->ch].pm);
|
iowrite32(pm, &chip->reg->regs[chip->ch].pm);
|
||||||
mutex_unlock(&chip->lock);
|
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -447,7 +448,6 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev,
|
||||||
chip->base = base;
|
chip->base = base;
|
||||||
chip->reg = chip->base;
|
chip->reg = chip->base;
|
||||||
chip->ch = i;
|
chip->ch = i;
|
||||||
mutex_init(&chip->lock);
|
|
||||||
spin_lock_init(&chip->spinlock);
|
spin_lock_init(&chip->spinlock);
|
||||||
ioh_gpio_setup(chip, num_ports[i]);
|
ioh_gpio_setup(chip, num_ports[i]);
|
||||||
ret = gpiochip_add(&chip->gpio);
|
ret = gpiochip_add(&chip->gpio);
|
||||||
|
|
|
@ -317,9 +317,7 @@ static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc)
|
||||||
|
|
||||||
chained_irq_enter(chip, desc);
|
chained_irq_enter(chip, desc);
|
||||||
|
|
||||||
for (i = find_first_bit(msm_gpio.enabled_irqs, NR_GPIO_IRQS);
|
for_each_set_bit(i, msm_gpio.enabled_irqs, NR_GPIO_IRQS) {
|
||||||
i < NR_GPIO_IRQS;
|
|
||||||
i = find_next_bit(msm_gpio.enabled_irqs, NR_GPIO_IRQS, i + 1)) {
|
|
||||||
if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS))
|
if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS))
|
||||||
generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
|
generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
|
||||||
i));
|
i));
|
||||||
|
|
|
@ -23,7 +23,12 @@
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c/pcf857x.h>
|
#include <linux/i2c/pcf857x.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
|
||||||
static const struct i2c_device_id pcf857x_id[] = {
|
static const struct i2c_device_id pcf857x_id[] = {
|
||||||
|
@ -60,7 +65,12 @@ struct pcf857x {
|
||||||
struct gpio_chip chip;
|
struct gpio_chip chip;
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct mutex lock; /* protect 'out' */
|
struct mutex lock; /* protect 'out' */
|
||||||
|
struct work_struct work; /* irq demux work */
|
||||||
|
struct irq_domain *irq_domain; /* for irq demux */
|
||||||
|
spinlock_t slock; /* protect irq demux */
|
||||||
unsigned out; /* software latch */
|
unsigned out; /* software latch */
|
||||||
|
unsigned status; /* current status */
|
||||||
|
int irq; /* real irq number */
|
||||||
|
|
||||||
int (*write)(struct i2c_client *client, unsigned data);
|
int (*write)(struct i2c_client *client, unsigned data);
|
||||||
int (*read)(struct i2c_client *client);
|
int (*read)(struct i2c_client *client);
|
||||||
|
@ -150,6 +160,100 @@ static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||||
|
{
|
||||||
|
struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
|
||||||
|
|
||||||
|
return irq_create_mapping(gpio->irq_domain, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pcf857x_irq_demux_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct pcf857x *gpio = container_of(work,
|
||||||
|
struct pcf857x,
|
||||||
|
work);
|
||||||
|
unsigned long change, i, status, flags;
|
||||||
|
|
||||||
|
status = gpio->read(gpio->client);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&gpio->slock, flags);
|
||||||
|
|
||||||
|
change = gpio->status ^ status;
|
||||||
|
for_each_set_bit(i, &change, gpio->chip.ngpio)
|
||||||
|
generic_handle_irq(irq_find_mapping(gpio->irq_domain, i));
|
||||||
|
gpio->status = status;
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&gpio->slock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t pcf857x_irq_demux(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct pcf857x *gpio = data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pcf857x can't read/write data here,
|
||||||
|
* since i2c data access might go to sleep.
|
||||||
|
*/
|
||||||
|
schedule_work(&gpio->work);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int virq,
|
||||||
|
irq_hw_number_t hw)
|
||||||
|
{
|
||||||
|
irq_set_chip_and_handler(virq,
|
||||||
|
&dummy_irq_chip,
|
||||||
|
handle_level_irq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_domain_ops pcf857x_irq_domain_ops = {
|
||||||
|
.map = pcf857x_irq_domain_map,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio)
|
||||||
|
{
|
||||||
|
if (gpio->irq_domain)
|
||||||
|
irq_domain_remove(gpio->irq_domain);
|
||||||
|
|
||||||
|
if (gpio->irq)
|
||||||
|
free_irq(gpio->irq, gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcf857x_irq_domain_init(struct pcf857x *gpio,
|
||||||
|
struct pcf857x_platform_data *pdata,
|
||||||
|
struct device *dev)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
gpio->irq_domain = irq_domain_add_linear(dev->of_node,
|
||||||
|
gpio->chip.ngpio,
|
||||||
|
&pcf857x_irq_domain_ops,
|
||||||
|
NULL);
|
||||||
|
if (!gpio->irq_domain)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* enable real irq */
|
||||||
|
status = request_irq(pdata->irq, pcf857x_irq_demux, 0,
|
||||||
|
dev_name(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;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
pcf857x_irq_domain_cleanup(gpio);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static int pcf857x_probe(struct i2c_client *client,
|
static int pcf857x_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -168,6 +272,7 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
mutex_init(&gpio->lock);
|
mutex_init(&gpio->lock);
|
||||||
|
spin_lock_init(&gpio->slock);
|
||||||
|
|
||||||
gpio->chip.base = pdata ? pdata->gpio_base : -1;
|
gpio->chip.base = pdata ? pdata->gpio_base : -1;
|
||||||
gpio->chip.can_sleep = 1;
|
gpio->chip.can_sleep = 1;
|
||||||
|
@ -179,6 +284,15 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||||
gpio->chip.direction_output = pcf857x_output;
|
gpio->chip.direction_output = pcf857x_output;
|
||||||
gpio->chip.ngpio = id->driver_data;
|
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 (status < 0) {
|
||||||
|
dev_err(&client->dev, "irq_domain init failed\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* NOTE: the OnSemi jlc1562b is also largely compatible with
|
/* NOTE: the OnSemi jlc1562b is also largely compatible with
|
||||||
* these parts, notably for output. It has a low-resolution
|
* these parts, notably for output. It has a low-resolution
|
||||||
* DAC instead of pin change IRQs; and its inputs can be the
|
* DAC instead of pin change IRQs; and its inputs can be the
|
||||||
|
@ -248,6 +362,7 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||||
* all-ones reset state. Otherwise it flags pins to be driven low.
|
* all-ones reset state. Otherwise it flags pins to be driven low.
|
||||||
*/
|
*/
|
||||||
gpio->out = pdata ? ~pdata->n_latch : ~0;
|
gpio->out = pdata ? ~pdata->n_latch : ~0;
|
||||||
|
gpio->status = gpio->out;
|
||||||
|
|
||||||
status = gpiochip_add(&gpio->chip);
|
status = gpiochip_add(&gpio->chip);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
|
@ -278,6 +393,10 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||||
fail:
|
fail:
|
||||||
dev_dbg(&client->dev, "probe error %d for '%s'\n",
|
dev_dbg(&client->dev, "probe error %d for '%s'\n",
|
||||||
status, client->name);
|
status, client->name);
|
||||||
|
|
||||||
|
if (pdata && pdata->irq)
|
||||||
|
pcf857x_irq_domain_cleanup(gpio);
|
||||||
|
|
||||||
kfree(gpio);
|
kfree(gpio);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -299,6 +418,9 @@ static int pcf857x_remove(struct i2c_client *client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pdata && pdata->irq)
|
||||||
|
pcf857x_irq_domain_cleanup(gpio);
|
||||||
|
|
||||||
status = gpiochip_remove(&gpio->chip);
|
status = gpiochip_remove(&gpio->chip);
|
||||||
if (status == 0)
|
if (status == 0)
|
||||||
kfree(gpio);
|
kfree(gpio);
|
||||||
|
|
|
@ -92,9 +92,7 @@ struct pch_gpio_reg_data {
|
||||||
* @lock: Used for register access protection
|
* @lock: Used for register access protection
|
||||||
* @irq_base: Save base of IRQ number for interrupt
|
* @irq_base: Save base of IRQ number for interrupt
|
||||||
* @ioh: IOH ID
|
* @ioh: IOH ID
|
||||||
* @spinlock: Used for register access protection in
|
* @spinlock: Used for register access protection
|
||||||
* interrupt context pch_irq_mask,
|
|
||||||
* pch_irq_unmask and pch_irq_type;
|
|
||||||
*/
|
*/
|
||||||
struct pch_gpio {
|
struct pch_gpio {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
@ -102,7 +100,6 @@ struct pch_gpio {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct gpio_chip gpio;
|
struct gpio_chip gpio;
|
||||||
struct pch_gpio_reg_data pch_gpio_reg;
|
struct pch_gpio_reg_data pch_gpio_reg;
|
||||||
struct mutex lock;
|
|
||||||
int irq_base;
|
int irq_base;
|
||||||
enum pch_type_t ioh;
|
enum pch_type_t ioh;
|
||||||
spinlock_t spinlock;
|
spinlock_t spinlock;
|
||||||
|
@ -112,8 +109,9 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
|
||||||
{
|
{
|
||||||
u32 reg_val;
|
u32 reg_val;
|
||||||
struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
|
struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
spin_lock_irqsave(&chip->spinlock, flags);
|
||||||
reg_val = ioread32(&chip->reg->po);
|
reg_val = ioread32(&chip->reg->po);
|
||||||
if (val)
|
if (val)
|
||||||
reg_val |= (1 << nr);
|
reg_val |= (1 << nr);
|
||||||
|
@ -121,7 +119,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
|
||||||
reg_val &= ~(1 << nr);
|
reg_val &= ~(1 << nr);
|
||||||
|
|
||||||
iowrite32(reg_val, &chip->reg->po);
|
iowrite32(reg_val, &chip->reg->po);
|
||||||
mutex_unlock(&chip->lock);
|
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
|
static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
|
||||||
|
@ -137,8 +135,9 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
||||||
struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
|
struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
|
||||||
u32 pm;
|
u32 pm;
|
||||||
u32 reg_val;
|
u32 reg_val;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
spin_lock_irqsave(&chip->spinlock, flags);
|
||||||
pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
|
pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
|
||||||
pm |= (1 << nr);
|
pm |= (1 << nr);
|
||||||
iowrite32(pm, &chip->reg->pm);
|
iowrite32(pm, &chip->reg->pm);
|
||||||
|
@ -149,8 +148,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
||||||
else
|
else
|
||||||
reg_val &= ~(1 << nr);
|
reg_val &= ~(1 << nr);
|
||||||
iowrite32(reg_val, &chip->reg->po);
|
iowrite32(reg_val, &chip->reg->po);
|
||||||
|
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||||
mutex_unlock(&chip->lock);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -159,12 +157,13 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
|
||||||
{
|
{
|
||||||
struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
|
struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio);
|
||||||
u32 pm;
|
u32 pm;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
spin_lock_irqsave(&chip->spinlock, flags);
|
||||||
pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
|
pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1);
|
||||||
pm &= ~(1 << nr);
|
pm &= ~(1 << nr);
|
||||||
iowrite32(pm, &chip->reg->pm);
|
iowrite32(pm, &chip->reg->pm);
|
||||||
mutex_unlock(&chip->lock);
|
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -387,7 +386,6 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev,
|
||||||
|
|
||||||
chip->reg = chip->base;
|
chip->reg = chip->base;
|
||||||
pci_set_drvdata(pdev, chip);
|
pci_set_drvdata(pdev, chip);
|
||||||
mutex_init(&chip->lock);
|
|
||||||
spin_lock_init(&chip->spinlock);
|
spin_lock_init(&chip->spinlock);
|
||||||
pch_gpio_setup(chip);
|
pch_gpio_setup(chip);
|
||||||
ret = gpiochip_add(&chip->gpio);
|
ret = gpiochip_add(&chip->gpio);
|
||||||
|
|
|
@ -370,12 +370,10 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc)
|
||||||
gedr = gedr & c->irq_mask;
|
gedr = gedr & c->irq_mask;
|
||||||
writel_relaxed(gedr, c->regbase + GEDR_OFFSET);
|
writel_relaxed(gedr, c->regbase + GEDR_OFFSET);
|
||||||
|
|
||||||
n = find_first_bit(&gedr, BITS_PER_LONG);
|
for_each_set_bit(n, &gedr, BITS_PER_LONG) {
|
||||||
while (n < BITS_PER_LONG) {
|
|
||||||
loop = 1;
|
loop = 1;
|
||||||
|
|
||||||
generic_handle_irq(gpio_to_irq(gpio_base + n));
|
generic_handle_irq(gpio_to_irq(gpio_base + n));
|
||||||
n = find_next_bit(&gedr, BITS_PER_LONG, n + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (loop);
|
} while (loop);
|
||||||
|
@ -589,19 +587,12 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev)
|
||||||
iounmap(gpio_reg_base);
|
iounmap(gpio_reg_base);
|
||||||
return PTR_ERR(clk);
|
return PTR_ERR(clk);
|
||||||
}
|
}
|
||||||
ret = clk_prepare(clk);
|
ret = clk_prepare_enable(clk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
clk_put(clk);
|
clk_put(clk);
|
||||||
iounmap(gpio_reg_base);
|
iounmap(gpio_reg_base);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret = clk_enable(clk);
|
|
||||||
if (ret) {
|
|
||||||
clk_unprepare(clk);
|
|
||||||
clk_put(clk);
|
|
||||||
iounmap(gpio_reg_base);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize GPIO chips */
|
/* Initialize GPIO chips */
|
||||||
info = dev_get_platdata(&pdev->dev);
|
info = dev_get_platdata(&pdev->dev);
|
||||||
|
|
|
@ -270,7 +270,7 @@ static void sdv_gpio_remove(struct pci_dev *pdev)
|
||||||
kfree(sd);
|
kfree(sd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pci_device_id sdv_gpio_pci_ids[] __devinitdata = {
|
static DEFINE_PCI_DEVICE_TABLE(sdv_gpio_pci_ids) = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_SDV_GPIO) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_SDV_GPIO) },
|
||||||
{ 0, },
|
{ 0, },
|
||||||
};
|
};
|
||||||
|
|
|
@ -311,11 +311,9 @@ static int sx150x_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
|
||||||
|
|
||||||
static void sx150x_irq_mask(struct irq_data *d)
|
static void sx150x_irq_mask(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct irq_chip *ic = irq_data_get_irq_chip(d);
|
struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
|
||||||
struct sx150x_chip *chip;
|
|
||||||
unsigned n;
|
unsigned n;
|
||||||
|
|
||||||
chip = container_of(ic, struct sx150x_chip, irq_chip);
|
|
||||||
n = d->irq - chip->irq_base;
|
n = d->irq - chip->irq_base;
|
||||||
chip->irq_masked |= (1 << n);
|
chip->irq_masked |= (1 << n);
|
||||||
chip->irq_update = n;
|
chip->irq_update = n;
|
||||||
|
@ -323,27 +321,22 @@ static void sx150x_irq_mask(struct irq_data *d)
|
||||||
|
|
||||||
static void sx150x_irq_unmask(struct irq_data *d)
|
static void sx150x_irq_unmask(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct irq_chip *ic = irq_data_get_irq_chip(d);
|
struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
|
||||||
struct sx150x_chip *chip;
|
|
||||||
unsigned n;
|
unsigned n;
|
||||||
|
|
||||||
chip = container_of(ic, struct sx150x_chip, irq_chip);
|
|
||||||
n = d->irq - chip->irq_base;
|
n = d->irq - chip->irq_base;
|
||||||
|
|
||||||
chip->irq_masked &= ~(1 << n);
|
chip->irq_masked &= ~(1 << n);
|
||||||
chip->irq_update = n;
|
chip->irq_update = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
|
static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
|
||||||
{
|
{
|
||||||
struct irq_chip *ic = irq_data_get_irq_chip(d);
|
struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
|
||||||
struct sx150x_chip *chip;
|
|
||||||
unsigned n, val = 0;
|
unsigned n, val = 0;
|
||||||
|
|
||||||
if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
|
if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
chip = container_of(ic, struct sx150x_chip, irq_chip);
|
|
||||||
n = d->irq - chip->irq_base;
|
n = d->irq - chip->irq_base;
|
||||||
|
|
||||||
if (flow_type & IRQ_TYPE_EDGE_RISING)
|
if (flow_type & IRQ_TYPE_EDGE_RISING)
|
||||||
|
@ -391,22 +384,16 @@ static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
|
||||||
|
|
||||||
static void sx150x_irq_bus_lock(struct irq_data *d)
|
static void sx150x_irq_bus_lock(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct irq_chip *ic = irq_data_get_irq_chip(d);
|
struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
|
||||||
struct sx150x_chip *chip;
|
|
||||||
|
|
||||||
chip = container_of(ic, struct sx150x_chip, irq_chip);
|
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
mutex_lock(&chip->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
|
static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct irq_chip *ic = irq_data_get_irq_chip(d);
|
struct sx150x_chip *chip = irq_data_get_irq_chip_data(d);
|
||||||
struct sx150x_chip *chip;
|
|
||||||
unsigned n;
|
unsigned n;
|
||||||
|
|
||||||
chip = container_of(ic, struct sx150x_chip, irq_chip);
|
|
||||||
|
|
||||||
if (chip->irq_update == NO_UPDATE_PENDING)
|
if (chip->irq_update == NO_UPDATE_PENDING)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -551,6 +538,7 @@ static int sx150x_install_irq_chip(struct sx150x_chip *chip,
|
||||||
|
|
||||||
for (n = 0; n < chip->dev_cfg->ngpios; ++n) {
|
for (n = 0; n < chip->dev_cfg->ngpios; ++n) {
|
||||||
irq = irq_base + n;
|
irq = irq_base + n;
|
||||||
|
irq_set_chip_data(irq, chip);
|
||||||
irq_set_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq);
|
irq_set_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq);
|
||||||
irq_set_nested_thread(irq, 1);
|
irq_set_nested_thread(irq, 1);
|
||||||
#ifdef CONFIG_ARM
|
#ifdef CONFIG_ARM
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/mfd/tc3589x.h>
|
#include <linux/mfd/tc3589x.h>
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ struct tc3589x_gpio {
|
||||||
struct tc3589x *tc3589x;
|
struct tc3589x *tc3589x;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct mutex irq_lock;
|
struct mutex irq_lock;
|
||||||
|
struct irq_domain *domain;
|
||||||
|
|
||||||
int irq_base;
|
int irq_base;
|
||||||
|
|
||||||
|
@ -92,11 +95,28 @@ static int tc3589x_gpio_direction_input(struct gpio_chip *chip,
|
||||||
return tc3589x_set_bits(tc3589x, reg, 1 << pos, 0);
|
return tc3589x_set_bits(tc3589x, reg, 1 << pos, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tc3589x_gpio_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
|
||||||
|
*
|
||||||
|
* @tc3589x_gpio: tc3589x_gpio_irq controller to operate on.
|
||||||
|
* @irq: index of the interrupt requested in the chip IRQs
|
||||||
|
*
|
||||||
|
* Useful for drivers to request their own IRQs.
|
||||||
|
*/
|
||||||
|
static int tc3589x_gpio_irq_get_virq(struct tc3589x_gpio *tc3589x_gpio,
|
||||||
|
int irq)
|
||||||
|
{
|
||||||
|
if (!tc3589x_gpio)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return irq_create_mapping(tc3589x_gpio->domain, irq);
|
||||||
|
}
|
||||||
|
|
||||||
static int tc3589x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
static int tc3589x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||||
{
|
{
|
||||||
struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
|
struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
|
||||||
|
|
||||||
return tc3589x_gpio->irq_base + offset;
|
return tc3589x_gpio_irq_get_virq(tc3589x_gpio, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct gpio_chip template_chip = {
|
static struct gpio_chip template_chip = {
|
||||||
|
@ -113,7 +133,7 @@ static struct gpio_chip template_chip = {
|
||||||
static int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
static int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||||
{
|
{
|
||||||
struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
|
struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
|
||||||
int offset = d->irq - tc3589x_gpio->irq_base;
|
int offset = d->hwirq;
|
||||||
int regoffset = offset / 8;
|
int regoffset = offset / 8;
|
||||||
int mask = 1 << (offset % 8);
|
int mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
@ -175,7 +195,7 @@ static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d)
|
||||||
static void tc3589x_gpio_irq_mask(struct irq_data *d)
|
static void tc3589x_gpio_irq_mask(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
|
struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
|
||||||
int offset = d->irq - tc3589x_gpio->irq_base;
|
int offset = d->hwirq;
|
||||||
int regoffset = offset / 8;
|
int regoffset = offset / 8;
|
||||||
int mask = 1 << (offset % 8);
|
int mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
@ -185,7 +205,7 @@ static void tc3589x_gpio_irq_mask(struct irq_data *d)
|
||||||
static void tc3589x_gpio_irq_unmask(struct irq_data *d)
|
static void tc3589x_gpio_irq_unmask(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
|
struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
|
||||||
int offset = d->irq - tc3589x_gpio->irq_base;
|
int offset = d->hwirq;
|
||||||
int regoffset = offset / 8;
|
int regoffset = offset / 8;
|
||||||
int mask = 1 << (offset % 8);
|
int mask = 1 << (offset % 8);
|
||||||
|
|
||||||
|
@ -222,8 +242,9 @@ static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
|
||||||
while (stat) {
|
while (stat) {
|
||||||
int bit = __ffs(stat);
|
int bit = __ffs(stat);
|
||||||
int line = i * 8 + bit;
|
int line = i * 8 + bit;
|
||||||
|
int virq = tc3589x_gpio_irq_get_virq(tc3589x_gpio, line);
|
||||||
|
|
||||||
handle_nested_irq(tc3589x_gpio->irq_base + line);
|
handle_nested_irq(virq);
|
||||||
stat &= ~(1 << bit);
|
stat &= ~(1 << bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,51 +254,78 @@ static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio)
|
static int tc3589x_gpio_irq_map(struct irq_domain *d, unsigned int virq,
|
||||||
|
irq_hw_number_t hwirq)
|
||||||
{
|
{
|
||||||
int base = tc3589x_gpio->irq_base;
|
struct tc3589x *tc3589x_gpio = d->host_data;
|
||||||
int irq;
|
|
||||||
|
|
||||||
for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) {
|
irq_set_chip_data(virq, tc3589x_gpio);
|
||||||
irq_set_chip_data(irq, tc3589x_gpio);
|
irq_set_chip_and_handler(virq, &tc3589x_gpio_irq_chip,
|
||||||
irq_set_chip_and_handler(irq, &tc3589x_gpio_irq_chip,
|
handle_simple_irq);
|
||||||
handle_simple_irq);
|
irq_set_nested_thread(virq, 1);
|
||||||
irq_set_nested_thread(irq, 1);
|
|
||||||
#ifdef CONFIG_ARM
|
#ifdef CONFIG_ARM
|
||||||
set_irq_flags(irq, IRQF_VALID);
|
set_irq_flags(virq, IRQF_VALID);
|
||||||
#else
|
#else
|
||||||
irq_set_noprobe(irq);
|
irq_set_noprobe(virq);
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tc3589x_gpio_irq_remove(struct tc3589x_gpio *tc3589x_gpio)
|
static void tc3589x_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 struct irq_domain_ops tc3589x_irq_ops = {
|
||||||
|
.map = tc3589x_gpio_irq_map,
|
||||||
|
.unmap = tc3589x_gpio_irq_unmap,
|
||||||
|
.xlate = irq_domain_xlate_twocell,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio,
|
||||||
|
struct device_node *np)
|
||||||
{
|
{
|
||||||
int base = tc3589x_gpio->irq_base;
|
int base = tc3589x_gpio->irq_base;
|
||||||
int irq;
|
|
||||||
|
|
||||||
for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) {
|
if (base) {
|
||||||
#ifdef CONFIG_ARM
|
tc3589x_gpio->domain = irq_domain_add_legacy(
|
||||||
set_irq_flags(irq, 0);
|
NULL, tc3589x_gpio->chip.ngpio, base,
|
||||||
#endif
|
0, &tc3589x_irq_ops, tc3589x_gpio);
|
||||||
irq_set_chip_and_handler(irq, NULL, NULL);
|
|
||||||
irq_set_chip_data(irq, NULL);
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
tc3589x_gpio->domain = irq_domain_add_linear(
|
||||||
|
np, tc3589x_gpio->chip.ngpio,
|
||||||
|
&tc3589x_irq_ops, tc3589x_gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tc3589x_gpio->domain) {
|
||||||
|
dev_err(tc3589x_gpio->dev, "Failed to create irqdomain\n");
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
|
static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
|
struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
|
||||||
struct tc3589x_gpio_platform_data *pdata;
|
struct tc3589x_gpio_platform_data *pdata;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
struct tc3589x_gpio *tc3589x_gpio;
|
struct tc3589x_gpio *tc3589x_gpio;
|
||||||
int ret;
|
int ret;
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
pdata = tc3589x->pdata->gpio;
|
pdata = tc3589x->pdata->gpio;
|
||||||
if (!pdata)
|
|
||||||
return -ENODEV;
|
if (!(pdata || np)) {
|
||||||
|
dev_err(&pdev->dev, "No platform data or Device Tree found\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
|
@ -295,9 +343,14 @@ static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
|
||||||
tc3589x_gpio->chip = template_chip;
|
tc3589x_gpio->chip = template_chip;
|
||||||
tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;
|
tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;
|
||||||
tc3589x_gpio->chip.dev = &pdev->dev;
|
tc3589x_gpio->chip.dev = &pdev->dev;
|
||||||
tc3589x_gpio->chip.base = pdata->gpio_base;
|
tc3589x_gpio->chip.base = (pdata) ? pdata->gpio_base : -1;
|
||||||
|
|
||||||
tc3589x_gpio->irq_base = tc3589x->irq_base + TC3589x_INT_GPIO(0);
|
#ifdef CONFIG_OF_GPIO
|
||||||
|
tc3589x_gpio->chip.of_node = np;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tc3589x_gpio->irq_base = tc3589x->irq_base ?
|
||||||
|
tc3589x->irq_base + TC3589x_INT_GPIO(0) : 0;
|
||||||
|
|
||||||
/* Bring the GPIO module out of reset */
|
/* Bring the GPIO module out of reset */
|
||||||
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL,
|
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL,
|
||||||
|
@ -305,7 +358,7 @@ static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
|
||||||
ret = tc3589x_gpio_irq_init(tc3589x_gpio);
|
ret = tc3589x_gpio_irq_init(tc3589x_gpio, np);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
|
||||||
|
@ -313,7 +366,7 @@ static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
|
||||||
"tc3589x-gpio", tc3589x_gpio);
|
"tc3589x-gpio", tc3589x_gpio);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
|
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
|
||||||
goto out_removeirq;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = gpiochip_add(&tc3589x_gpio->chip);
|
ret = gpiochip_add(&tc3589x_gpio->chip);
|
||||||
|
@ -322,7 +375,7 @@ static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
|
||||||
goto out_freeirq;
|
goto out_freeirq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->setup)
|
if (pdata && pdata->setup)
|
||||||
pdata->setup(tc3589x, tc3589x_gpio->chip.base);
|
pdata->setup(tc3589x, tc3589x_gpio->chip.base);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, tc3589x_gpio);
|
platform_set_drvdata(pdev, tc3589x_gpio);
|
||||||
|
@ -331,8 +384,6 @@ static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
out_freeirq:
|
out_freeirq:
|
||||||
free_irq(irq, tc3589x_gpio);
|
free_irq(irq, tc3589x_gpio);
|
||||||
out_removeirq:
|
|
||||||
tc3589x_gpio_irq_remove(tc3589x_gpio);
|
|
||||||
out_free:
|
out_free:
|
||||||
kfree(tc3589x_gpio);
|
kfree(tc3589x_gpio);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -346,7 +397,7 @@ static int __devexit tc3589x_gpio_remove(struct platform_device *pdev)
|
||||||
int irq = platform_get_irq(pdev, 0);
|
int irq = platform_get_irq(pdev, 0);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (pdata->remove)
|
if (pdata && pdata->remove)
|
||||||
pdata->remove(tc3589x, tc3589x_gpio->chip.base);
|
pdata->remove(tc3589x, tc3589x_gpio->chip.base);
|
||||||
|
|
||||||
ret = gpiochip_remove(&tc3589x_gpio->chip);
|
ret = gpiochip_remove(&tc3589x_gpio->chip);
|
||||||
|
@ -357,7 +408,6 @@ static int __devexit tc3589x_gpio_remove(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
free_irq(irq, tc3589x_gpio);
|
free_irq(irq, tc3589x_gpio);
|
||||||
tc3589x_gpio_irq_remove(tc3589x_gpio);
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
kfree(tc3589x_gpio);
|
kfree(tc3589x_gpio);
|
||||||
|
|
|
@ -70,7 +70,6 @@ static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset)
|
||||||
|
|
||||||
return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
|
return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
|
||||||
GPIO_CFG_MASK);
|
GPIO_CFG_MASK);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct gpio_chip template_chip = {
|
static struct gpio_chip template_chip = {
|
||||||
|
@ -92,7 +91,8 @@ static int __devinit tps65912_gpio_probe(struct platform_device *pdev)
|
||||||
struct tps65912_gpio_data *tps65912_gpio;
|
struct tps65912_gpio_data *tps65912_gpio;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
tps65912_gpio = kzalloc(sizeof(*tps65912_gpio), GFP_KERNEL);
|
tps65912_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65912_gpio),
|
||||||
|
GFP_KERNEL);
|
||||||
if (tps65912_gpio == NULL)
|
if (tps65912_gpio == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -105,28 +105,19 @@ static int __devinit tps65912_gpio_probe(struct platform_device *pdev)
|
||||||
ret = gpiochip_add(&tps65912_gpio->gpio_chip);
|
ret = gpiochip_add(&tps65912_gpio->gpio_chip);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
|
dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
|
||||||
goto err;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, tps65912_gpio);
|
platform_set_drvdata(pdev, tps65912_gpio);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
err:
|
|
||||||
kfree(tps65912_gpio);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devexit tps65912_gpio_remove(struct platform_device *pdev)
|
static int __devexit tps65912_gpio_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev);
|
struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev);
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = gpiochip_remove(&tps65912_gpio->gpio_chip);
|
return gpiochip_remove(&tps65912_gpio->gpio_chip);
|
||||||
if (ret == 0)
|
|
||||||
kfree(tps65912_gpio);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver tps65912_gpio_driver = {
|
static struct platform_driver tps65912_gpio_driver = {
|
||||||
|
|
|
@ -250,7 +250,8 @@ static int __devinit wm831x_gpio_probe(struct platform_device *pdev)
|
||||||
struct wm831x_gpio *wm831x_gpio;
|
struct wm831x_gpio *wm831x_gpio;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
wm831x_gpio = kzalloc(sizeof(*wm831x_gpio), GFP_KERNEL);
|
wm831x_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm831x_gpio),
|
||||||
|
GFP_KERNEL);
|
||||||
if (wm831x_gpio == NULL)
|
if (wm831x_gpio == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -265,30 +266,20 @@ static int __devinit wm831x_gpio_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ret = gpiochip_add(&wm831x_gpio->gpio_chip);
|
ret = gpiochip_add(&wm831x_gpio->gpio_chip);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
|
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
|
||||||
ret);
|
return ret;
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, wm831x_gpio);
|
platform_set_drvdata(pdev, wm831x_gpio);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
err:
|
|
||||||
kfree(wm831x_gpio);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devexit wm831x_gpio_remove(struct platform_device *pdev)
|
static int __devexit wm831x_gpio_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev);
|
struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev);
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = gpiochip_remove(&wm831x_gpio->gpio_chip);
|
return gpiochip_remove(&wm831x_gpio->gpio_chip);
|
||||||
if (ret == 0)
|
|
||||||
kfree(wm831x_gpio);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver wm831x_gpio_driver = {
|
static struct platform_driver wm831x_gpio_driver = {
|
||||||
|
|
|
@ -116,7 +116,8 @@ static int __devinit wm8350_gpio_probe(struct platform_device *pdev)
|
||||||
struct wm8350_gpio_data *wm8350_gpio;
|
struct wm8350_gpio_data *wm8350_gpio;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
wm8350_gpio = kzalloc(sizeof(*wm8350_gpio), GFP_KERNEL);
|
wm8350_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm8350_gpio),
|
||||||
|
GFP_KERNEL);
|
||||||
if (wm8350_gpio == NULL)
|
if (wm8350_gpio == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -131,30 +132,20 @@ static int __devinit wm8350_gpio_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ret = gpiochip_add(&wm8350_gpio->gpio_chip);
|
ret = gpiochip_add(&wm8350_gpio->gpio_chip);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
|
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
|
||||||
ret);
|
return ret;
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, wm8350_gpio);
|
platform_set_drvdata(pdev, wm8350_gpio);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
err:
|
|
||||||
kfree(wm8350_gpio);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devexit wm8350_gpio_remove(struct platform_device *pdev)
|
static int __devexit wm8350_gpio_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct wm8350_gpio_data *wm8350_gpio = platform_get_drvdata(pdev);
|
struct wm8350_gpio_data *wm8350_gpio = platform_get_drvdata(pdev);
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = gpiochip_remove(&wm8350_gpio->gpio_chip);
|
return gpiochip_remove(&wm8350_gpio->gpio_chip);
|
||||||
if (ret == 0)
|
|
||||||
kfree(wm8350_gpio);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver wm8350_gpio_driver = {
|
static struct platform_driver wm8350_gpio_driver = {
|
||||||
|
|
|
@ -1773,56 +1773,102 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpiolib_show(struct seq_file *s, void *unused)
|
static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
|
||||||
{
|
{
|
||||||
struct gpio_chip *chip = NULL;
|
struct gpio_chip *chip = NULL;
|
||||||
unsigned gpio;
|
unsigned int gpio;
|
||||||
int started = 0;
|
void *ret = NULL;
|
||||||
|
loff_t index = 0;
|
||||||
|
|
||||||
/* REVISIT this isn't locked against gpio_chip removal ... */
|
/* REVISIT this isn't locked against gpio_chip removal ... */
|
||||||
|
|
||||||
for (gpio = 0; gpio_is_valid(gpio); gpio++) {
|
for (gpio = 0; gpio_is_valid(gpio); gpio++) {
|
||||||
struct device *dev;
|
if (gpio_desc[gpio].chip == chip)
|
||||||
|
|
||||||
if (chip == gpio_desc[gpio].chip)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
chip = gpio_desc[gpio].chip;
|
chip = gpio_desc[gpio].chip;
|
||||||
if (!chip)
|
if (!chip)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
seq_printf(s, "%sGPIOs %d-%d",
|
if (index++ >= *pos) {
|
||||||
started ? "\n" : "",
|
ret = chip;
|
||||||
chip->base, chip->base + chip->ngpio - 1);
|
break;
|
||||||
dev = chip->dev;
|
}
|
||||||
if (dev)
|
|
||||||
seq_printf(s, ", %s/%s",
|
|
||||||
dev->bus ? dev->bus->name : "no-bus",
|
|
||||||
dev_name(dev));
|
|
||||||
if (chip->label)
|
|
||||||
seq_printf(s, ", %s", chip->label);
|
|
||||||
if (chip->can_sleep)
|
|
||||||
seq_printf(s, ", can sleep");
|
|
||||||
seq_printf(s, ":\n");
|
|
||||||
|
|
||||||
started = 1;
|
|
||||||
if (chip->dbg_show)
|
|
||||||
chip->dbg_show(s, chip);
|
|
||||||
else
|
|
||||||
gpiolib_dbg_show(s, chip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->private = "";
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||||
|
{
|
||||||
|
struct gpio_chip *chip = v;
|
||||||
|
unsigned int gpio;
|
||||||
|
void *ret = NULL;
|
||||||
|
|
||||||
|
/* skip GPIOs provided by the current chip */
|
||||||
|
for (gpio = chip->base + chip->ngpio; gpio_is_valid(gpio); gpio++) {
|
||||||
|
chip = gpio_desc[gpio].chip;
|
||||||
|
if (chip) {
|
||||||
|
ret = chip;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s->private = "\n";
|
||||||
|
++*pos;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpiolib_seq_stop(struct seq_file *s, void *v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpiolib_seq_show(struct seq_file *s, void *v)
|
||||||
|
{
|
||||||
|
struct gpio_chip *chip = v;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
seq_printf(s, "%sGPIOs %d-%d", (char *)s->private,
|
||||||
|
chip->base, chip->base + chip->ngpio - 1);
|
||||||
|
dev = chip->dev;
|
||||||
|
if (dev)
|
||||||
|
seq_printf(s, ", %s/%s", dev->bus ? dev->bus->name : "no-bus",
|
||||||
|
dev_name(dev));
|
||||||
|
if (chip->label)
|
||||||
|
seq_printf(s, ", %s", chip->label);
|
||||||
|
if (chip->can_sleep)
|
||||||
|
seq_printf(s, ", can sleep");
|
||||||
|
seq_printf(s, ":\n");
|
||||||
|
|
||||||
|
if (chip->dbg_show)
|
||||||
|
chip->dbg_show(s, chip);
|
||||||
|
else
|
||||||
|
gpiolib_dbg_show(s, chip);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct seq_operations gpiolib_seq_ops = {
|
||||||
|
.start = gpiolib_seq_start,
|
||||||
|
.next = gpiolib_seq_next,
|
||||||
|
.stop = gpiolib_seq_stop,
|
||||||
|
.show = gpiolib_seq_show,
|
||||||
|
};
|
||||||
|
|
||||||
static int gpiolib_open(struct inode *inode, struct file *file)
|
static int gpiolib_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
return single_open(file, gpiolib_show, NULL);
|
return seq_open(file, &gpiolib_seq_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct file_operations gpiolib_operations = {
|
static const struct file_operations gpiolib_operations = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
.open = gpiolib_open,
|
.open = gpiolib_open,
|
||||||
.read = seq_read,
|
.read = seq_read,
|
||||||
.llseek = seq_lseek,
|
.llseek = seq_lseek,
|
||||||
.release = single_release,
|
.release = seq_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init gpiolib_debugfs_init(void)
|
static int __init gpiolib_debugfs_init(void)
|
||||||
|
|
|
@ -60,6 +60,8 @@ struct device_node;
|
||||||
* @get: returns value for signal "offset"; for output signals this
|
* @get: returns value for signal "offset"; for output signals this
|
||||||
* returns either the value actually sensed, or zero
|
* returns either the value actually sensed, or zero
|
||||||
* @direction_output: configures signal "offset" as output, or returns error
|
* @direction_output: configures signal "offset" as output, or returns error
|
||||||
|
* @set_debounce: optional hook for setting debounce time for specified gpio in
|
||||||
|
* interrupt triggered gpio chips
|
||||||
* @set: assigns output value for signal "offset"
|
* @set: assigns output value for signal "offset"
|
||||||
* @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
|
* @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
|
||||||
* implementation may not sleep
|
* implementation may not sleep
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
* @setup: optional callback issued once the GPIOs are valid
|
* @setup: optional callback issued once the GPIOs are valid
|
||||||
* @teardown: optional callback issued before the GPIOs are invalidated
|
* @teardown: optional callback issued before the GPIOs are invalidated
|
||||||
* @context: optional parameter passed to setup() and teardown()
|
* @context: optional parameter passed to setup() and teardown()
|
||||||
|
* @irq: optional interrupt number
|
||||||
*
|
*
|
||||||
* In addition to the I2C_BOARD_INFO() state appropriate to each chip,
|
* In addition to the I2C_BOARD_INFO() state appropriate to each chip,
|
||||||
* the i2c_board_info used with the pcf875x driver must provide its
|
* the i2c_board_info used with the pcf875x driver must provide its
|
||||||
|
@ -39,6 +40,8 @@ struct pcf857x_platform_data {
|
||||||
int gpio, unsigned ngpio,
|
int gpio, unsigned ngpio,
|
||||||
void *context);
|
void *context);
|
||||||
void *context;
|
void *context;
|
||||||
|
|
||||||
|
int irq;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __LINUX_PCF857X_H */
|
#endif /* __LINUX_PCF857X_H */
|
||||||
|
|
Loading…
Reference in New Issue