Merge tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6

Pull GPIO updates from Grant Likely:
 "GPIO follow up patch and type change for v3.5 merge window

  Primarily device driver additions, features and bug fixes.  Not much
  touching gpio common subsystem support.  Should not be scary."

* tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6: (34 commits)
  gpio: Provide the STMPE GPIO driver with its own IRQ Domain
  gpio: add TS-5500 DIO blocks support
  gpio: pcf857x: use client->irq for gpio_to_irq()
  gpio: stmpe: Add DT support for stmpe gpio
  gpio: pl061 depends on ARM
  gpio/pl061: remove old comment
  gpio: SPEAr: add spi chipselect control driver
  gpio: gpio-max710x: Support device tree probing
  gpio: twl4030: Use only TWL4030_MODULE_LED for LED configuration
  gpio: tegra: read output value when gpio is set in direction_out
  gpio: pca953x: Add compatible strings to gpio-pca953x driver
  gpio: pca953x: Register an IRQ domain
  gpio: mvebu: Set free callback for gpio_chip
  gpio: tegra: Drop exporting static functions
  gpio: tegra: Staticize non-exported symbols
  gpio: tegra: fix suspend/resume apis
  gpio-pch: Set parent dev for gpio chip
  gpio: em: Fix build errors
  GPIO: clps711x: use platform_device_unregister in gpio_clps711x_init()
  gpio/tc3589x: convert to use the simple irqdomain
  ...
This commit is contained in:
Linus Torvalds
2012-12-11 13:00:56 -08:00
29 changed files with 1528 additions and 249 deletions

View File

@@ -90,11 +90,26 @@ config GPIO_DA9052
help
Say yes here to enable the GPIO driver for the DA9052 chip.
config GPIO_DA9055
tristate "Dialog Semiconductor DA9055 GPIO"
depends on MFD_DA9055
help
Say yes here to enable the GPIO driver for the DA9055 chip.
The Dialog DA9055 PMIC chip has 3 GPIO pins that can be
be controller by this driver.
If driver is built as a module it will be called gpio-da9055.
config GPIO_MAX730X
tristate
comment "Memory mapped GPIO drivers:"
config GPIO_CLPS711X
def_bool y
depends on ARCH_CLPS711X
config GPIO_GENERIC_PLATFORM
tristate "Generic memory-mapped GPIO controller support (MMIO platform device)"
select GPIO_GENERIC
@@ -174,7 +189,7 @@ config GPIO_MXS
config GPIO_PL061
bool "PrimeCell PL061 GPIO support"
depends on ARM_AMBA
depends on ARM && ARM_AMBA
select GENERIC_IRQ_CHIP
help
Say yes here to support the PrimeCell PL061 GPIO device
@@ -185,6 +200,13 @@ config GPIO_PXA
help
Say yes here to support the PXA GPIO device
config GPIO_SPEAR_SPICS
bool "ST SPEAr13xx SPI Chip Select as GPIO support"
depends on PLAT_SPEAR
select GENERIC_IRQ_CHIP
help
Say yes here to support ST SPEAr SPI Chip Select as GPIO device
config GPIO_STA2X11
bool "STA2x11/ConneXt GPIO support"
depends on MFD_STA2X11
@@ -193,6 +215,14 @@ config GPIO_STA2X11
Say yes here to support the STA2x11/ConneXt GPIO device.
The GPIO module has 128 GPIO pins with alternate functions.
config GPIO_TS5500
tristate "TS-5500 DIO blocks and compatibles"
help
This driver supports Digital I/O exposed by pin blocks found on some
Technologic Systems platforms. It includes, but is not limited to, 3
blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
LCD port.
config GPIO_VT8500
bool "VIA/Wondermedia SoC GPIO Support"
depends on ARCH_VT8500

View File

@@ -17,8 +17,10 @@ obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o
obj-$(CONFIG_GPIO_EM) += gpio-em.o
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
@@ -58,6 +60,7 @@ obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
@@ -69,6 +72,7 @@ obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o
obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o

View File

