Merge branch 'gpio/next' of git://git.secretlab.ca/git/linux-2.6
* 'gpio/next' of git://git.secretlab.ca/git/linux-2.6: gpio/via: rename VIA local config struct basic_mmio_gpio: split into a gpio library and platform device gpio: remove some legacy comments in build files gpio: add trace events for setting direction and value gpio/pca953x: Use handle_simple_irq instead of handle_edge_irq gpiolib: export gpiochip_find gpio: remove redundant Kconfig depends on GPIOLIB basic_mmio_gpio: convert to non-__raw* accessors basic_mmio_gpio: support direction registers basic_mmio_gpio: support different input/output registers basic_mmio_gpio: detect output method at probe time basic_mmio_gpio: request register regions basic_mmio_gpio: allow overriding number of gpio basic_mmio_gpio: convert to platform_{get,set}_drvdata() basic_mmio_gpio: remove runtime width/endianness evaluation
This commit is contained in:
commit
8b29336fe0
8 changed files with 559 additions and 178 deletions
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# platform-neutral GPIO infrastructure and expanders
|
||||
# GPIO infrastructure and drivers
|
||||
#
|
||||
|
||||
config ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
|
@ -31,7 +31,7 @@ menuconfig GPIOLIB
|
|||
help
|
||||
This enables GPIO support through the generic GPIO library.
|
||||
You only need to enable this, if you also want to enable
|
||||
one or more of the GPIO expansion card drivers below.
|
||||
one or more of the GPIO drivers below.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
|
@ -63,21 +63,26 @@ config GPIO_SYSFS
|
|||
Kernel drivers may also request that a particular GPIO be
|
||||
exported to userspace; this can be useful when debugging.
|
||||
|
||||
# put expanders in the right section, in alphabetical order
|
||||
# put drivers in the right section, in alphabetical order
|
||||
|
||||
config GPIO_MAX730X
|
||||
tristate
|
||||
|
||||
comment "Memory mapped GPIO expanders:"
|
||||
comment "Memory mapped GPIO drivers:"
|
||||
|
||||
config GPIO_BASIC_MMIO_CORE
|
||||
tristate
|
||||
help
|
||||
Provides core functionality for basic memory-mapped GPIO controllers.
|
||||
|
||||
config GPIO_BASIC_MMIO
|
||||
tristate "Basic memory-mapped GPIO controllers support"
|
||||
select GPIO_BASIC_MMIO_CORE
|
||||
help
|
||||
Say yes here to support basic memory-mapped GPIO controllers.
|
||||
|
||||
config GPIO_IT8761E
|
||||
tristate "IT8761E GPIO support"
|
||||
depends on GPIOLIB
|
||||
help
|
||||
Say yes here to support GPIO functionality of IT8761E super I/O chip.
|
||||
|
||||
|
@ -101,7 +106,7 @@ config GPIO_VR41XX
|
|||
|
||||
config GPIO_SCH
|
||||
tristate "Intel SCH/TunnelCreek GPIO"
|
||||
depends on GPIOLIB && PCI && X86
|
||||
depends on PCI && X86
|
||||
select MFD_CORE
|
||||
select LPC_SCH
|
||||
help
|
||||
|
@ -121,7 +126,7 @@ config GPIO_SCH
|
|||
|
||||
config GPIO_VX855
|
||||
tristate "VIA VX855/VX875 GPIO"
|
||||
depends on GPIOLIB && MFD_SUPPORT && PCI
|
||||
depends on MFD_SUPPORT && PCI
|
||||
select MFD_CORE
|
||||
select MFD_VX855
|
||||
help
|
||||
|
@ -347,13 +352,13 @@ config GPIO_ML_IOH
|
|||
|
||||
config GPIO_TIMBERDALE
|
||||
bool "Support for timberdale GPIO IP"
|
||||
depends on MFD_TIMBERDALE && GPIOLIB && HAS_IOMEM
|
||||
depends on MFD_TIMBERDALE && HAS_IOMEM
|
||||
---help---
|
||||
Add support for the GPIO IP in the timberdale FPGA.
|
||||
|
||||
config GPIO_RDC321X
|
||||
tristate "RDC R-321x GPIO support"
|
||||
depends on PCI && GPIOLIB
|
||||
depends on PCI
|
||||
select MFD_SUPPORT
|
||||
select MFD_CORE
|
||||
select MFD_RDC321X
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
# generic gpio support: dedicated expander chips, etc
|
||||
#
|
||||
# NOTE: platform-specific GPIO drivers don't belong in the
|
||||
# drivers/gpio directory; put them with other platform setup
|
||||
# code, IRQ controllers, board init, etc.
|
||||
# generic gpio support: platform drivers, dedicated expander chips, etc
|
||||
|
||||
ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
|
||||
|
||||
|
@ -10,6 +6,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o
|
|||
|
||||
obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o
|
||||
obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o
|
||||
obj-$(CONFIG_GPIO_BASIC_MMIO_CORE) += basic_mmio_gpio.o
|
||||
obj-$(CONFIG_GPIO_BASIC_MMIO) += basic_mmio_gpio.o
|
||||
obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o
|
||||
obj-$(CONFIG_GPIO_MAX730X) += max730x.o
|
||||
|
|
|
@ -45,6 +45,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
|
|||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -61,103 +62,72 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
|
|||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/basic_mmio_gpio.h>
|
||||
|
||||
struct bgpio_chip {
|
||||
struct gpio_chip gc;
|
||||
void __iomem *reg_dat;
|
||||
void __iomem *reg_set;
|
||||
void __iomem *reg_clr;
|
||||
|
||||
/* Number of bits (GPIOs): <register width> * 8. */
|
||||
int bits;
|
||||
|
||||
/*
|
||||
* Some GPIO controllers work with the big-endian bits notation,
|
||||
* e.g. in a 8-bits register, GPIO7 is the least significant bit.
|
||||
*/
|
||||
int big_endian_bits;
|
||||
|
||||
/*
|
||||
* Used to lock bgpio_chip->data. Also, this is needed to keep
|
||||
* shadowed and real data registers writes together.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
|
||||
/* Shadowed data register to clear/set bits safely. */
|
||||
unsigned long data;
|
||||
};
|
||||
|
||||
static struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc)
|
||||
static void bgpio_write8(void __iomem *reg, unsigned long data)
|
||||
{
|
||||
return container_of(gc, struct bgpio_chip, gc);
|
||||
writeb(data, reg);
|
||||
}
|
||||
|
||||
static unsigned long bgpio_in(struct bgpio_chip *bgc)
|
||||
static unsigned long bgpio_read8(void __iomem *reg)
|
||||
{
|
||||
switch (bgc->bits) {
|
||||
case 8:
|
||||
return __raw_readb(bgc->reg_dat);
|
||||
case 16:
|
||||
return __raw_readw(bgc->reg_dat);
|
||||
case 32:
|
||||
return __raw_readl(bgc->reg_dat);
|
||||
return readb(reg);
|
||||
}
|
||||
|
||||
static void bgpio_write16(void __iomem *reg, unsigned long data)
|
||||
{
|
||||
writew(data, reg);
|
||||
}
|
||||
|
||||
static unsigned long bgpio_read16(void __iomem *reg)
|
||||
{
|
||||
return readw(reg);
|
||||
}
|
||||
|
||||
static void bgpio_write32(void __iomem *reg, unsigned long data)
|
||||
{
|
||||
writel(data, reg);
|
||||
}
|
||||
|
||||
static unsigned long bgpio_read32(void __iomem *reg)
|
||||
{
|
||||
return readl(reg);
|
||||
}
|
||||
|
||||
#if BITS_PER_LONG >= 64
|
||||
case 64:
|
||||
return __raw_readq(bgc->reg_dat);
|
||||
#endif
|
||||
}
|
||||
return -EINVAL;
|
||||
static void bgpio_write64(void __iomem *reg, unsigned long data)
|
||||
{
|
||||
writeq(data, reg);
|
||||
}
|
||||
|
||||
static void bgpio_out(struct bgpio_chip *bgc, void __iomem *reg,
|
||||
unsigned long data)
|
||||
static unsigned long bgpio_read64(void __iomem *reg)
|
||||
{
|
||||
switch (bgc->bits) {
|
||||
case 8:
|
||||
__raw_writeb(data, reg);
|
||||
return;
|
||||
case 16:
|
||||
__raw_writew(data, reg);
|
||||
return;
|
||||
case 32:
|
||||
__raw_writel(data, reg);
|
||||
return;
|
||||
#if BITS_PER_LONG >= 64
|
||||
case 64:
|
||||
__raw_writeq(data, reg);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
return readq(reg);
|
||||
}
|
||||
#endif /* BITS_PER_LONG >= 64 */
|
||||
|
||||
static unsigned long bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin)
|
||||
{
|
||||
if (bgc->big_endian_bits)
|
||||
return 1 << (bgc->bits - 1 - pin);
|
||||
else
|
||||
return 1 << pin;
|
||||
return 1 << pin;
|
||||
}
|
||||
|
||||
static unsigned long bgpio_pin2mask_be(struct bgpio_chip *bgc,
|
||||
unsigned int pin)
|
||||
{
|
||||
return 1 << (bgc->bits - 1 - pin);
|
||||
}
|
||||
|
||||
static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
struct bgpio_chip *bgc = to_bgpio_chip(gc);
|
||||
|
||||
return bgpio_in(bgc) & bgpio_pin2mask(bgc, gpio);
|
||||
return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio);
|
||||
}
|
||||
|
||||
static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
|
||||
{
|
||||
struct bgpio_chip *bgc = to_bgpio_chip(gc);
|
||||
unsigned long mask = bgpio_pin2mask(bgc, gpio);
|
||||
unsigned long mask = bgc->pin2mask(bgc, gpio);
|
||||
unsigned long flags;
|
||||
|
||||
if (bgc->reg_set) {
|
||||
if (val)
|
||||
bgpio_out(bgc, bgc->reg_set, mask);
|
||||
else
|
||||
bgpio_out(bgc, bgc->reg_clr, mask);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&bgc->lock, flags);
|
||||
|
||||
if (val)
|
||||
|
@ -165,103 +135,382 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
|
|||
else
|
||||
bgc->data &= ~mask;
|
||||
|
||||
bgpio_out(bgc, bgc->reg_dat, bgc->data);
|
||||
bgc->write_reg(bgc->reg_dat, bgc->data);
|
||||
|
||||
spin_unlock_irqrestore(&bgc->lock, flags);
|
||||
}
|
||||
|
||||
static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
|
||||
int val)
|
||||
{
|
||||
struct bgpio_chip *bgc = to_bgpio_chip(gc);
|
||||
unsigned long mask = bgc->pin2mask(bgc, gpio);
|
||||
|
||||
if (val)
|
||||
bgc->write_reg(bgc->reg_set, mask);
|
||||
else
|
||||
bgc->write_reg(bgc->reg_clr, mask);
|
||||
}
|
||||
|
||||
static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
|
||||
{
|
||||
struct bgpio_chip *bgc = to_bgpio_chip(gc);
|
||||
unsigned long mask = bgc->pin2mask(bgc, gpio);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&bgc->lock, flags);
|
||||
|
||||
if (val)
|
||||
bgc->data |= mask;
|
||||
else
|
||||
bgc->data &= ~mask;
|
||||
|
||||
bgc->write_reg(bgc->reg_set, bgc->data);
|
||||
|
||||
spin_unlock_irqrestore(&bgc->lock, flags);
|
||||
}
|
||||
|
||||
static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio,
|
||||
int val)
|
||||
{
|
||||
gc->set(gc, gpio, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
struct bgpio_chip *bgc = to_bgpio_chip(gc);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&bgc->lock, flags);
|
||||
|
||||
bgc->dir &= ~bgc->pin2mask(bgc, gpio);
|
||||
bgc->write_reg(bgc->reg_dir, bgc->dir);
|
||||
|
||||
spin_unlock_irqrestore(&bgc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
|
||||
{
|
||||
bgpio_set(gc, gpio, val);
|
||||
struct bgpio_chip *bgc = to_bgpio_chip(gc);
|
||||
unsigned long flags;
|
||||
|
||||
gc->set(gc, gpio, val);
|
||||
|
||||
spin_lock_irqsave(&bgc->lock, flags);
|
||||
|
||||
bgc->dir |= bgc->pin2mask(bgc, gpio);
|
||||
bgc->write_reg(bgc->reg_dir, bgc->dir);
|
||||
|
||||
spin_unlock_irqrestore(&bgc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit bgpio_probe(struct platform_device *pdev)
|
||||
static int bgpio_dir_in_inv(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
const struct platform_device_id *platid = platform_get_device_id(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct bgpio_pdata *pdata = dev_get_platdata(dev);
|
||||
struct bgpio_chip *bgc;
|
||||
struct resource *res_dat;
|
||||
struct resource *res_set;
|
||||
struct resource *res_clr;
|
||||
resource_size_t dat_sz;
|
||||
int bits;
|
||||
int ret;
|
||||
struct bgpio_chip *bgc = to_bgpio_chip(gc);
|
||||
unsigned long flags;
|
||||
|
||||
res_dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
|
||||
if (!res_dat)
|
||||
return -EINVAL;
|
||||
spin_lock_irqsave(&bgc->lock, flags);
|
||||
|
||||
dat_sz = resource_size(res_dat);
|
||||
if (!is_power_of_2(dat_sz))
|
||||
return -EINVAL;
|
||||
bgc->dir |= bgc->pin2mask(bgc, gpio);
|
||||
bgc->write_reg(bgc->reg_dir, bgc->dir);
|
||||
|
||||
bits = dat_sz * 8;
|
||||
if (bits > BITS_PER_LONG)
|
||||
return -EINVAL;
|
||||
spin_unlock_irqrestore(&bgc->lock, flags);
|
||||
|
||||
bgc = devm_kzalloc(dev, sizeof(*bgc), GFP_KERNEL);
|
||||
if (!bgc)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bgc->reg_dat = devm_ioremap(dev, res_dat->start, dat_sz);
|
||||
if (!bgc->reg_dat)
|
||||
return -ENOMEM;
|
||||
static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val)
|
||||
{
|
||||
struct bgpio_chip *bgc = to_bgpio_chip(gc);
|
||||
unsigned long flags;
|
||||
|
||||
res_set = platform_get_resource_byname(pdev, IORESOURCE_MEM, "set");
|
||||
res_clr = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clr");
|
||||
if (res_set && res_clr) {
|
||||
if (resource_size(res_set) != resource_size(res_clr) ||
|
||||
resource_size(res_set) != dat_sz)
|
||||
return -EINVAL;
|
||||
gc->set(gc, gpio, val);
|
||||
|
||||
bgc->reg_set = devm_ioremap(dev, res_set->start, dat_sz);
|
||||
bgc->reg_clr = devm_ioremap(dev, res_clr->start, dat_sz);
|
||||
if (!bgc->reg_set || !bgc->reg_clr)
|
||||
return -ENOMEM;
|
||||
} else if (res_set || res_clr) {
|
||||
spin_lock_irqsave(&bgc->lock, flags);
|
||||
|
||||
bgc->dir &= ~bgc->pin2mask(bgc, gpio);
|
||||
bgc->write_reg(bgc->reg_dir, bgc->dir);
|
||||
|
||||
spin_unlock_irqrestore(&bgc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bgpio_setup_accessors(struct device *dev,
|
||||
struct bgpio_chip *bgc,
|
||||
bool be)
|
||||
{
|
||||
|
||||
switch (bgc->bits) {
|
||||
case 8:
|
||||
bgc->read_reg = bgpio_read8;
|
||||
bgc->write_reg = bgpio_write8;
|
||||
break;
|
||||
case 16:
|
||||
bgc->read_reg = bgpio_read16;
|
||||
bgc->write_reg = bgpio_write16;
|
||||
break;
|
||||
case 32:
|
||||
bgc->read_reg = bgpio_read32;
|
||||
bgc->write_reg = bgpio_write32;
|
||||
break;
|
||||
#if BITS_PER_LONG >= 64
|
||||
case 64:
|
||||
bgc->read_reg = bgpio_read64;
|
||||
bgc->write_reg = bgpio_write64;
|
||||
break;
|
||||
#endif /* BITS_PER_LONG >= 64 */
|
||||
default:
|
||||
dev_err(dev, "unsupported data width %u bits\n", bgc->bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_init(&bgc->lock);
|
||||
bgc->pin2mask = be ? bgpio_pin2mask_be : bgpio_pin2mask;
|
||||
|
||||
bgc->bits = bits;
|
||||
bgc->big_endian_bits = !strcmp(platid->name, "basic-mmio-gpio-be");
|
||||
bgc->data = bgpio_in(bgc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the device and allocate the resources. For setting GPIO's there are
|
||||
* three supported configurations:
|
||||
*
|
||||
* - single input/output register resource (named "dat").
|
||||
* - set/clear pair (named "set" and "clr").
|
||||
* - single output register resource and single input resource ("set" and
|
||||
* dat").
|
||||
*
|
||||
* For the single output register, this drives a 1 by setting a bit and a zero
|
||||
* by clearing a bit. For the set clr pair, this drives a 1 by setting a bit
|
||||
* in the set register and clears it by setting a bit in the clear register.
|
||||
* The configuration is detected by which resources are present.
|
||||
*
|
||||
* For setting the GPIO direction, there are three supported configurations:
|
||||
*
|
||||
* - simple bidirection GPIO that requires no configuration.
|
||||
* - an output direction register (named "dirout") where a 1 bit
|
||||
* indicates the GPIO is an output.
|
||||
* - an input direction register (named "dirin") where a 1 bit indicates
|
||||
* the GPIO is an input.
|
||||
*/
|
||||
static int bgpio_setup_io(struct bgpio_chip *bgc,
|
||||
void __iomem *dat,
|
||||
void __iomem *set,
|
||||
void __iomem *clr)
|
||||
{
|
||||
|
||||
bgc->reg_dat = dat;
|
||||
if (!bgc->reg_dat)
|
||||
return -EINVAL;
|
||||
|
||||
if (set && clr) {
|
||||
bgc->reg_set = set;
|
||||
bgc->reg_clr = clr;
|
||||
bgc->gc.set = bgpio_set_with_clear;
|
||||
} else if (set && !clr) {
|
||||
bgc->reg_set = set;
|
||||
bgc->gc.set = bgpio_set_set;
|
||||
} else {
|
||||
bgc->gc.set = bgpio_set;
|
||||
}
|
||||
|
||||
bgc->gc.ngpio = bits;
|
||||
bgc->gc.direction_input = bgpio_dir_in;
|
||||
bgc->gc.direction_output = bgpio_dir_out;
|
||||
bgc->gc.get = bgpio_get;
|
||||
bgc->gc.set = bgpio_set;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bgpio_setup_direction(struct bgpio_chip *bgc,
|
||||
void __iomem *dirout,
|
||||
void __iomem *dirin)
|
||||
{
|
||||
if (dirout && dirin) {
|
||||
return -EINVAL;
|
||||
} else if (dirout) {
|
||||
bgc->reg_dir = dirout;
|
||||
bgc->gc.direction_output = bgpio_dir_out;
|
||||
bgc->gc.direction_input = bgpio_dir_in;
|
||||
} else if (dirin) {
|
||||
bgc->reg_dir = dirin;
|
||||
bgc->gc.direction_output = bgpio_dir_out_inv;
|
||||
bgc->gc.direction_input = bgpio_dir_in_inv;
|
||||
} else {
|
||||
bgc->gc.direction_output = bgpio_simple_dir_out;
|
||||
bgc->gc.direction_input = bgpio_simple_dir_in;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __devexit bgpio_remove(struct bgpio_chip *bgc)
|
||||
{
|
||||
int err = gpiochip_remove(&bgc->gc);
|
||||
|
||||
kfree(bgc);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bgpio_remove);
|
||||
|
||||
int __devinit bgpio_init(struct bgpio_chip *bgc,
|
||||
struct device *dev,
|
||||
unsigned long sz,
|
||||
void __iomem *dat,
|
||||
void __iomem *set,
|
||||
void __iomem *clr,
|
||||
void __iomem *dirout,
|
||||
void __iomem *dirin,
|
||||
bool big_endian)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!is_power_of_2(sz))
|
||||
return -EINVAL;
|
||||
|
||||
bgc->bits = sz * 8;
|
||||
if (bgc->bits > BITS_PER_LONG)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_init(&bgc->lock);
|
||||
bgc->gc.dev = dev;
|
||||
bgc->gc.label = dev_name(dev);
|
||||
bgc->gc.base = -1;
|
||||
bgc->gc.ngpio = bgc->bits;
|
||||
|
||||
if (pdata)
|
||||
bgc->gc.base = pdata->base;
|
||||
else
|
||||
bgc->gc.base = -1;
|
||||
|
||||
dev_set_drvdata(dev, bgc);
|
||||
|
||||
ret = gpiochip_add(&bgc->gc);
|
||||
ret = bgpio_setup_io(bgc, dat, set, clr);
|
||||
if (ret)
|
||||
dev_err(dev, "gpiochip_add() failed: %d\n", ret);
|
||||
return ret;
|
||||
|
||||
ret = bgpio_setup_accessors(dev, bgc, big_endian);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = bgpio_setup_direction(bgc, dirout, dirin);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bgc->data = bgc->read_reg(bgc->reg_dat);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bgpio_init);
|
||||
|
||||
#ifdef CONFIG_GPIO_BASIC_MMIO
|
||||
|
||||
static void __iomem *bgpio_map(struct platform_device *pdev,
|
||||
const char *name,
|
||||
resource_size_t sane_sz,
|
||||
int *err)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *r;
|
||||
resource_size_t start;
|
||||
resource_size_t sz;
|
||||
void __iomem *ret;
|
||||
|
||||
*err = 0;
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
sz = resource_size(r);
|
||||
if (sz != sane_sz) {
|
||||
*err = -EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start = r->start;
|
||||
if (!devm_request_mem_region(dev, start, sz, r->name)) {
|
||||
*err = -EBUSY;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = devm_ioremap(dev, start, sz);
|
||||
if (!ret) {
|
||||
*err = -ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit bgpio_remove(struct platform_device *pdev)
|
||||
static int __devinit bgpio_pdev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bgpio_chip *bgc = dev_get_drvdata(&pdev->dev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *r;
|
||||
void __iomem *dat;
|
||||
void __iomem *set;
|
||||
void __iomem *clr;
|
||||
void __iomem *dirout;
|
||||
void __iomem *dirin;
|
||||
unsigned long sz;
|
||||
bool be;
|
||||
int err;
|
||||
struct bgpio_chip *bgc;
|
||||
struct bgpio_pdata *pdata = dev_get_platdata(dev);
|
||||
|
||||
return gpiochip_remove(&bgc->gc);
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
|
||||
if (!r)
|
||||
return -EINVAL;
|
||||
|
||||
sz = resource_size(r);
|
||||
|
||||
dat = bgpio_map(pdev, "dat", sz, &err);
|
||||
if (!dat)
|
||||
return err ? err : -EINVAL;
|
||||
|
||||
set = bgpio_map(pdev, "set", sz, &err);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
clr = bgpio_map(pdev, "clr", sz, &err);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dirout = bgpio_map(pdev, "dirout", sz, &err);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dirin = bgpio_map(pdev, "dirin", sz, &err);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
be = !strcmp(platform_get_device_id(pdev)->name, "basic-mmio-gpio-be");
|
||||
|
||||
bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL);
|
||||
if (!bgc)
|
||||
return -ENOMEM;
|
||||
|
||||
err = bgpio_init(bgc, dev, sz, dat, set, clr, dirout, dirin, be);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (pdata) {
|
||||
bgc->gc.base = pdata->base;
|
||||
if (pdata->ngpio > 0)
|
||||
bgc->gc.ngpio = pdata->ngpio;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, bgc);
|
||||
|
||||
return gpiochip_add(&bgc->gc);
|
||||
}
|
||||
|
||||
static int __devexit bgpio_pdev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bgpio_chip *bgc = platform_get_drvdata(pdev);
|
||||
|
||||
return bgpio_remove(bgc);
|
||||
}
|
||||
|
||||
static const struct platform_device_id bgpio_id_table[] = {
|
||||
|
@ -276,21 +525,23 @@ static struct platform_driver bgpio_driver = {
|
|||
.name = "basic-mmio-gpio",
|
||||
},
|
||||
.id_table = bgpio_id_table,
|
||||
.probe = bgpio_probe,
|
||||
.remove = __devexit_p(bgpio_remove),
|
||||
.probe = bgpio_pdev_probe,
|
||||
.remove = __devexit_p(bgpio_pdev_remove),
|
||||
};
|
||||
|
||||
static int __init bgpio_init(void)
|
||||
static int __init bgpio_platform_init(void)
|
||||
{
|
||||
return platform_driver_register(&bgpio_driver);
|
||||
}
|
||||
module_init(bgpio_init);
|
||||
module_init(bgpio_platform_init);
|
||||
|
||||
static void __exit bgpio_exit(void)
|
||||
static void __exit bgpio_platform_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bgpio_driver);
|
||||
}
|
||||
module_exit(bgpio_exit);
|
||||
module_exit(bgpio_platform_exit);
|
||||
|
||||
#endif /* CONFIG_GPIO_BASIC_MMIO */
|
||||
|
||||
MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers");
|
||||
MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <linux/idr.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/gpio.h>
|
||||
|
||||
/* Optional implementation infrastructure for GPIO interfaces.
|
||||
*
|
||||
|
@ -1165,6 +1167,7 @@ struct gpio_chip *gpiochip_find(void *data,
|
|||
|
||||
return chip;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_find);
|
||||
|
||||
/* These "optional" allocation calls help prevent drivers from stomping
|
||||
* on each other, and help provide better diagnostics in debugfs.
|
||||
|
@ -1404,6 +1407,8 @@ int gpio_direction_input(unsigned gpio)
|
|||
status = chip->direction_input(chip, gpio);
|
||||
if (status == 0)
|
||||
clear_bit(FLAG_IS_OUT, &desc->flags);
|
||||
|
||||
trace_gpio_direction(chip->base + gpio, 1, status);
|
||||
lose:
|
||||
return status;
|
||||
fail:
|
||||
|
@ -1457,6 +1462,8 @@ int gpio_direction_output(unsigned gpio, int value)
|
|||
status = chip->direction_output(chip, gpio, value);
|
||||
if (status == 0)
|
||||
set_bit(FLAG_IS_OUT, &desc->flags);
|
||||
trace_gpio_value(chip->base + gpio, 0, value);
|
||||
trace_gpio_direction(chip->base + gpio, 0, status);
|
||||
lose:
|
||||
return status;
|
||||
fail:
|
||||
|
@ -1546,10 +1553,13 @@ EXPORT_SYMBOL_GPL(gpio_set_debounce);
|
|||
int __gpio_get_value(unsigned gpio)
|
||||
{
|
||||
struct gpio_chip *chip;
|
||||
int value;
|
||||
|
||||
chip = gpio_to_chip(gpio);
|
||||
WARN_ON(chip->can_sleep);
|
||||
return chip->get ? chip->get(chip, gpio - chip->base) : 0;
|
||||
value = chip->get ? chip->get(chip, gpio - chip->base) : 0;
|
||||
trace_gpio_value(gpio, 1, value);
|
||||
return value;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__gpio_get_value);
|
||||
|
||||
|
@ -1568,6 +1578,7 @@ void __gpio_set_value(unsigned gpio, int value)
|
|||
|
||||
chip = gpio_to_chip(gpio);
|
||||
WARN_ON(chip->can_sleep);
|
||||
trace_gpio_value(gpio, 0, value);
|
||||
chip->set(chip, gpio - chip->base, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__gpio_set_value);
|
||||
|
@ -1618,10 +1629,13 @@ EXPORT_SYMBOL_GPL(__gpio_to_irq);
|
|||
int gpio_get_value_cansleep(unsigned gpio)
|
||||
{
|
||||
struct gpio_chip *chip;
|
||||
int value;
|
||||
|
||||
might_sleep_if(extra_checks);
|
||||
chip = gpio_to_chip(gpio);
|
||||
return chip->get ? chip->get(chip, gpio - chip->base) : 0;
|
||||
value = chip->get ? chip->get(chip, gpio - chip->base) : 0;
|
||||
trace_gpio_value(gpio, 1, value);
|
||||
return value;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);
|
||||
|
||||
|
@ -1631,6 +1645,7 @@ void gpio_set_value_cansleep(unsigned gpio, int value)
|
|||
|
||||
might_sleep_if(extra_checks);
|
||||
chip = gpio_to_chip(gpio);
|
||||
trace_gpio_value(gpio, 0, value);
|
||||
chip->set(chip, gpio - chip->base, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
|
||||
|
|
|
@ -397,7 +397,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
|
|||
|
||||
irq_set_chip_data(irq, chip);
|
||||
irq_set_chip_and_handler(irq, &pca953x_irq_chip,
|
||||
handle_edge_irq);
|
||||
handle_simple_irq);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
|
|
|
@ -145,7 +145,7 @@ static int via_gpio_get(struct gpio_chip *chip, unsigned int nr)
|
|||
}
|
||||
|
||||
|
||||
static struct viafb_gpio_cfg gpio_config = {
|
||||
static struct viafb_gpio_cfg viafb_gpio_config = {
|
||||
.gpio_chip = {
|
||||
.label = "VIAFB onboard GPIO",
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -183,8 +183,8 @@ static int viafb_gpio_resume(void *private)
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2)
|
||||
viafb_gpio_enable(gpio_config.active_gpios[i]);
|
||||
for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2)
|
||||
viafb_gpio_enable(viafb_gpio_config.active_gpios[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -201,9 +201,9 @@ int viafb_gpio_lookup(const char *name)
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gpio_config.gpio_chip.ngpio; i++)
|
||||
if (!strcmp(name, gpio_config.active_gpios[i]->vg_name))
|
||||
return gpio_config.gpio_chip.base + i;
|
||||
for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i++)
|
||||
if (!strcmp(name, viafb_gpio_config.active_gpios[i]->vg_name))
|
||||
return viafb_gpio_config.gpio_chip.base + i;
|
||||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(viafb_gpio_lookup);
|
||||
|
@ -229,14 +229,15 @@ static __devinit int viafb_gpio_probe(struct platform_device *platdev)
|
|||
for (gpio = viafb_all_gpios;
|
||||
gpio < viafb_all_gpios + VIAFB_NUM_GPIOS; gpio++)
|
||||
if (gpio->vg_port_index == port_cfg[i].ioport_index) {
|
||||
gpio_config.active_gpios[ngpio] = gpio;
|
||||
gpio_config.gpio_names[ngpio] = gpio->vg_name;
|
||||
viafb_gpio_config.active_gpios[ngpio] = gpio;
|
||||
viafb_gpio_config.gpio_names[ngpio] =
|
||||
gpio->vg_name;
|
||||
ngpio++;
|
||||
}
|
||||
}
|
||||
gpio_config.gpio_chip.ngpio = ngpio;
|
||||
gpio_config.gpio_chip.names = gpio_config.gpio_names;
|
||||
gpio_config.vdev = vdev;
|
||||
viafb_gpio_config.gpio_chip.ngpio = ngpio;
|
||||
viafb_gpio_config.gpio_chip.names = viafb_gpio_config.gpio_names;
|
||||
viafb_gpio_config.vdev = vdev;
|
||||
if (ngpio == 0) {
|
||||
printk(KERN_INFO "viafb: no GPIOs configured\n");
|
||||
return 0;
|
||||
|
@ -245,18 +246,18 @@ static __devinit int viafb_gpio_probe(struct platform_device *platdev)
|
|||
* Enable the ports. They come in pairs, with a single
|
||||
* enable bit for both.
|
||||
*/
|
||||
spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags);
|
||||
spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags);
|
||||
for (i = 0; i < ngpio; i += 2)
|
||||
viafb_gpio_enable(gpio_config.active_gpios[i]);
|
||||
spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags);
|
||||
viafb_gpio_enable(viafb_gpio_config.active_gpios[i]);
|
||||
spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags);
|
||||
/*
|
||||
* Get registered.
|
||||
*/
|
||||
gpio_config.gpio_chip.base = -1; /* Dynamic */
|
||||
ret = gpiochip_add(&gpio_config.gpio_chip);
|
||||
viafb_gpio_config.gpio_chip.base = -1; /* Dynamic */
|
||||
ret = gpiochip_add(&viafb_gpio_config.gpio_chip);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret);
|
||||
gpio_config.gpio_chip.ngpio = 0;
|
||||
viafb_gpio_config.gpio_chip.ngpio = 0;
|
||||
}
|
||||
#ifdef CONFIG_PM
|
||||
viafb_pm_register(&viafb_gpio_pm_hooks);
|
||||
|
@ -277,8 +278,8 @@ static int viafb_gpio_remove(struct platform_device *platdev)
|
|||
/*
|
||||
* Get unregistered.
|
||||
*/
|
||||
if (gpio_config.gpio_chip.ngpio > 0) {
|
||||
ret = gpiochip_remove(&gpio_config.gpio_chip);
|
||||
if (viafb_gpio_config.gpio_chip.ngpio > 0) {
|
||||
ret = gpiochip_remove(&viafb_gpio_config.gpio_chip);
|
||||
if (ret) { /* Somebody still using it? */
|
||||
printk(KERN_ERR "Viafb: GPIO remove failed\n");
|
||||
return ret;
|
||||
|
@ -287,11 +288,11 @@ static int viafb_gpio_remove(struct platform_device *platdev)
|
|||
/*
|
||||
* Disable the ports.
|
||||
*/
|
||||
spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags);
|
||||
for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2)
|
||||
viafb_gpio_disable(gpio_config.active_gpios[i]);
|
||||
gpio_config.gpio_chip.ngpio = 0;
|
||||
spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags);
|
||||
spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags);
|
||||
for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2)
|
||||
viafb_gpio_disable(viafb_gpio_config.active_gpios[i]);
|
||||
viafb_gpio_config.gpio_chip.ngpio = 0;
|
||||
spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,64 @@
|
|||
#ifndef __BASIC_MMIO_GPIO_H
|
||||
#define __BASIC_MMIO_GPIO_H
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
struct bgpio_pdata {
|
||||
int base;
|
||||
int ngpio;
|
||||
};
|
||||
|
||||
struct device;
|
||||
|
||||
struct bgpio_chip {
|
||||
struct gpio_chip gc;
|
||||
|
||||
unsigned long (*read_reg)(void __iomem *reg);
|
||||
void (*write_reg)(void __iomem *reg, unsigned long data);
|
||||
|
||||
void __iomem *reg_dat;
|
||||
void __iomem *reg_set;
|
||||
void __iomem *reg_clr;
|
||||
void __iomem *reg_dir;
|
||||
|
||||
/* Number of bits (GPIOs): <register width> * 8. */
|
||||
int bits;
|
||||
|
||||
/*
|
||||
* Some GPIO controllers work with the big-endian bits notation,
|
||||
* e.g. in a 8-bits register, GPIO7 is the least significant bit.
|
||||
*/
|
||||
unsigned long (*pin2mask)(struct bgpio_chip *bgc, unsigned int pin);
|
||||
|
||||
/*
|
||||
* Used to lock bgpio_chip->data. Also, this is needed to keep
|
||||
* shadowed and real data registers writes together.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
|
||||
/* Shadowed data register to clear/set bits safely. */
|
||||
unsigned long data;
|
||||
|
||||
/* Shadowed direction registers to clear/set direction safely. */
|
||||
unsigned long dir;
|
||||
};
|
||||
|
||||
static inline struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc)
|
||||
{
|
||||
return container_of(gc, struct bgpio_chip, gc);
|
||||
}
|
||||
|
||||
int __devexit bgpio_remove(struct bgpio_chip *bgc);
|
||||
int __devinit bgpio_init(struct bgpio_chip *bgc,
|
||||
struct device *dev,
|
||||
unsigned long sz,
|
||||
void __iomem *dat,
|
||||
void __iomem *set,
|
||||
void __iomem *clr,
|
||||
void __iomem *dirout,
|
||||
void __iomem *dirin,
|
||||
bool big_endian);
|
||||
|
||||
#endif /* __BASIC_MMIO_GPIO_H */
|
||||
|
|
56
include/trace/events/gpio.h
Normal file
56
include/trace/events/gpio.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM gpio
|
||||
|
||||
#if !defined(_TRACE_GPIO_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_GPIO_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(gpio_direction,
|
||||
|
||||
TP_PROTO(unsigned gpio, int in, int err),
|
||||
|
||||
TP_ARGS(gpio, in, err),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned, gpio)
|
||||
__field(int, in)
|
||||
__field(int, err)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->gpio = gpio;
|
||||
__entry->in = in;
|
||||
__entry->err = err;
|
||||
),
|
||||
|
||||
TP_printk("%u %3s (%d)", __entry->gpio,
|
||||
__entry->in ? "in" : "out", __entry->err)
|
||||
);
|
||||
|
||||
TRACE_EVENT(gpio_value,
|
||||
|
||||
TP_PROTO(unsigned gpio, int get, int value),
|
||||
|
||||
TP_ARGS(gpio, get, value),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned, gpio)
|
||||
__field(int, get)
|
||||
__field(int, value)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->gpio = gpio;
|
||||
__entry->get = get;
|
||||
__entry->value = value;
|
||||
),
|
||||
|
||||
TP_printk("%u %3s %d", __entry->gpio,
|
||||
__entry->get ? "get" : "set", __entry->value)
|
||||
);
|
||||
|
||||
#endif /* if !defined(_TRACE_GPIO_H) || defined(TRACE_HEADER_MULTI_READ) */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
Loading…
Reference in a new issue