@@ -0,0 +1,199 @@
/*
* CLPS711X GPIO driver
*
* Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <mach/hardware.h>
#define CLPS711X_GPIO_PORTS 5
#define CLPS711X_GPIO_NAME "gpio-clps711x"
struct clps711x_gpio {
struct gpio_chip chip[CLPS711X_GPIO_PORTS];
spinlock_t lock;
};
static void __iomem *clps711x_ports[] = {
CLPS711X_VIRT_BASE + PADR,
CLPS711X_VIRT_BASE + PBDR,
CLPS711X_VIRT_BASE + PCDR,
CLPS711X_VIRT_BASE + PDDR,
CLPS711X_VIRT_BASE + PEDR,
};
static void __iomem *clps711x_pdirs[] = {
CLPS711X_VIRT_BASE + PADDR,
CLPS711X_VIRT_BASE + PBDDR,
CLPS711X_VIRT_BASE + PCDDR,
CLPS711X_VIRT_BASE + PDDDR,
CLPS711X_VIRT_BASE + PEDDR,
};
#define clps711x_port(x) clps711x_ports[x->base / 8]
#define clps711x_pdir(x) clps711x_pdirs[x->base / 8]
static int gpio_clps711x_get(struct gpio_chip *chip, unsigned offset)
{
return !!(readb(clps711x_port(chip)) & (1 << offset));
}
static void gpio_clps711x_set(struct gpio_chip *chip, unsigned offset,
int value)
{
int tmp;
unsigned long flags;
struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
spin_lock_irqsave(&gpio->lock, flags);
tmp = readb(clps711x_port(chip)) & ~(1 << offset);
if (value)
tmp |= 1 << offset;
writeb(tmp, clps711x_port(chip));
spin_unlock_irqrestore(&gpio->lock, flags);
}
static int gpio_clps711x_dir_in(struct gpio_chip *chip, unsigned offset)
{
int tmp;
unsigned long flags;
struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
spin_lock_irqsave(&gpio->lock, flags);
tmp = readb(clps711x_pdir(chip)) & ~(1 << offset);
writeb(tmp, clps711x_pdir(chip));
spin_unlock_irqrestore(&gpio->lock, flags);
return 0;
}
static int gpio_clps711x_dir_out(struct gpio_chip *chip, unsigned offset,
int value)
{
int tmp;
unsigned long flags;
struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
spin_lock_irqsave(&gpio->lock, flags);
tmp = readb(clps711x_pdir(chip)) | (1 << offset);
writeb(tmp, clps711x_pdir(chip));
tmp = readb(clps711x_port(chip)) & ~(1 << offset);
if (value)
tmp |= 1 << offset;
writeb(tmp, clps711x_port(chip));
spin_unlock_irqrestore(&gpio->lock, flags);
return 0;
}
static int gpio_clps711x_dir_in_inv(struct gpio_chip *chip, unsigned offset)
{
int tmp;
unsigned long flags;
struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
spin_lock_irqsave(&gpio->lock, flags);
tmp = readb(clps711x_pdir(chip)) | (1 << offset);
writeb(tmp, clps711x_pdir(chip));
spin_unlock_irqrestore(&gpio->lock, flags);
return 0;
}
static int gpio_clps711x_dir_out_inv(struct gpio_chip *chip, unsigned offset,
int value)
{
int tmp;
unsigned long flags;
struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev);
spin_lock_irqsave(&gpio->lock, flags);
tmp = readb(clps711x_pdir(chip)) & ~(1 << offset);
writeb(tmp, clps711x_pdir(chip));
tmp = readb(clps711x_port(chip)) & ~(1 << offset);
if (value)
tmp |= 1 << offset;
writeb(tmp, clps711x_port(chip));
spin_unlock_irqrestore(&gpio->lock, flags);
return 0;
}
static struct {
char *name;
int nr;
int inv_dir;
} clps711x_gpio_ports[] __initconst = {
{ "PORTA", 8, 0, },
{ "PORTB", 8, 0, },
{ "PORTC", 8, 0, },
{ "PORTD", 8, 1, },
{ "PORTE", 3, 0, },
};
static int __init gpio_clps711x_init(void)
{
int i;
struct platform_device *pdev;
struct clps711x_gpio *gpio;
pdev = platform_device_alloc(CLPS711X_GPIO_NAME, 0);
if (!pdev) {
pr_err("Cannot create platform device: %s\n",
CLPS711X_GPIO_NAME);
return -ENOMEM;
}
platform_device_add(pdev);
gpio = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_gpio),
GFP_KERNEL);
if (!gpio) {
dev_err(&pdev->dev, "GPIO allocating memory error\n");
platform_device_unregister(pdev);
return -ENOMEM;
}
platform_set_drvdata(pdev, gpio);
spin_lock_init(&gpio->lock);
for (i = 0; i < CLPS711X_GPIO_PORTS; i++) {
gpio->chip[i].owner = THIS_MODULE;
gpio->chip[i].dev = &pdev->dev;
gpio->chip[i].label = clps711x_gpio_ports[i].name;
gpio->chip[i].base = i * 8;
gpio->chip[i].ngpio = clps711x_gpio_ports[i].nr;
gpio->chip[i].get = gpio_clps711x_get;
gpio->chip[i].set = gpio_clps711x_set;
if (!clps711x_gpio_ports[i].inv_dir) {
gpio->chip[i].direction_input = gpio_clps711x_dir_in;
gpio->chip[i].direction_output = gpio_clps711x_dir_out;
} else {
gpio->chip[i].direction_input = gpio_clps711x_dir_in_inv;
gpio->chip[i].direction_output = gpio_clps711x_dir_out_inv;
}
WARN_ON(gpiochip_add(&gpio->chip[i]));
}
dev_info(&pdev->dev, "GPIO driver initialized\n");
return 0;
}
arch_initcall(gpio_clps711x_init);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
MODULE_DESCRIPTION("CLPS711X GPIO driver");

204
drivers/gpio/gpio-da9055.c Normal file
View File

@@ -0,0 +1,204 @@
/*
* GPIO Driver for Dialog DA9055 PMICs.
*
* Copyright(c) 2012 Dialog Semiconductor Ltd.
*
* Author: David Dajun Chen <dchen@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/mfd/da9055/core.h>
#include <linux/mfd/da9055/reg.h>
#include <linux/mfd/da9055/pdata.h>
#define DA9055_VDD_IO 0x0
#define DA9055_PUSH_PULL 0x3
#define DA9055_ACT_LOW 0x0
#define DA9055_GPI 0x1
#define DA9055_PORT_MASK 0x3
#define DA9055_PORT_SHIFT(offset) (4 * (offset % 2))
#define DA9055_INPUT DA9055_GPI
#define DA9055_OUTPUT DA9055_PUSH_PULL
#define DA9055_IRQ_GPI0 3
struct da9055_gpio {
struct da9055 *da9055;
struct gpio_chip gp;
};
static inline struct da9055_gpio *to_da9055_gpio(struct gpio_chip *chip)
{
return container_of(chip, struct da9055_gpio, gp);
}
static int da9055_gpio_get(struct gpio_chip *gc, unsigned offset)
{
struct da9055_gpio *gpio = to_da9055_gpio(gc);
int gpio_direction = 0;
int ret;
/* Get GPIO direction */
ret = da9055_reg_read(gpio->da9055, (offset >> 1) + DA9055_REG_GPIO0_1);
if (ret < 0)
return ret;
gpio_direction = ret & (DA9055_PORT_MASK) << DA9055_PORT_SHIFT(offset);
gpio_direction >>= DA9055_PORT_SHIFT(offset);
switch (gpio_direction) {
case DA9055_INPUT:
ret = da9055_reg_read(gpio->da9055, DA9055_REG_STATUS_B);
if (ret < 0)
return ret;
break;
case DA9055_OUTPUT:
ret = da9055_reg_read(gpio->da9055, DA9055_REG_GPIO_MODE0_2);
if (ret < 0)
return ret;
}
return ret & (1 << offset);
}
static void da9055_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
{
struct da9055_gpio *gpio = to_da9055_gpio(gc);
da9055_reg_update(gpio->da9055,
DA9055_REG_GPIO_MODE0_2,
1 << offset,
value << offset);
}
static int da9055_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
struct da9055_gpio *gpio = to_da9055_gpio(gc);
unsigned char reg_byte;
reg_byte = (DA9055_ACT_LOW | DA9055_GPI)
<< DA9055_PORT_SHIFT(offset);
return da9055_reg_update(gpio->da9055, (offset >> 1) +
DA9055_REG_GPIO0_1,
DA9055_PORT_MASK <<
DA9055_PORT_SHIFT(offset),
reg_byte);
}
static int da9055_gpio_direction_output(struct gpio_chip *gc,
unsigned offset, int value)
{
struct da9055_gpio *gpio = to_da9055_gpio(gc);
unsigned char reg_byte;
int ret;
reg_byte = (DA9055_VDD_IO | DA9055_PUSH_PULL)
<< DA9055_PORT_SHIFT(offset);
ret = da9055_reg_update(gpio->da9055, (offset >> 1) +
DA9055_REG_GPIO0_1,
DA9055_PORT_MASK <<
DA9055_PORT_SHIFT(offset),
reg_byte);
if (ret < 0)
return ret;
da9055_gpio_set(gc, offset, value);
return 0;
}
static int da9055_gpio_to_irq(struct gpio_chip *gc, u32 offset)
{
struct da9055_gpio *gpio = to_da9055_gpio(gc);
struct da9055 *da9055 = gpio->da9055;
return regmap_irq_get_virq(da9055->irq_data,
DA9055_IRQ_GPI0 + offset);
}
static struct gpio_chip reference_gp __devinitdata = {
.label = "da9055-gpio",
.owner = THIS_MODULE,
.get = da9055_gpio_get,
.set = da9055_gpio_set,
.direction_input = da9055_gpio_direction_input,
.direction_output = da9055_gpio_direction_output,
.to_irq = da9055_gpio_to_irq,
.can_sleep = 1,
.ngpio = 3,
.base = -1,
};
static int __devinit da9055_gpio_probe(struct platform_device *pdev)
{
struct da9055_gpio *gpio;
struct da9055_pdata *pdata;
int ret;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (gpio == NULL)
return -ENOMEM;
gpio->da9055 = dev_get_drvdata(pdev->dev.parent);
pdata = gpio->da9055->dev->platform_data;
gpio->gp = reference_gp;
if (pdata && pdata->gpio_base)
gpio->gp.base = pdata->gpio_base;
ret = gpiochip_add(&gpio->gp);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
goto err_mem;
}
platform_set_drvdata(pdev, gpio);
return 0;
err_mem:
return ret;
}
static int __devexit da9055_gpio_remove(struct platform_device *pdev)
{
struct da9055_gpio *gpio = platform_get_drvdata(pdev);
return gpiochip_remove(&gpio->gp);
}
static struct platform_driver da9055_gpio_driver = {
.probe = da9055_gpio_probe,
.remove = __devexit_p(da9055_gpio_remove),
.driver = {
.name = "da9055-gpio",
.owner = THIS_MODULE,
},
};
static int __init da9055_gpio_init(void)
{
return platform_driver_register(&da9055_gpio_driver);
}
subsys_initcall(da9055_gpio_init);
static void __exit da9055_gpio_exit(void)
{
platform_driver_unregister(&da9055_gpio_driver);
}
module_exit(da9055_gpio_exit);
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
MODULE_DESCRIPTION("DA9055 GPIO Device Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:da9055-gpio");

View File

@@ -35,7 +35,6 @@
struct em_gio_priv {
void __iomem *base0;
void __iomem *base1;
unsigned int irq_base;
spinlock_t sense_lock;
struct platform_device *pdev;
struct gpio_chip gpio_chip;
@@ -214,7 +213,7 @@ static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset,
static int em_gio_to_irq(struct gpio_chip *chip, unsigned offset)
{
return irq_find_mapping(gpio_to_priv(chip)->irq_domain, offset);
return irq_create_mapping(gpio_to_priv(chip)->irq_domain, offset);
}
static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int virq,
@@ -234,40 +233,6 @@ static struct irq_domain_ops em_gio_irq_domain_ops = {
.map = em_gio_irq_domain_map,
};
static int __devinit em_gio_irq_domain_init(struct em_gio_priv *p)
{
struct platform_device *pdev = p->pdev;
struct gpio_em_config *pdata = pdev->dev.platform_data;
p->irq_base = irq_alloc_descs(pdata->irq_base, 0,
pdata->number_of_pins, numa_node_id());
if (p->irq_base < 0) {
dev_err(&pdev->dev, "cannot get irq_desc\n");
return p->irq_base;
}
pr_debug("gio: hw base = %d, nr = %d, sw base = %d\n",
pdata->gpio_base, pdata->number_of_pins, p->irq_base);
p->irq_domain = irq_domain_add_legacy(pdev->dev.of_node,
pdata->number_of_pins,
p->irq_base, 0,
&em_gio_irq_domain_ops, p);
if (!p->irq_domain) {
irq_free_descs(p->irq_base, pdata->number_of_pins);
return -ENXIO;
}
return 0;
}
static void em_gio_irq_domain_cleanup(struct em_gio_priv *p)
{
struct gpio_em_config *pdata = p->pdev->dev.platform_data;
irq_free_descs(p->irq_base, pdata->number_of_pins);
/* FIXME: irq domain wants to be freed! */
}
static int __devinit em_gio_probe(struct platform_device *pdev)
{
struct gpio_em_config *pdata = pdev->dev.platform_data;
@@ -334,8 +299,11 @@ static int __devinit em_gio_probe(struct platform_device *pdev)
irq_chip->irq_set_type = em_gio_irq_set_type;
irq_chip->flags = IRQCHIP_SKIP_SET_WAKE;
ret = em_gio_irq_domain_init(p);
if (ret) {
p->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
pdata->number_of_pins,
&em_gio_irq_domain_ops, p);
if (!p->irq_domain) {
ret = -ENXIO;
dev_err(&pdev->dev, "cannot initialize irq domain\n");
goto err3;
}
@@ -364,7 +332,7 @@ err6:
err5:
free_irq(irq[0]->start, pdev);
err4:
em_gio_irq_domain_cleanup(p);
irq_domain_remove(p->irq_domain);
err3:
iounmap(p->base1);
err2:
@@ -390,7 +358,7 @@ static int __devexit em_gio_remove(struct platform_device *pdev)
free_irq(irq[1]->start, pdev);
free_irq(irq[0]->start, pdev);
em_gio_irq_domain_cleanup(p);
irq_domain_remove(p->irq_domain);
iounmap(p->base1);
iounmap(p->base0);
kfree(p);

View File

@@ -167,10 +167,6 @@ int __devinit __max730x_probe(struct max7301 *ts)
int i, ret;
pdata = dev->platform_data;
if (!pdata || !pdata->base) {
dev_err(dev, "incorrect or missing platform data\n");
return -EINVAL;
}
mutex_init(&ts->lock);
dev_set_drvdata(dev, ts);
@@ -178,7 +174,12 @@ int __devinit __max730x_probe(struct max7301 *ts)
/* Power up the chip and disable IRQ output */
ts->write(dev, 0x04, 0x01);
ts->input_pullup_active = pdata->input_pullup_active;
if (pdata) {
ts->input_pullup_active = pdata->input_pullup_active;
ts->chip.base = pdata->base;
} else {
ts->chip.base = -1;
}
ts->chip.label = dev->driver->name;
ts->chip.direction_input = max7301_direction_input;
@@ -186,7 +187,6 @@ int __devinit __max730x_probe(struct max7301 *ts)
ts->chip.direction_output = max7301_direction_output;
ts->chip.set = max7301_set;
ts->chip.base = pdata->base;
ts->chip.ngpio = PIN_NUMBER;
ts->chip.can_sleep = 1;
ts->chip.dev = dev;

View File

@@ -168,12 +168,12 @@ static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip)
* Functions implementing the gpio_chip methods
*/
int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin)
static int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin)
{
return pinctrl_request_gpio(chip->base + pin);
}
void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin)
static void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin)
{
pinctrl_free_gpio(chip->base + pin);
}
@@ -546,6 +546,7 @@ static int __devinit mvebu_gpio_probe(struct platform_device *pdev)
mvchip->chip.label = dev_name(&pdev->dev);
mvchip->chip.dev = &pdev->dev;
mvchip->chip.request = mvebu_gpio_request;
mvchip->chip.free = mvebu_gpio_free;
mvchip->chip.direction_input = mvebu_gpio_direction_input;
mvchip->chip.get = mvebu_gpio_get;
mvchip->chip.direction_output = mvebu_gpio_direction_output;
@@ -673,8 +674,8 @@ static int __devinit mvebu_gpio_probe(struct platform_device *pdev)
IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE);
/* Setup irq domain on top of the generic chip. */
mvchip->domain = irq_domain_add_legacy(np, mvchip->chip.ngpio,
mvchip->irqbase, 0,
mvchip->domain = irq_domain_add_simple(np, mvchip->chip.ngpio,
mvchip->irqbase,
&irq_domain_simple_ops,
mvchip);
if (!mvchip->domain) {

View File

@@ -1105,7 +1105,7 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
if (!pdata)
return -EINVAL;
bank = devm_kzalloc(&pdev->dev, sizeof(struct gpio_bank), GFP_KERNEL);
bank = devm_kzalloc(dev, sizeof(struct gpio_bank), GFP_KERNEL);
if (!bank) {
dev_err(dev, "Memory alloc failed\n");
return -ENOMEM;

View File

@@ -16,6 +16,7 @@
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/i2c.h>
#include <linux/i2c/pca953x.h>
#include <linux/slab.h>
@@ -83,6 +84,7 @@ struct pca953x_chip {
u32 irq_trig_raise;
u32 irq_trig_fall;
int irq_base;
struct irq_domain *domain;
#endif
struct i2c_client *client;
@@ -333,14 +335,14 @@ static void pca953x_irq_mask(struct irq_data *d)
{
struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
chip->irq_mask &= ~(1 << (d->irq - chip->irq_base));
chip->irq_mask &= ~(1 << d->hwirq);
}
static void pca953x_irq_unmask(struct irq_data *d)
{
struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
chip->irq_mask |= 1 << (d->irq - chip->irq_base);
chip->irq_mask |= 1 << d->hwirq;
}
static void pca953x_irq_bus_lock(struct irq_data *d)
@@ -372,8 +374,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
{
struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
u32 level = d->irq - chip->irq_base;
u32 mask = 1 << level;
u32 mask = 1 << d->hwirq;
if (!(type & IRQ_TYPE_EDGE_BOTH)) {
dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
@@ -454,7 +455,7 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid)
do {
level = __ffs(pending);
handle_nested_irq(level + chip->irq_base);
handle_nested_irq(irq_find_mapping(chip->domain, level));
pending &= ~(1 << level);
} while (pending);
@@ -499,6 +500,17 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
if (chip->irq_base < 0)
goto out_failed;
chip->domain = irq_domain_add_legacy(client->dev.of_node,
chip->gpio_chip.ngpio,
chip->irq_base,
0,
&irq_domain_simple_ops,
NULL);
if (!chip->domain) {
ret = -ENODEV;
goto out_irqdesc_free;
}
for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) {
int irq = lvl + chip->irq_base;
@@ -521,7 +533,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
client->irq);
goto out_failed;
goto out_irqdesc_free;
}
chip->gpio_chip.to_irq = pca953x_gpio_to_irq;
@@ -529,6 +541,8 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
return 0;
out_irqdesc_free:
irq_free_descs(chip->irq_base, chip->gpio_chip.ngpio);
out_failed:
chip->irq_base = -1;
return ret;
@@ -751,9 +765,38 @@ static int pca953x_remove(struct i2c_client *client)
return 0;
}
static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "nxp,pca9534", },
{ .compatible = "nxp,pca9535", },
{ .compatible = "nxp,pca9536", },
{ .compatible = "nxp,pca9537", },
{ .compatible = "nxp,pca9538", },
{ .compatible = "nxp,pca9539", },
{ .compatible = "nxp,pca9554", },
{ .compatible = "nxp,pca9555", },
{ .compatible = "nxp,pca9556", },
{ .compatible = "nxp,pca9557", },
{ .compatible = "nxp,pca9574", },
{ .compatible = "nxp,pca9575", },
{ .compatible = "maxim,max7310", },
{ .compatible = "maxim,max7312", },
{ .compatible = "maxim,max7313", },
{ .compatible = "maxim,max7315", },
{ .compatible = "ti,pca6107", },
{ .compatible = "ti,tca6408", },
{ .compatible = "ti,tca6416", },
{ .compatible = "ti,tca6424", },
{ }
};
MODULE_DEVICE_TABLE(of, pca953x_dt_ids);
static struct i2c_driver pca953x_driver = {
.driver = {
.name = "pca953x",
.of_match_table = pca953x_dt_ids,
},
.probe = pca953x_probe,
.remove = pca953x_remove,

View File

@@ -223,11 +223,11 @@ static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio)
static int pcf857x_irq_domain_init(struct pcf857x *gpio,
struct pcf857x_platform_data *pdata,
struct device *dev)
struct i2c_client *client)
{
int status;
gpio->irq_domain = irq_domain_add_linear(dev->of_node,
gpio->irq_domain = irq_domain_add_linear(client->dev.of_node,
gpio->chip.ngpio,
&pcf857x_irq_domain_ops,
NULL);
@@ -235,15 +235,15 @@ static int pcf857x_irq_domain_init(struct pcf857x *gpio,
goto fail;
/* enable real irq */
status = request_irq(pdata->irq, pcf857x_irq_demux, 0,
dev_name(dev), gpio);
status = request_irq(client->irq, pcf857x_irq_demux, 0,
dev_name(&client->dev), gpio);
if (status)
goto fail;
/* enable gpio_to_irq() */
INIT_WORK(&gpio->work, pcf857x_irq_demux_work);
gpio->chip.to_irq = pcf857x_to_irq;
gpio->irq = pdata->irq;
gpio->irq = client->irq;
return 0;
@@ -285,8 +285,8 @@ static int pcf857x_probe(struct i2c_client *client,
gpio->chip.ngpio = id->driver_data;
/* enable gpio_to_irq() if platform has settings */
if (pdata && pdata->irq) {
status = pcf857x_irq_domain_init(gpio, pdata, &client->dev);
if (pdata && client->irq) {
status = pcf857x_irq_domain_init(gpio, pdata, client);
if (status < 0) {
dev_err(&client->dev, "irq_domain init failed\n");
goto fail;
@@ -368,15 +368,6 @@ static int pcf857x_probe(struct i2c_client *client,
if (status < 0)
goto fail;
/* NOTE: these chips can issue "some pin-changed" IRQs, which we
* don't yet even try to use. Among other issues, the relevant
* genirq state isn't available to modular drivers; and most irq
* methods can't be called from sleeping contexts.
*/
dev_info(&client->dev, "%s\n",
client->irq ? " (irq ignored)" : "");
/* Let platform code set up the GPIOs and their users.
* Now is the first time anyone could use them.
*/
@@ -388,13 +379,15 @@ static int pcf857x_probe(struct i2c_client *client,
dev_warn(&client->dev, "setup --> %d\n", status);
}
dev_info(&client->dev, "probed\n");
return 0;
fail:
dev_dbg(&client->dev, "probe error %d for '%s'\n",
status, client->name);
if (pdata && pdata->irq)
if (pdata && client->irq)
pcf857x_irq_domain_cleanup(gpio);
kfree(gpio);
@@ -418,7 +411,7 @@ static int pcf857x_remove(struct i2c_client *client)
}
}
if (pdata && pdata->irq)
if (pdata && client->irq)
pcf857x_irq_domain_cleanup(gpio);
status = gpiochip_remove(&gpio->chip);

View File

@@ -215,6 +215,7 @@ static void pch_gpio_setup(struct pch_gpio *chip)
struct gpio_chip *gpio = &chip->gpio;
gpio->label = dev_name(chip->dev);
gpio->dev = chip->dev;
gpio->owner = THIS_MODULE;
gpio->direction_input = pch_gpio_direction_input;
gpio->get = pch_gpio_get;

View File

@@ -48,12 +48,7 @@ struct pl061_context_save_regs {
#endif
struct pl061_gpio {
/* Each of the two spinlocks protects a different set of hardware
* regiters and data structurs. This decouples the code of the IRQ from
* the GPIO code. This also makes the case of a GPIO routine call from
* the IRQ code simpler.
*/
spinlock_t lock; /* GPIO registers */
spinlock_t lock;
void __iomem *base;
int irq_base;
@@ -216,39 +211,34 @@ static void __init pl061_init_gc(struct pl061_gpio *chip, int irq_base)
IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0);
}
static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct pl061_platform_data *pdata;
struct device *dev = &adev->dev;
struct pl061_platform_data *pdata = dev->platform_data;
struct pl061_gpio *chip;
int ret, irq, i;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
pdata = dev->dev.platform_data;
if (pdata) {
chip->gc.base = pdata->gpio_base;
chip->irq_base = pdata->irq_base;
} else if (dev->dev.of_node) {
} else if (adev->dev.of_node) {
chip->gc.base = -1;
chip->irq_base = 0;
} else {
ret = -ENODEV;
goto free_mem;
}
} else
return -ENODEV;
if (!request_mem_region(dev->res.start,
resource_size(&dev->res), "pl061")) {
ret = -EBUSY;
goto free_mem;
}
if (!devm_request_mem_region(dev, adev->res.start,
resource_size(&adev->res), "pl061"))
return -EBUSY;
chip->base = ioremap(dev->res.start, resource_size(&dev->res));
if (chip->base == NULL) {
ret = -ENOMEM;
goto release_region;
}
chip->base = devm_ioremap(dev, adev->res.start,
resource_size(&adev->res));
if (chip->base == NULL)
return -ENOMEM;
spin_lock_init(&chip->lock);
@@ -258,13 +248,13 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
chip->gc.set = pl061_set_value;
chip->gc.to_irq = pl061_to_irq;
chip->gc.ngpio = PL061_GPIO_NR;
chip->gc.label = dev_name(&dev->dev);
chip->gc.dev = &dev->dev;
chip->gc.label = dev_name(dev);
chip->gc.dev = dev;
chip->gc.owner = THIS_MODULE;
ret = gpiochip_add(&chip->gc);
if (ret)
goto iounmap;
return ret;
/*
* irq_chip support
@@ -276,11 +266,10 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
pl061_init_gc(chip, chip->irq_base);
writeb(0, chip->base + GPIOIE); /* disable irqs */
irq = dev->irq[0];
if (irq < 0) {
ret = -ENODEV;
goto iounmap;
}
irq = adev->irq[0];
if (irq < 0)
return -ENODEV;
irq_set_chained_handler(irq, pl061_irq_handler);
irq_set_handler_data(irq, chip);
@@ -294,18 +283,9 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
}
}
amba_set_drvdata(dev, chip);
amba_set_drvdata(adev, chip);
return 0;
iounmap:
iounmap(chip->base);
release_region:
release_mem_region(dev->res.start, resource_size(&dev->res));
free_mem:
kfree(chip);
return ret;
}
#ifdef CONFIG_PM

View File

@@ -0,0 +1,217 @@
/*
* SPEAr platform SPI chipselect abstraction over gpiolib
*
* Copyright (C) 2012 ST Microelectronics
* Shiraz Hashim <shiraz.hashim@st.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/types.h>
/* maximum chipselects */
#define NUM_OF_GPIO 4
/*
* Provision is available on some SPEAr SoCs to control ARM PL022 spi cs
* through system registers. This register lies outside spi (pl022)
* address space into system registers.
*
* It provides control for spi chip select lines so that any chipselect
* (out of 4 possible chipselects in pl022) can be made low to select
* the particular slave.
*/
/**
* struct spear_spics - represents spi chip select control
* @base: base address
* @perip_cfg: configuration register
* @sw_enable_bit: bit to enable s/w control over chipselects
* @cs_value_bit: bit to program high or low chipselect
* @cs_enable_mask: mask to select bits required to select chipselect
* @cs_enable_shift: bit pos of cs_enable_mask
* @use_count: use count of a spi controller cs lines
* @last_off: stores last offset caller of set_value()
* @chip: gpio_chip abstraction
*/
struct spear_spics {
void __iomem *base;
u32 perip_cfg;
u32 sw_enable_bit;
u32 cs_value_bit;
u32 cs_enable_mask;
u32 cs_enable_shift;
unsigned long use_count;
int last_off;
struct gpio_chip chip;
};
/* gpio framework specific routines */
static int spics_get_value(struct gpio_chip *chip, unsigned offset)
{
return -ENXIO;
}
static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value)
{
struct spear_spics *spics = container_of(chip, struct spear_spics,
chip);
u32 tmp;
/* select chip select from register */
tmp = readl_relaxed(spics->base + spics->perip_cfg);
if (spics->last_off != offset) {
spics->last_off = offset;
tmp &= ~(spics->cs_enable_mask << spics->cs_enable_shift);
tmp |= offset << spics->cs_enable_shift;
}
/* toggle chip select line */
tmp &= ~(0x1 << spics->cs_value_bit);
tmp |= value << spics->cs_value_bit;
writel_relaxed(tmp, spics->base + spics->perip_cfg);
}
static int spics_direction_input(struct gpio_chip *chip, unsigned offset)
{
return -ENXIO;
}
static int spics_direction_output(struct gpio_chip *chip, unsigned offset,
int value)
{
spics_set_value(chip, offset, value);
return 0;
}
static int spics_request(struct gpio_chip *chip, unsigned offset)
{
struct spear_spics *spics = container_of(chip, struct spear_spics,
chip);
u32 tmp;
if (!spics->use_count++) {
tmp = readl_relaxed(spics->base + spics->perip_cfg);
tmp |= 0x1 << spics->sw_enable_bit;
tmp |= 0x1 << spics->cs_value_bit;
writel_relaxed(tmp, spics->base + spics->perip_cfg);
}
return 0;
}
static void spics_free(struct gpio_chip *chip, unsigned offset)
{
struct spear_spics *spics = container_of(chip, struct spear_spics,
chip);
u32 tmp;
if (!--spics->use_count) {
tmp = readl_relaxed(spics->base + spics->perip_cfg);
tmp &= ~(0x1 << spics->sw_enable_bit);
writel_relaxed(tmp, spics->base + spics->perip_cfg);
}
}
static int spics_gpio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct spear_spics *spics;
struct resource *res;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "invalid IORESOURCE_MEM\n");
return -EBUSY;
}
spics = devm_kzalloc(&pdev->dev, sizeof(*spics), GFP_KERNEL);
if (!spics) {
dev_err(&pdev->dev, "memory allocation fail\n");
return -ENOMEM;
}
spics->base = devm_request_and_ioremap(&pdev->dev, res);
if (!spics->base) {
dev_err(&pdev->dev, "request and ioremap fail\n");
return -ENOMEM;
}
if (of_property_read_u32(np, "st-spics,peripcfg-reg",
&spics->perip_cfg))
goto err_dt_data;
if (of_property_read_u32(np, "st-spics,sw-enable-bit",
&spics->sw_enable_bit))
goto err_dt_data;
if (of_property_read_u32(np, "st-spics,cs-value-bit",
&spics->cs_value_bit))
goto err_dt_data;
if (of_property_read_u32(np, "st-spics,cs-enable-mask",
&spics->cs_enable_mask))
goto err_dt_data;
if (of_property_read_u32(np, "st-spics,cs-enable-shift",
&spics->cs_enable_shift))
goto err_dt_data;
platform_set_drvdata(pdev, spics);
spics->chip.ngpio = NUM_OF_GPIO;
spics->chip.base = -1;
spics->chip.request = spics_request;
spics->chip.free = spics_free;
spics->chip.direction_input = spics_direction_input;
spics->chip.direction_output = spics_direction_output;
spics->chip.get = spics_get_value;
spics->chip.set = spics_set_value;
spics->chip.label = dev_name(&pdev->dev);
spics->chip.dev = &pdev->dev;
spics->chip.owner = THIS_MODULE;
spics->last_off = -1;
ret = gpiochip_add(&spics->chip);
if (ret) {
dev_err(&pdev->dev, "unable to add gpio chip\n");
return ret;
}
dev_info(&pdev->dev, "spear spics registered\n");
return 0;
err_dt_data:
dev_err(&pdev->dev, "DT probe failed\n");
return -EINVAL;
}
static const struct of_device_id spics_gpio_of_match[] = {
{ .compatible = "st,spear-spics-gpio" },
{}
};
MODULE_DEVICE_TABLE(of, spics_gpio_of_match);
static struct platform_driver spics_gpio_driver = {
.probe = spics_gpio_probe,
.driver = {
.owner = THIS_MODULE,
.name = "spear-spics-gpio",
.of_match_table = spics_gpio_of_match,
},
};
static int __init spics_gpio_init(void)
{
return platform_driver_register(&spics_gpio_driver);
}
subsys_initcall(spics_gpio_init);
MODULE_AUTHOR("Shiraz Hashim <shiraz.hashim@st.com>");
MODULE_DESCRIPTION("ST Microlectronics SPEAr SPI Chip Select Abstraction");
MODULE_LICENSE("GPL");

View File

@@ -11,7 +11,9 @@
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/mfd/stmpe.h>
/*
@@ -28,6 +30,7 @@ struct stmpe_gpio {
struct stmpe *stmpe;
struct device *dev;
struct mutex irq_lock;
struct irq_domain *domain;
int irq_base;
unsigned norequest_mask;
@@ -103,7 +106,7 @@ static int stmpe_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
return stmpe_gpio->irq_base + offset;
return irq_create_mapping(stmpe_gpio->domain, offset);
}
static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
@@ -132,7 +135,7 @@ static struct gpio_chip template_chip = {
static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
int offset = d->irq - stmpe_gpio->irq_base;
int offset = d->hwirq;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -199,7 +202,7 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d)
static void stmpe_gpio_irq_mask(struct irq_data *d)
{
struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
int offset = d->irq - stmpe_gpio->irq_base;
int offset = d->hwirq;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -209,7 +212,7 @@ static void stmpe_gpio_irq_mask(struct irq_data *d)
static void stmpe_gpio_irq_unmask(struct irq_data *d)
{
struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
int offset = d->irq - stmpe_gpio->irq_base;
int offset = d->hwirq;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -251,8 +254,9 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
while (stat) {
int bit = __ffs(stat);
int line = bank * 8 + bit;
int virq = irq_find_mapping(stmpe_gpio->domain, line);
handle_nested_irq(stmpe_gpio->irq_base + line);
handle_nested_irq(virq);
stat &= ~(1 << bit);
}
@@ -267,43 +271,61 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
return IRQ_HANDLED;
}
int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hwirq)
{
struct stmpe_gpio *stmpe_gpio = d->host_data;
if (!stmpe_gpio)
return -EINVAL;
irq_set_chip_data(hwirq, stmpe_gpio);
irq_set_chip_and_handler(hwirq, &stmpe_gpio_irq_chip,
handle_simple_irq);
irq_set_nested_thread(hwirq, 1);
#ifdef CONFIG_ARM
set_irq_flags(hwirq, IRQF_VALID);
#else
irq_set_noprobe(hwirq);
#endif
return 0;
}
void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int virq)
{
#ifdef CONFIG_ARM
set_irq_flags(virq, 0);
#endif
irq_set_chip_and_handler(virq, NULL, NULL);
irq_set_chip_data(virq, NULL);
}
static const struct irq_domain_ops stmpe_gpio_irq_simple_ops = {
.unmap = stmpe_gpio_irq_unmap,
.map = stmpe_gpio_irq_map,
.xlate = irq_domain_xlate_twocell,
};
static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio)
{
int base = stmpe_gpio->irq_base;
int irq;
for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
irq_set_chip_data(irq, stmpe_gpio);
irq_set_chip_and_handler(irq, &stmpe_gpio_irq_chip,
handle_simple_irq);
irq_set_nested_thread(irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(irq, IRQF_VALID);
#else
irq_set_noprobe(irq);
#endif
stmpe_gpio->domain = irq_domain_add_simple(NULL,
stmpe_gpio->chip.ngpio, base,
&stmpe_gpio_irq_simple_ops, stmpe_gpio);
if (!stmpe_gpio->domain) {
dev_err(stmpe_gpio->dev, "failed to create irqdomain\n");
return -ENOSYS;
}
return 0;
}
static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio)
{
int base = stmpe_gpio->irq_base;
int irq;
for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
#ifdef CONFIG_ARM
set_irq_flags(irq, 0);
#endif
irq_set_chip_and_handler(irq, NULL, NULL);
irq_set_chip_data(irq, NULL);
}
}
static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
{
struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
struct device_node *np = pdev->dev.of_node;
struct stmpe_gpio_platform_data *pdata;
struct stmpe_gpio *stmpe_gpio;
int ret;
@@ -321,13 +343,17 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
stmpe_gpio->dev = &pdev->dev;
stmpe_gpio->stmpe = stmpe;
stmpe_gpio->norequest_mask = pdata ? pdata->norequest_mask : 0;
stmpe_gpio->chip = template_chip;
stmpe_gpio->chip.ngpio = stmpe->num_gpios;
stmpe_gpio->chip.dev = &pdev->dev;
stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1;
if (pdata)
stmpe_gpio->norequest_mask = pdata->norequest_mask;
else if (np)
of_property_read_u32(np, "st,norequest-mask",
&stmpe_gpio->norequest_mask);
if (irq >= 0)
stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
else
@@ -348,7 +374,7 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
IRQF_ONESHOT, "stmpe-gpio", stmpe_gpio);
if (ret) {
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
goto out_removeirq;
goto out_disable;
}
}
@@ -368,9 +394,6 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
out_freeirq:
if (irq >= 0)
free_irq(irq, stmpe_gpio);
out_removeirq:
if (irq >= 0)
stmpe_gpio_irq_remove(stmpe_gpio);
out_disable:
stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
out_free:
@@ -398,10 +421,9 @@ static int __devexit stmpe_gpio_remove(struct platform_device *pdev)
stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
if (irq >= 0) {
if (irq >= 0)
free_irq(irq, stmpe_gpio);
stmpe_gpio_irq_remove(stmpe_gpio);
}
platform_set_drvdata(pdev, NULL);
kfree(stmpe_gpio);

View File

@@ -292,17 +292,15 @@ static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio,
{
int base = tc3589x_gpio->irq_base;
if (base) {
tc3589x_gpio->domain = irq_domain_add_legacy(
NULL, tc3589x_gpio->chip.ngpio, base,
0, &tc3589x_irq_ops, tc3589x_gpio);
}
else {
tc3589x_gpio->domain = irq_domain_add_linear(
np, tc3589x_gpio->chip.ngpio,
&tc3589x_irq_ops, tc3589x_gpio);
}
/*
* If this results in a linear domain, irq_create_mapping() will
* take care of allocating IRQ descriptors at runtime. When a base
* is provided, the IRQ descriptors will be allocated when the
* domain is instantiated.
*/
tc3589x_gpio->domain = irq_domain_add_simple(np,
tc3589x_gpio->chip.ngpio, base, &tc3589x_irq_ops,
tc3589x_gpio);
if (!tc3589x_gpio->domain) {
dev_err(tc3589x_gpio->dev, "Failed to create irqdomain\n");
return -ENOSYS;

View File

@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/irqdomain.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm.h>
#include <asm/mach/irq.h>
@@ -64,7 +65,7 @@ struct tegra_gpio_bank {
int bank;
int irq;
spinlock_t lvl_lock[4];
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
u32 cnf[4];
u32 out[4];
u32 oe[4];
@@ -109,20 +110,18 @@ static void tegra_gpio_enable(int gpio)
{
tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 1);
}
EXPORT_SYMBOL_GPL(tegra_gpio_enable);
static void tegra_gpio_disable(int gpio)
{
tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 0);
}
EXPORT_SYMBOL_GPL(tegra_gpio_disable);
int tegra_gpio_request(struct gpio_chip *chip, unsigned offset)
static int tegra_gpio_request(struct gpio_chip *chip, unsigned offset)
{
return pinctrl_request_gpio(offset);
}
void tegra_gpio_free(struct gpio_chip *chip, unsigned offset)
static void tegra_gpio_free(struct gpio_chip *chip, unsigned offset)
{
pinctrl_free_gpio(offset);
tegra_gpio_disable(offset);
@@ -135,6 +134,11 @@ static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset)
{
/* If gpio is in output mode then read from the out value */
if ((tegra_gpio_readl(GPIO_OE(offset)) >> GPIO_BIT(offset)) & 1)
return (tegra_gpio_readl(GPIO_OUT(offset)) >>
GPIO_BIT(offset)) & 0x1;
return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1;
}
@@ -285,8 +289,8 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
}
#ifdef CONFIG_PM
void tegra_gpio_resume(void)
#ifdef CONFIG_PM_SLEEP
static int tegra_gpio_resume(struct device *dev)
{
unsigned long flags;
int b;
@@ -308,9 +312,10 @@ void tegra_gpio_resume(void)
}
local_irq_restore(flags);
return 0;
}
void tegra_gpio_suspend(void)
static int tegra_gpio_suspend(struct device *dev)
{
unsigned long flags;
int b;
@@ -330,6 +335,7 @@ void tegra_gpio_suspend(void)
}
}
local_irq_restore(flags);
return 0;
}
static int tegra_gpio_wake_enable(struct irq_data *d, unsigned int enable)
@@ -345,11 +351,15 @@ static struct irq_chip tegra_gpio_irq_chip = {
.irq_mask = tegra_gpio_irq_mask,
.irq_unmask = tegra_gpio_irq_unmask,
.irq_set_type = tegra_gpio_irq_set_type,
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
.irq_set_wake = tegra_gpio_wake_enable,
#endif
};
static const struct dev_pm_ops tegra_gpio_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume)
};
struct tegra_gpio_soc_config {
u32 bank_stride;
u32 upper_offset;
@@ -380,7 +390,6 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct tegra_gpio_soc_config *config;
int irq_base;
struct resource *res;
struct tegra_gpio_bank *bank;
int gpio;
@@ -417,14 +426,11 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
return -ENODEV;
}
irq_base = irq_alloc_descs(-1, 0, tegra_gpio_chip.ngpio, 0);
if (irq_base < 0) {
dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n");
return -ENODEV;
}
irq_domain = irq_domain_add_legacy(pdev->dev.of_node,
tegra_gpio_chip.ngpio, irq_base, 0,
irq_domain = irq_domain_add_linear(pdev->dev.of_node,
tegra_gpio_chip.ngpio,
&irq_domain_simple_ops, NULL);
if (!irq_domain)
return -ENODEV;
for (i = 0; i < tegra_gpio_bank_count; i++) {
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
@@ -464,7 +470,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
gpiochip_add(&tegra_gpio_chip);
for (gpio = 0; gpio < tegra_gpio_chip.ngpio; gpio++) {
int irq = irq_find_mapping(irq_domain, gpio);
int irq = irq_create_mapping(irq_domain, gpio);
/* No validity check; all Tegra GPIOs are valid IRQs */
bank = &tegra_gpio_banks[GPIO_BANK(gpio)];
@@ -493,6 +499,7 @@ static struct platform_driver tegra_gpio_driver = {
.driver = {
.name = "tegra-gpio",
.owner = THIS_MODULE,
.pm = &tegra_gpio_pm_ops,
.of_match_table = tegra_gpio_of_match,
},
.probe = tegra_gpio_probe,

466
drivers/gpio/gpio-ts5500.c Normal file
View File

@@ -0,0 +1,466 @@
/*
* Digital I/O driver for Technologic Systems TS-5500
*
* Copyright (c) 2012 Savoir-faire Linux Inc.
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*
* Technologic Systems platforms have pin blocks, exposing several Digital
* Input/Output lines (DIO). This driver aims to support single pin blocks.
* In that sense, the support is not limited to the TS-5500 blocks.
* Actually, the following platforms have DIO support:
*
* TS-5500:
* Documentation: http://wiki.embeddedarm.com/wiki/TS-5500
* Blocks: DIO1, DIO2 and LCD port.
*
* TS-5600:
* Documentation: http://wiki.embeddedarm.com/wiki/TS-5600
* Blocks: LCD port (identical to TS-5500 LCD).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/bitops.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_data/gpio-ts5500.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/* List of supported Technologic Systems platforms DIO blocks */
enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD };
struct ts5500_priv {
const struct ts5500_dio *pinout;
struct gpio_chip gpio_chip;
spinlock_t lock;
bool strap;
u8 hwirq;
};
/*
* Hex 7D is used to control several blocks (e.g. DIO2 and LCD port).
* This flag ensures that the region has been requested by this driver.
*/
static bool hex7d_reserved;
/*
* This structure is used to describe capabilities of DIO lines,
* such as available directions and connected interrupt (if any).
*/
struct ts5500_dio {
const u8 value_addr;
const u8 value_mask;
const u8 control_addr;
const u8 control_mask;
const bool no_input;
const bool no_output;
const u8 irq;
};
#define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit) \
{ \
.value_addr = vaddr, \
.value_mask = BIT(vbit), \
.control_addr = caddr, \
.control_mask = BIT(cbit), \
}
#define TS5500_DIO_IN(addr, bit) \
{ \
.value_addr = addr, \
.value_mask = BIT(bit), \
.no_output = true, \
}
#define TS5500_DIO_IN_IRQ(addr, bit, _irq) \
{ \
.value_addr = addr, \
.value_mask = BIT(bit), \
.no_output = true, \
.irq = _irq, \
}
#define TS5500_DIO_OUT(addr, bit) \
{ \
.value_addr = addr, \
.value_mask = BIT(bit), \
.no_input = true, \
}
/*
* Input/Output DIO lines are programmed in groups of 4. Their values are
* available through 4 consecutive bits in a value port, whereas the direction
* of these 4 lines is driven by only 1 bit in a control port.
*/
#define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit) \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit), \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit), \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit), \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit)
/*
* TS-5500 DIO1 block
*
* value control dir hw
* addr bit addr bit in out irq name pin offset
*
* 0x7b 0 0x7a 0 x x DIO1_0 1 0
* 0x7b 1 0x7a 0 x x DIO1_1 3 1
* 0x7b 2 0x7a 0 x x DIO1_2 5 2
* 0x7b 3 0x7a 0 x x DIO1_3 7 3
* 0x7b 4 0x7a 1 x x DIO1_4 9 4
* 0x7b 5 0x7a 1 x x DIO1_5 11 5
* 0x7b 6 0x7a 1 x x DIO1_6 13 6
* 0x7b 7 0x7a 1 x x DIO1_7 15 7
* 0x7c 0 0x7a 5 x x DIO1_8 4 8
* 0x7c 1 0x7a 5 x x DIO1_9 6 9
* 0x7c 2 0x7a 5 x x DIO1_10 8 10
* 0x7c 3 0x7a 5 x x DIO1_11 10 11
* 0x7c 4 x DIO1_12 12 12
* 0x7c 5 x 7 DIO1_13 14 13
*/
static const struct ts5500_dio ts5500_dio1[] = {
TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0),
TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1),
TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5),
TS5500_DIO_IN(0x7c, 4),
TS5500_DIO_IN_IRQ(0x7c, 5, 7),
};
/*
* TS-5500 DIO2 block
*
* value control dir hw
* addr bit addr bit in out irq name pin offset
*
* 0x7e 0 0x7d 0 x x DIO2_0 1 0
* 0x7e 1 0x7d 0 x x DIO2_1 3 1
* 0x7e 2 0x7d 0 x x DIO2_2 5 2
* 0x7e 3 0x7d 0 x x DIO2_3 7 3
* 0x7e 4 0x7d 1 x x DIO2_4 9 4
* 0x7e 5 0x7d 1 x x DIO2_5 11 5
* 0x7e 6 0x7d 1 x x DIO2_6 13 6
* 0x7e 7 0x7d 1 x x DIO2_7 15 7
* 0x7f 0 0x7d 5 x x DIO2_8 4 8
* 0x7f 1 0x7d 5 x x DIO2_9 6 9
* 0x7f 2 0x7d 5 x x DIO2_10 8 10
* 0x7f 3 0x7d 5 x x DIO2_11 10 11
* 0x7f 4 x 6 DIO2_13 14 12
*/
static const struct ts5500_dio ts5500_dio2[] = {
TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0),
TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1),
TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5),
TS5500_DIO_IN_IRQ(0x7f, 4, 6),
};
/*
* TS-5500 LCD port used as DIO block
* TS-5600 LCD port is identical
*
* value control dir hw
* addr bit addr bit in out irq name pin offset
*
* 0x72 0 0x7d 2 x x LCD_0 8 0
* 0x72 1 0x7d 2 x x LCD_1 7 1
* 0x72 2 0x7d 2 x x LCD_2 10 2
* 0x72 3 0x7d 2 x x LCD_3 9 3
* 0x72 4 0x7d 3 x x LCD_4 12 4
* 0x72 5 0x7d 3 x x LCD_5 11 5
* 0x72 6 0x7d 3 x x LCD_6 14 6
* 0x72 7 0x7d 3 x x LCD_7 13 7
* 0x73 0 x LCD_EN 5 8
* 0x73 6 x LCD_WR 6 9
* 0x73 7 x 1 LCD_RS 3 10
*/
static const struct ts5500_dio ts5500_lcd[] = {
TS5500_DIO_GROUP(0x72, 0, 0x7d, 2),
TS5500_DIO_GROUP(0x72, 4, 0x7d, 3),
TS5500_DIO_OUT(0x73, 0),
TS5500_DIO_IN(0x73, 6),
TS5500_DIO_IN_IRQ(0x73, 7, 1),
};
static inline struct ts5500_priv *ts5500_gc_to_priv(struct gpio_chip *chip)
{
return container_of(chip, struct ts5500_priv, gpio_chip);
}
static inline void ts5500_set_mask(u8 mask, u8 addr)
{
u8 val = inb(addr);
val |= mask;
outb(val, addr);
}
static inline void ts5500_clear_mask(u8 mask, u8 addr)
{
u8 val = inb(addr);
val &= ~mask;
outb(val, addr);
}
static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset)
{
struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
const struct ts5500_dio line = priv->pinout[offset];
unsigned long flags;
if (line.no_input)
return -ENXIO;
if (line.no_output)
return 0;
spin_lock_irqsave(&priv->lock, flags);
ts5500_clear_mask(line.control_mask, line.control_addr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
const struct ts5500_dio line = priv->pinout[offset];
return !!(inb(line.value_addr) & line.value_mask);
}
static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val)
{
struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
const struct ts5500_dio line = priv->pinout[offset];
unsigned long flags;
if (line.no_output)
return -ENXIO;
spin_lock_irqsave(&priv->lock, flags);
if (!line.no_input)
ts5500_set_mask(line.control_mask, line.control_addr);
if (val)
ts5500_set_mask(line.value_mask, line.value_addr);
else
ts5500_clear_mask(line.value_mask, line.value_addr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
{
struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
const struct ts5500_dio line = priv->pinout[offset];
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (val)
ts5500_set_mask(line.value_mask, line.value_addr);
else
ts5500_clear_mask(line.value_mask, line.value_addr);
spin_unlock_irqrestore(&priv->lock, flags);
}
static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
const struct ts5500_dio *block = priv->pinout;
const struct ts5500_dio line = block[offset];
/* Only one pin is connected to an interrupt */
if (line.irq)
return line.irq;
/* As this pin is input-only, we may strap it to another in/out pin */
if (priv->strap)
return priv->hwirq;
return -ENXIO;
}
static int ts5500_enable_irq(struct ts5500_priv *priv)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (priv->hwirq == 7)
ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
else if (priv->hwirq == 6)
ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
else if (priv->hwirq == 1)
ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
else
ret = -EINVAL;
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
static void ts5500_disable_irq(struct ts5500_priv *priv)
{
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (priv->hwirq == 7)
ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
else if (priv->hwirq == 6)
ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
else if (priv->hwirq == 1)
ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
else
dev_err(priv->gpio_chip.dev, "invalid hwirq %d\n", priv->hwirq);
spin_unlock_irqrestore(&priv->lock, flags);
}
static int __devinit ts5500_dio_probe(struct platform_device *pdev)
{
enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
struct ts5500_dio_platform_data *pdata = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
const char *name = dev_name(dev);
struct ts5500_priv *priv;
struct resource *res;
unsigned long flags;
int ret;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(dev, "missing IRQ resource\n");
return -EINVAL;
}
priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->hwirq = res->start;
spin_lock_init(&priv->lock);
priv->gpio_chip.owner = THIS_MODULE;
priv->gpio_chip.label = name;
priv->gpio_chip.dev = dev;
priv->gpio_chip.direction_input = ts5500_gpio_input;
priv->gpio_chip.direction_output = ts5500_gpio_output;
priv->gpio_chip.get = ts5500_gpio_get;
priv->gpio_chip.set = ts5500_gpio_set;
priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
priv->gpio_chip.base = -1;
if (pdata) {
priv->gpio_chip.base = pdata->base;
priv->strap = pdata->strap;
}
switch (block) {
case TS5500_DIO1:
priv->pinout = ts5500_dio1;
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1);
if (!devm_request_region(dev, 0x7a, 3, name)) {
dev_err(dev, "failed to request %s ports\n", name);
return -EBUSY;
}
break;
case TS5500_DIO2:
priv->pinout = ts5500_dio2;
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2);
if (!devm_request_region(dev, 0x7e, 2, name)) {
dev_err(dev, "failed to request %s ports\n", name);
return -EBUSY;
}
if (hex7d_reserved)
break;
if (!devm_request_region(dev, 0x7d, 1, name)) {
dev_err(dev, "failed to request %s 7D\n", name);
return -EBUSY;
}
hex7d_reserved = true;
break;
case TS5500_LCD:
case TS5600_LCD:
priv->pinout = ts5500_lcd;
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd);
if (!devm_request_region(dev, 0x72, 2, name)) {
dev_err(dev, "failed to request %s ports\n", name);
return -EBUSY;
}
if (!hex7d_reserved) {
if (!devm_request_region(dev, 0x7d, 1, name)) {
dev_err(dev, "failed to request %s 7D\n", name);
return -EBUSY;
}
hex7d_reserved = true;
}
/* Ensure usage of LCD port as DIO */
spin_lock_irqsave(&priv->lock, flags);
ts5500_clear_mask(BIT(4), 0x7d);
spin_unlock_irqrestore(&priv->lock, flags);
break;
}
ret = gpiochip_add(&priv->gpio_chip);
if (ret) {
dev_err(dev, "failed to register the gpio chip\n");
return ret;
}
ret = ts5500_enable_irq(priv);
if (ret) {
dev_err(dev, "invalid interrupt %d\n", priv->hwirq);
goto cleanup;
}
return 0;
cleanup:
if (gpiochip_remove(&priv->gpio_chip))
dev_err(dev, "failed to remove gpio chip\n");
return ret;
}
static int __devexit ts5500_dio_remove(struct platform_device *pdev)
{
struct ts5500_priv *priv = platform_get_drvdata(pdev);
ts5500_disable_irq(priv);
return gpiochip_remove(&priv->gpio_chip);
}
static struct platform_device_id ts5500_dio_ids[] = {
{ "ts5500-dio1", TS5500_DIO1 },
{ "ts5500-dio2", TS5500_DIO2 },
{ "ts5500-dio-lcd", TS5500_LCD },
{ "ts5600-dio-lcd", TS5600_LCD },
{ }
};
MODULE_DEVICE_TABLE(platform, ts5500_dio_ids);
static struct platform_driver ts5500_dio_driver = {
.driver = {
.name = "ts5500-dio",
.owner = THIS_MODULE,
},
.probe = ts5500_dio_probe,
.remove = __devexit_p(ts5500_dio_remove),
.id_table = ts5500_dio_ids,
};
module_platform_driver(ts5500_dio_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver");

View File

@@ -88,11 +88,15 @@ static inline int gpio_twl4030_write(u8 address, u8 data)
/*----------------------------------------------------------------------*/
/*
* LED register offsets (use TWL4030_MODULE_{LED,PWMA,PWMB}))
* LED register offsets from TWL_MODULE_LED base
* PWMs A and B are dedicated to LEDs A and B, respectively.
*/
#define TWL4030_LED_LEDEN 0x0
#define TWL4030_LED_LEDEN_REG 0x00
#define TWL4030_PWMAON_REG 0x01
#define TWL4030_PWMAOFF_REG 0x02
#define TWL4030_PWMBON_REG 0x03
#define TWL4030_PWMBOFF_REG 0x04
/* LEDEN bits */
#define LEDEN_LEDAON BIT(0)
@@ -104,9 +108,6 @@ static inline int gpio_twl4030_write(u8 address, u8 data)
#define LEDEN_PWM_LENGTHA BIT(6)
#define LEDEN_PWM_LENGTHB BIT(7)
#define TWL4030_PWMx_PWMxON 0x0
#define TWL4030_PWMx_PWMxOFF 0x1
#define PWMxON_LENGTH BIT(7)
/*----------------------------------------------------------------------*/
@@ -145,7 +146,7 @@ static void twl4030_led_set_value(int led, int value)
else
cached_leden |= mask;
status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
TWL4030_LED_LEDEN);
TWL4030_LED_LEDEN_REG);
mutex_unlock(&gpio_lock);
}
@@ -216,33 +217,33 @@ static int twl_request(struct gpio_chip *chip, unsigned offset)
if (offset >= TWL4030_GPIO_MAX) {
u8 ledclr_mask = LEDEN_LEDAON | LEDEN_LEDAEXT
| LEDEN_LEDAPWM | LEDEN_PWM_LENGTHA;
u8 module = TWL4030_MODULE_PWMA;
u8 reg = TWL4030_PWMAON_REG;
offset -= TWL4030_GPIO_MAX;
if (offset) {
ledclr_mask <<= 1;
module = TWL4030_MODULE_PWMB;
reg = TWL4030_PWMBON_REG;
}
/* initialize PWM to always-drive */
status = twl_i2c_write_u8(module, 0x7f,
TWL4030_PWMx_PWMxOFF);
/* Configure PWM OFF register first */
status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg + 1);
if (status < 0)
goto done;
status = twl_i2c_write_u8(module, 0x7f,
TWL4030_PWMx_PWMxON);
/* Followed by PWM ON register */
status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg);
if (status < 0)
goto done;
/* init LED to not-driven (high) */
module = TWL4030_MODULE_LED;
status = twl_i2c_read_u8(module, &cached_leden,
TWL4030_LED_LEDEN);
status = twl_i2c_read_u8(TWL4030_MODULE_LED, &cached_leden,
TWL4030_LED_LEDEN_REG);
if (status < 0)
goto done;
cached_leden &= ~ledclr_mask;
status = twl_i2c_write_u8(module, cached_leden,
TWL4030_LED_LEDEN);
status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
TWL4030_LED_LEDEN_REG);
if (status < 0)
goto done;

View File

@@ -96,6 +96,7 @@ static struct vt8500_gpio_data wm8505_data = {
VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12),
VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16),
VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22),
VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6),
},
};
@@ -115,6 +116,7 @@ static struct vt8500_gpio_data wm8650_data = {
VT8500_BANK(0x58, 0x98, 0xD8, 0x18, 32),
VT8500_BANK(0x5C, 0x9C, 0xDC, 0x1C, 32),
VT8500_BANK(0x7C, 0xBC, 0xFC, 0x3C, 32),
VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6),
},
};

View File

@@ -191,6 +191,32 @@ err:
return ret;
}
/* caller ensures gpio is valid and requested, chip->get_direction may sleep */
static int gpio_get_direction(unsigned gpio)
{
struct gpio_chip *chip;
struct gpio_desc *desc = &gpio_desc[gpio];
int status = -EINVAL;
chip = gpio_to_chip(gpio);
gpio -= chip->base;
if (!chip->get_direction)
return status;
status = chip->get_direction(chip, gpio);
if (status > 0) {
/* GPIOF_DIR_IN, or other positive */
status = 1;
clear_bit(FLAG_IS_OUT, &desc->flags);
}
if (status == 0) {
/* GPIOF_DIR_OUT */
set_bit(FLAG_IS_OUT, &desc->flags);
}
return status;
}
#ifdef CONFIG_GPIO_SYSFS
/* lock protects against unexport_gpio() being called while
@@ -223,6 +249,7 @@ static ssize_t gpio_direction_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const struct gpio_desc *desc = dev_get_drvdata(dev);
unsigned gpio = desc - gpio_desc;
ssize_t status;
mutex_lock(&sysfs_lock);
@@ -230,6 +257,7 @@ static ssize_t gpio_direction_show(struct device *dev,
if (!test_bit(FLAG_EXPORT, &desc->flags))
status = -EIO;
else
gpio_get_direction(gpio);
status = sprintf(buf, "%s\n",
test_bit(FLAG_IS_OUT, &desc->flags)
? "out" : "in");
@@ -704,8 +732,9 @@ int gpio_export(unsigned gpio, bool direction_may_change)
{
unsigned long flags;
struct gpio_desc *desc;
int status = -EINVAL;
int status;
const char *ioname = NULL;
struct device *dev;
/* can't export until sysfs is available ... */
if (!gpio_class.p) {
@@ -713,59 +742,66 @@ int gpio_export(unsigned gpio, bool direction_may_change)
return -ENOENT;
}
if (!gpio_is_valid(gpio))
goto done;
if (!gpio_is_valid(gpio)) {
pr_debug("%s: gpio %d is not valid\n", __func__, gpio);
return -EINVAL;
}
mutex_lock(&sysfs_lock);
spin_lock_irqsave(&gpio_lock, flags);
desc = &gpio_desc[gpio];
if (test_bit(FLAG_REQUESTED, &desc->flags)
&& !test_bit(FLAG_EXPORT, &desc->flags)) {
status = 0;
if (!desc->chip->direction_input
|| !desc->chip->direction_output)
direction_may_change = false;
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
test_bit(FLAG_EXPORT, &desc->flags)) {
spin_unlock_irqrestore(&gpio_lock, flags);
pr_debug("%s: gpio %d unavailable (requested=%d, exported=%d)\n",
__func__, gpio,
test_bit(FLAG_REQUESTED, &desc->flags),
test_bit(FLAG_EXPORT, &desc->flags));
status = -EPERM;
goto fail_unlock;
}
if (!desc->chip->direction_input || !desc->chip->direction_output)
direction_may_change = false;
spin_unlock_irqrestore(&gpio_lock, flags);
if (desc->chip->names && desc->chip->names[gpio - desc->chip->base])
ioname = desc->chip->names[gpio - desc->chip->base];
if (status == 0) {
struct device *dev;
dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
desc, ioname ? ioname : "gpio%u", gpio);
if (!IS_ERR(dev)) {
status = sysfs_create_group(&dev->kobj,
&gpio_attr_group);
if (!status && direction_may_change)
status = device_create_file(dev,
&dev_attr_direction);
if (!status && gpio_to_irq(gpio) >= 0
&& (direction_may_change
|| !test_bit(FLAG_IS_OUT,
&desc->flags)))
status = device_create_file(dev,
&dev_attr_edge);
if (status != 0)
device_unregister(dev);
} else
status = PTR_ERR(dev);
if (status == 0)
set_bit(FLAG_EXPORT, &desc->flags);
dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
desc, ioname ? ioname : "gpio%u", gpio);
if (IS_ERR(dev)) {
status = PTR_ERR(dev);
goto fail_unlock;
}
mutex_unlock(&sysfs_lock);
done:
status = sysfs_create_group(&dev->kobj, &gpio_attr_group);
if (status)
pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
goto fail_unregister_device;
if (direction_may_change) {
status = device_create_file(dev, &dev_attr_direction);
if (status)
goto fail_unregister_device;
}
if (gpio_to_irq(gpio) >= 0 && (direction_may_change ||
!test_bit(FLAG_IS_OUT, &desc->flags))) {
status = device_create_file(dev, &dev_attr_edge);
if (status)
goto fail_unregister_device;
}
set_bit(FLAG_EXPORT, &desc->flags);
mutex_unlock(&sysfs_lock);
return 0;
fail_unregister_device:
device_unregister(dev);
fail_unlock:
mutex_unlock(&sysfs_lock);
pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
return status;
}
EXPORT_SYMBOL_GPL(gpio_export);
@@ -1075,6 +1111,7 @@ int gpiochip_add(struct gpio_chip *chip)
* inputs (often with pullups enabled) so power
* usage is minimized. Linux code should set the
* gpio direction first thing; but until it does,
* and in case chip->get_direction is not set,
* we may expose the wrong direction in sysfs.
*/
gpio_desc[id].flags = !chip->direction_input
@@ -1304,9 +1341,15 @@ int gpio_request(unsigned gpio, const char *label)
desc_set_label(desc, NULL);
module_put(chip->owner);
clear_bit(FLAG_REQUESTED, &desc->flags);
goto done;
}
}
if (chip->get_direction) {
/* chip->get_direction may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
gpio_get_direction(gpio);
spin_lock_irqsave(&gpio_lock, flags);
}
done:
if (status)
pr_debug("gpio_request: gpio-%d (%s) status %d\n",
@@ -1842,6 +1885,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
continue;
gpio_get_direction(gpio);
is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
seq_printf(s, " gpio-%-3d (%-20.20s) %s %s",
gpio, gdesc->label,