From 8467afec5f8137fd0c13121f8a38c99c54c913f6 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 20 May 2011 00:40:14 -0600 Subject: basic_mmio_gpio: remove runtime width/endianness evaluation Remove endianness/width calculations at runtime by installing function pointers for bit-to-mask conversion and register accessors. Reported-by: Thomas Gleixner Signed-off-by: Jamie Iles Acked-by: Anton Vorontsov Signed-off-by: Grant Likely --- drivers/gpio/basic_mmio_gpio.c | 136 ++++++++++++++++++++++++++++------------- 1 file changed, 92 insertions(+), 44 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c index 3addea6..e935a24 100644 --- a/drivers/gpio/basic_mmio_gpio.c +++ b/drivers/gpio/basic_mmio_gpio.c @@ -63,6 +63,10 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` 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; @@ -74,7 +78,7 @@ struct bgpio_chip { * 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; + unsigned long (*pin2mask)(struct bgpio_chip *bgc, unsigned int pin); /* * Used to lock bgpio_chip->data. Also, this is needed to keep @@ -91,70 +95,77 @@ static struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc) return container_of(gc, struct bgpio_chip, gc); } -static unsigned long bgpio_in(struct bgpio_chip *bgc) +static void bgpio_write8(void __iomem *reg, unsigned long data) { - 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); -#if BITS_PER_LONG >= 64 - case 64: - return __raw_readq(bgc->reg_dat); -#endif - } - return -EINVAL; + __raw_writeb(data, reg); } -static void bgpio_out(struct bgpio_chip *bgc, void __iomem *reg, - unsigned long data) +static unsigned long bgpio_read8(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; + return __raw_readb(reg); +} + +static void bgpio_write16(void __iomem *reg, unsigned long data) +{ + __raw_writew(data, reg); +} + +static unsigned long bgpio_read16(void __iomem *reg) +{ + return __raw_readw(reg); +} + +static void bgpio_write32(void __iomem *reg, unsigned long data) +{ + __raw_writel(data, reg); +} + +static unsigned long bgpio_read32(void __iomem *reg) +{ + return __raw_readl(reg); +} + #if BITS_PER_LONG >= 64 - case 64: - __raw_writeq(data, reg); - return; -#endif - } +static void bgpio_write64(void __iomem *reg, unsigned long data) +{ + __raw_writeq(data, reg); } +static unsigned long bgpio_read64(void __iomem *reg) +{ + return __raw_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); + bgc->write_reg(bgc->reg_set, mask); else - bgpio_out(bgc, bgc->reg_clr, mask); + bgc->write_reg(bgc->reg_clr, mask); return; } @@ -165,7 +176,7 @@ 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); } @@ -181,9 +192,44 @@ static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) return 0; } -static int __devinit bgpio_probe(struct platform_device *pdev) +static int bgpio_setup_accessors(struct platform_device *pdev, + struct bgpio_chip *bgc) { const struct platform_device_id *platid = platform_get_device_id(pdev); + + 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(&pdev->dev, "unsupported data width %u bits\n", + bgc->bits); + return -EINVAL; + } + + bgc->pin2mask = strcmp(platid->name, "basic-mmio-gpio-be") ? + bgpio_pin2mask : bgpio_pin2mask_be; + + return 0; +} + +static int __devinit bgpio_probe(struct platform_device *pdev) +{ struct device *dev = &pdev->dev; struct bgpio_pdata *pdata = dev_get_platdata(dev); struct bgpio_chip *bgc; @@ -232,9 +278,11 @@ static int __devinit bgpio_probe(struct platform_device *pdev) spin_lock_init(&bgc->lock); bgc->bits = bits; - bgc->big_endian_bits = !strcmp(platid->name, "basic-mmio-gpio-be"); - bgc->data = bgpio_in(bgc); + ret = bgpio_setup_accessors(pdev, bgc); + if (ret) + return ret; + bgc->data = bgc->read_reg(bgc->reg_dat); bgc->gc.ngpio = bits; bgc->gc.direction_input = bgpio_dir_in; bgc->gc.direction_output = bgpio_dir_out; -- cgit v1.1 From 4ddb8ae217ad2aae888d00d97c5160f677dd38f4 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 20 May 2011 00:40:14 -0600 Subject: basic_mmio_gpio: convert to platform_{get,set}_drvdata() Use the platform drvdata helpers rather than working on the struct device itself. Signed-off-by: Jamie Iles Acked-by: Anton Vorontsov Signed-off-by: Grant Likely --- drivers/gpio/basic_mmio_gpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c index e935a24..5db5de4 100644 --- a/drivers/gpio/basic_mmio_gpio.c +++ b/drivers/gpio/basic_mmio_gpio.c @@ -296,7 +296,7 @@ static int __devinit bgpio_probe(struct platform_device *pdev) else bgc->gc.base = -1; - dev_set_drvdata(dev, bgc); + platform_set_drvdata(pdev, bgc); ret = gpiochip_add(&bgc->gc); if (ret) @@ -307,7 +307,7 @@ static int __devinit bgpio_probe(struct platform_device *pdev) static int __devexit bgpio_remove(struct platform_device *pdev) { - struct bgpio_chip *bgc = dev_get_drvdata(&pdev->dev); + struct bgpio_chip *bgc = platform_get_drvdata(pdev); return gpiochip_remove(&bgc->gc); } -- cgit v1.1 From 924e7a9fc6da124588e27c611841d07047c157b4 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 20 May 2011 00:40:15 -0600 Subject: basic_mmio_gpio: allow overriding number of gpio Some platforms may have a number of GPIO that is less than the register width of the peripheral. Signed-off-by: Jamie Iles Acked-by: Anton Vorontsov Signed-off-by: Grant Likely --- drivers/gpio/basic_mmio_gpio.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c index 5db5de4..2b2d384 100644 --- a/drivers/gpio/basic_mmio_gpio.c +++ b/drivers/gpio/basic_mmio_gpio.c @@ -239,6 +239,7 @@ static int __devinit bgpio_probe(struct platform_device *pdev) resource_size_t dat_sz; int bits; int ret; + int ngpio; res_dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); if (!res_dat) @@ -249,6 +250,7 @@ static int __devinit bgpio_probe(struct platform_device *pdev) return -EINVAL; bits = dat_sz * 8; + ngpio = bits; if (bits > BITS_PER_LONG) return -EINVAL; @@ -277,13 +279,22 @@ static int __devinit bgpio_probe(struct platform_device *pdev) spin_lock_init(&bgc->lock); + if (pdata) { + bgc->gc.base = pdata->base; + if (pdata->ngpio > 0) + ngpio = pdata->ngpio; + } else { + bgc->gc.base = -1; + } + bgc->bits = bits; ret = bgpio_setup_accessors(pdev, bgc); if (ret) return ret; bgc->data = bgc->read_reg(bgc->reg_dat); - bgc->gc.ngpio = bits; + + bgc->gc.ngpio = ngpio; bgc->gc.direction_input = bgpio_dir_in; bgc->gc.direction_output = bgpio_dir_out; bgc->gc.get = bgpio_get; @@ -291,11 +302,6 @@ static int __devinit bgpio_probe(struct platform_device *pdev) bgc->gc.dev = dev; bgc->gc.label = dev_name(dev); - if (pdata) - bgc->gc.base = pdata->base; - else - bgc->gc.base = -1; - platform_set_drvdata(pdev, bgc); ret = gpiochip_add(&bgc->gc); -- cgit v1.1 From 364b5e871839fafffa851bbff9ecf64ac641023c Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 20 May 2011 00:40:16 -0600 Subject: basic_mmio_gpio: request register regions Make sure that we get the register regions with request_mem_region() before ioremap() to make sure we have exclusive access. Signed-off-by: Jamie Iles Acked-by: Anton Vorontsov Signed-off-by: Grant Likely --- drivers/gpio/basic_mmio_gpio.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c index 2b2d384..90f7f89 100644 --- a/drivers/gpio/basic_mmio_gpio.c +++ b/drivers/gpio/basic_mmio_gpio.c @@ -192,6 +192,16 @@ static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) return 0; } +static void __iomem *bgpio_request_and_map(struct device *dev, + struct resource *res) +{ + if (!devm_request_mem_region(dev, res->start, resource_size(res), + res->name ?: "mmio_gpio")) + return NULL; + + return devm_ioremap(dev, res->start, resource_size(res)); +} + static int bgpio_setup_accessors(struct platform_device *pdev, struct bgpio_chip *bgc) { @@ -258,7 +268,7 @@ static int __devinit bgpio_probe(struct platform_device *pdev) if (!bgc) return -ENOMEM; - bgc->reg_dat = devm_ioremap(dev, res_dat->start, dat_sz); + bgc->reg_dat = bgpio_request_and_map(dev, res_dat); if (!bgc->reg_dat) return -ENOMEM; @@ -269,8 +279,8 @@ static int __devinit bgpio_probe(struct platform_device *pdev) resource_size(res_set) != dat_sz) return -EINVAL; - bgc->reg_set = devm_ioremap(dev, res_set->start, dat_sz); - bgc->reg_clr = devm_ioremap(dev, res_clr->start, dat_sz); + bgc->reg_set = bgpio_request_and_map(dev, res_set); + bgc->reg_clr = bgpio_request_and_map(dev, res_clr); if (!bgc->reg_set || !bgc->reg_clr) return -ENOMEM; } else if (res_set || res_clr) { -- cgit v1.1 From e027d6f9d52d9ccabb307d0cb0265de3481b1e9e Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 20 May 2011 00:40:16 -0600 Subject: basic_mmio_gpio: detect output method at probe time Rather than detecting the output method each time in the .set() callback, do it at probe time and set the appropriate callback. Changes since v2: moved the reg_dat initialization into bgpio_setup_io(). Signed-off-by: Jamie Iles Acked-by: Anton Vorontsov Signed-off-by: Grant Likely --- drivers/gpio/basic_mmio_gpio.c | 90 +++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 31 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c index 90f7f89..728bc67 100644 --- a/drivers/gpio/basic_mmio_gpio.c +++ b/drivers/gpio/basic_mmio_gpio.c @@ -161,14 +161,6 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) unsigned long mask = bgc->pin2mask(bgc, gpio); unsigned long flags; - if (bgc->reg_set) { - if (val) - bgc->write_reg(bgc->reg_set, mask); - else - bgc->write_reg(bgc->reg_clr, mask); - return; - } - spin_lock_irqsave(&bgc->lock, flags); if (val) @@ -181,6 +173,18 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) 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 int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) { return 0; @@ -188,7 +192,8 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { - bgpio_set(gc, gpio, val); + gc->set(gc, gpio, val); + return 0; } @@ -238,18 +243,25 @@ static int bgpio_setup_accessors(struct platform_device *pdev, return 0; } -static int __devinit bgpio_probe(struct platform_device *pdev) +/* + * Create the device and allocate the resources. For setting GPIO's there are + * two supported configurations: + * + * - single output register resource (named "dat"). + * - set/clear pair (named "set" and "clr"). + * + * 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. + */ +static int bgpio_setup_io(struct platform_device *pdev, + struct bgpio_chip *bgc) { - 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; + struct resource *res_dat; resource_size_t dat_sz; - int bits; - int ret; - int ngpio; res_dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); if (!res_dat) @@ -259,16 +271,11 @@ static int __devinit bgpio_probe(struct platform_device *pdev) if (!is_power_of_2(dat_sz)) return -EINVAL; - bits = dat_sz * 8; - ngpio = bits; - if (bits > BITS_PER_LONG) + bgc->bits = dat_sz * 8; + if (bgc->bits > BITS_PER_LONG) return -EINVAL; - bgc = devm_kzalloc(dev, sizeof(*bgc), GFP_KERNEL); - if (!bgc) - return -ENOMEM; - - bgc->reg_dat = bgpio_request_and_map(dev, res_dat); + bgc->reg_dat = bgpio_request_and_map(&pdev->dev, res_dat); if (!bgc->reg_dat) return -ENOMEM; @@ -276,19 +283,41 @@ static int __devinit bgpio_probe(struct platform_device *pdev) 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) + resource_size(res_set) != resource_size(res_dat)) return -EINVAL; - bgc->reg_set = bgpio_request_and_map(dev, res_set); - bgc->reg_clr = bgpio_request_and_map(dev, res_clr); + bgc->reg_set = bgpio_request_and_map(&pdev->dev, res_set); + bgc->reg_clr = bgpio_request_and_map(&pdev->dev, res_clr); if (!bgc->reg_set || !bgc->reg_clr) return -ENOMEM; + + bgc->gc.set = bgpio_set_with_clear; } else if (res_set || res_clr) { return -EINVAL; + } else { + bgc->gc.set = bgpio_set; } - spin_lock_init(&bgc->lock); + return 0; +} + +static int __devinit bgpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bgpio_pdata *pdata = dev_get_platdata(dev); + struct bgpio_chip *bgc; + int ret; + int ngpio; + + bgc = devm_kzalloc(dev, sizeof(*bgc), GFP_KERNEL); + if (!bgc) + return -ENOMEM; + + ret = bgpio_setup_io(pdev, bgc); + if (ret) + return ret; + ngpio = bgc->bits; if (pdata) { bgc->gc.base = pdata->base; if (pdata->ngpio > 0) @@ -297,18 +326,17 @@ static int __devinit bgpio_probe(struct platform_device *pdev) bgc->gc.base = -1; } - bgc->bits = bits; ret = bgpio_setup_accessors(pdev, bgc); if (ret) return ret; + spin_lock_init(&bgc->lock); bgc->data = bgc->read_reg(bgc->reg_dat); bgc->gc.ngpio = ngpio; bgc->gc.direction_input = bgpio_dir_in; bgc->gc.direction_output = bgpio_dir_out; bgc->gc.get = bgpio_get; - bgc->gc.set = bgpio_set; bgc->gc.dev = dev; bgc->gc.label = dev_name(dev); -- cgit v1.1 From dd86a0cc5e35161538c10e35eb85e2ad0adfe14d Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 20 May 2011 00:40:16 -0600 Subject: basic_mmio_gpio: support different input/output registers Some controllers have separate input and output registers. For these controllers, use "set" for the output and "dat" for the input. Changes since v2: reuse "set" for output and "dat" for input rather than adding a new "in" register. Signed-off-by: Jamie Iles Acked-by: Anton Vorontsov Signed-off-by: Grant Likely --- drivers/gpio/basic_mmio_gpio.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c index 728bc67..6b99489 100644 --- a/drivers/gpio/basic_mmio_gpio.c +++ b/drivers/gpio/basic_mmio_gpio.c @@ -185,6 +185,24 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, 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_dir_in(struct gpio_chip *gc, unsigned int gpio) { return 0; @@ -245,10 +263,12 @@ static int bgpio_setup_accessors(struct platform_device *pdev, /* * Create the device and allocate the resources. For setting GPIO's there are - * two supported configurations: + * three supported configurations: * - * - single output register resource (named "dat"). + * - 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 @@ -292,12 +312,21 @@ static int bgpio_setup_io(struct platform_device *pdev, return -ENOMEM; bgc->gc.set = bgpio_set_with_clear; - } else if (res_set || res_clr) { - return -EINVAL; + } else if (res_set && !res_clr) { + if (resource_size(res_set) != resource_size(res_dat)) + return -EINVAL; + + bgc->reg_set = bgpio_request_and_map(&pdev->dev, res_set); + if (!bgc->reg_set) + return -ENOMEM; + + bgc->gc.set = bgpio_set_set; } else { bgc->gc.set = bgpio_set; } + bgc->gc.get = bgpio_get; + return 0; } @@ -336,7 +365,6 @@ static int __devinit bgpio_probe(struct platform_device *pdev) bgc->gc.ngpio = ngpio; bgc->gc.direction_input = bgpio_dir_in; bgc->gc.direction_output = bgpio_dir_out; - bgc->gc.get = bgpio_get; bgc->gc.dev = dev; bgc->gc.label = dev_name(dev); -- cgit v1.1 From 31029116ebc1f2481bd2380437e9f7a18f18dca5 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 20 May 2011 00:40:17 -0600 Subject: basic_mmio_gpio: support direction registers Most controllers require the direction of a GPIO to be set by writing to a direction register. Add support for either an input direction register or an output direction register. Signed-off-by: Jamie Iles Acked-by: Anton Vorontsov Signed-off-by: Grant Likely --- drivers/gpio/basic_mmio_gpio.c | 114 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c index 6b99489..f4afd96 100644 --- a/drivers/gpio/basic_mmio_gpio.c +++ b/drivers/gpio/basic_mmio_gpio.c @@ -70,6 +70,7 @@ struct bgpio_chip { void __iomem *reg_dat; void __iomem *reg_set; void __iomem *reg_clr; + void __iomem *reg_dir; /* Number of bits (GPIOs): * 8. */ int bits; @@ -88,6 +89,9 @@ struct bgpio_chip { /* Shadowed data register to clear/set bits safely. */ unsigned long data; + + /* Shadowed direction registers to clear/set direction safely. */ + unsigned long dir; }; static struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc) @@ -203,15 +207,80 @@ static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) 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) { + 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 bgpio_dir_in_inv(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_inv(struct gpio_chip *gc, unsigned int gpio, int 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; } @@ -274,6 +343,14 @@ static int bgpio_setup_accessors(struct platform_device *pdev, * 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 platform_device *pdev, struct bgpio_chip *bgc) @@ -330,6 +407,37 @@ static int bgpio_setup_io(struct platform_device *pdev, return 0; } +static int bgpio_setup_direction(struct platform_device *pdev, + struct bgpio_chip *bgc) +{ + struct resource *res_dirout; + struct resource *res_dirin; + + res_dirout = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "dirout"); + res_dirin = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirin"); + if (res_dirout && res_dirin) { + return -EINVAL; + } else if (res_dirout) { + bgc->reg_dir = bgpio_request_and_map(&pdev->dev, res_dirout); + if (!bgc->reg_dir) + return -ENOMEM; + bgc->gc.direction_output = bgpio_dir_out; + bgc->gc.direction_input = bgpio_dir_in; + } else if (res_dirin) { + bgc->reg_dir = bgpio_request_and_map(&pdev->dev, res_dirin); + if (!bgc->reg_dir) + return -ENOMEM; + 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; +} + static int __devinit bgpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -360,11 +468,13 @@ static int __devinit bgpio_probe(struct platform_device *pdev) return ret; spin_lock_init(&bgc->lock); + ret = bgpio_setup_direction(pdev, bgc); + if (ret) + return ret; + bgc->data = bgc->read_reg(bgc->reg_dat); bgc->gc.ngpio = ngpio; - bgc->gc.direction_input = bgpio_dir_in; - bgc->gc.direction_output = bgpio_dir_out; bgc->gc.dev = dev; bgc->gc.label = dev_name(dev); -- cgit v1.1 From fd9962352105f19711d55cc2caaf75b2e201598b Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 20 May 2011 00:40:17 -0600 Subject: basic_mmio_gpio: convert to non-__raw* accessors The __raw_* accessors don't include memory barriers and can cause problems when writes get stuck in write buffers. Suggested-by: Linus Walleij Acked-by: Anton Vorontsov Signed-off-by: Jamie Iles Signed-off-by: Grant Likely --- drivers/gpio/basic_mmio_gpio.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c index f4afd96..b2ec45f 100644 --- a/drivers/gpio/basic_mmio_gpio.c +++ b/drivers/gpio/basic_mmio_gpio.c @@ -101,43 +101,43 @@ static struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc) static void bgpio_write8(void __iomem *reg, unsigned long data) { - __raw_writeb(data, reg); + writeb(data, reg); } static unsigned long bgpio_read8(void __iomem *reg) { - return __raw_readb(reg); + return readb(reg); } static void bgpio_write16(void __iomem *reg, unsigned long data) { - __raw_writew(data, reg); + writew(data, reg); } static unsigned long bgpio_read16(void __iomem *reg) { - return __raw_readw(reg); + return readw(reg); } static void bgpio_write32(void __iomem *reg, unsigned long data) { - __raw_writel(data, reg); + writel(data, reg); } static unsigned long bgpio_read32(void __iomem *reg) { - return __raw_readl(reg); + return readl(reg); } #if BITS_PER_LONG >= 64 static void bgpio_write64(void __iomem *reg, unsigned long data) { - __raw_writeq(data, reg); + writeq(data, reg); } static unsigned long bgpio_read64(void __iomem *reg) { - return __raw_readq(reg); + return readq(reg); } #endif /* BITS_PER_LONG >= 64 */ -- cgit v1.1 From 6cff9e2b34414733fa61fddef909e9ae1e4beaf3 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 20 May 2011 00:40:17 -0600 Subject: gpio: remove redundant Kconfig depends on GPIOLIB Since all the drivers are in a 'if GPIOLIB/endif block', the depends on GPIOLIB is redundant so remove them. Signed-off-by: H Hartley Sweeten Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d3b2953..d2cd6cf 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -77,7 +77,6 @@ config GPIO_BASIC_MMIO 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 +100,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 +120,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 +346,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 -- cgit v1.1 From 8fa0c9bf82b0b8697ace9532c5a50a0f4e0c7bb5 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 20 May 2011 00:40:18 -0600 Subject: gpiolib: export gpiochip_find Export function gpiochip_find so that modules can use it. Signed-off-by: Jean Delvare Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 36a2974..c27e52b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1165,6 +1165,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. -- cgit v1.1 From cc300d9ead66235e23c674babb8a4ae4ad4c9db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bot=C3=B6?= Date: Fri, 20 May 2011 00:40:18 -0600 Subject: gpio/pca953x: Use handle_simple_irq instead of handle_edge_irq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make pca953x driver use the simple irq handler since no hardware control is necessary and the driver lacks an irq_ack function. handle_edge_irq() calls chip->irq_ack() which crashes since this function does not exist. Signed-off-by: Erik Botö Signed-off-by: Richard Röjfors Signed-off-by: Grant Likely --- drivers/gpio/pca953x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index 7630ab7b..78a8439 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -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 -- cgit v1.1 From 3f397c2144e46d9127662fdb6314f21960d8563d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 20 May 2011 00:40:19 -0600 Subject: gpio: add trace events for setting direction and value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch allows to trace gpio operations using ftrace Signed-off-by: Uwe Kleine-König Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c27e52b..137a8ca 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -12,6 +12,8 @@ #include #include +#define CREATE_TRACE_POINTS +#include /* Optional implementation infrastructure for GPIO interfaces. * @@ -1405,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: @@ -1458,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: @@ -1547,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); @@ -1569,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); @@ -1619,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); @@ -1632,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); -- cgit v1.1 From e849dc044af0939135c822833092bc9baf480222 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 20 May 2011 00:40:19 -0600 Subject: gpio: remove some legacy comments in build files The build files for drivers/gpio has some wording and comments about the directory being reserved exclusively for GPIO expanders (according to the gpio.txt file these are on external busses) and this has been false for some time. We already have PL061 and Xilinx drivers which are in silicon and now I'm moving more silicon drivers here, so delete this and reword it a bit. Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 8 ++++---- drivers/gpio/Makefile | 6 +----- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d2cd6cf..44235bd 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -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,12 +63,12 @@ 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 tristate "Basic memory-mapped GPIO controllers support" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index becef59..7b2bdce 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -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 -- cgit v1.1 From 280df6b3c3ad777a91f1011cd98d50df891bfef8 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 20 May 2011 00:40:19 -0600 Subject: basic_mmio_gpio: split into a gpio library and platform device Allow GPIO_BASIC_MMIO_CORE to be used to provide an accessor library for implementing GPIO drivers whilst abstracting the register access detail. Based on a patch from Anton Vorontsov[1] and adapted to allow bgpio_chip to be embedded in another structure. Changes since v1: - Register the gpio_chip in the platform device probe 1. https://lkml.org/lkml/2011/4/19/401 Signed-off-by: Anton Vorontsov Signed-off-by: Jamie Iles Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/basic_mmio_gpio.c | 317 ++++++++++++++++++++++------------------- 3 files changed, 176 insertions(+), 148 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 44235bd..b57ec09 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -70,8 +70,14 @@ config GPIO_MAX730X 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. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 7b2bdce..d92ce3a 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -6,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 diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c index b2ec45f..8152e9f5 100644 --- a/drivers/gpio/basic_mmio_gpio.c +++ b/drivers/gpio/basic_mmio_gpio.c @@ -45,6 +45,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` */ #include +#include #include #include #include @@ -61,44 +62,6 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` #include #include -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): * 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 struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc) -{ - return container_of(gc, struct bgpio_chip, gc); -} - static void bgpio_write8(void __iomem *reg, unsigned long data) { writeb(data, reg); @@ -284,20 +247,10 @@ static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val) return 0; } -static void __iomem *bgpio_request_and_map(struct device *dev, - struct resource *res) -{ - if (!devm_request_mem_region(dev, res->start, resource_size(res), - res->name ?: "mmio_gpio")) - return NULL; - - return devm_ioremap(dev, res->start, resource_size(res)); -} - -static int bgpio_setup_accessors(struct platform_device *pdev, - struct bgpio_chip *bgc) +static int bgpio_setup_accessors(struct device *dev, + struct bgpio_chip *bgc, + bool be) { - const struct platform_device_id *platid = platform_get_device_id(pdev); switch (bgc->bits) { case 8: @@ -319,13 +272,11 @@ static int bgpio_setup_accessors(struct platform_device *pdev, break; #endif /* BITS_PER_LONG >= 64 */ default: - dev_err(&pdev->dev, "unsupported data width %u bits\n", - bgc->bits); + dev_err(dev, "unsupported data width %u bits\n", bgc->bits); return -EINVAL; } - bgc->pin2mask = strcmp(platid->name, "basic-mmio-gpio-be") ? - bgpio_pin2mask : bgpio_pin2mask_be; + bgc->pin2mask = be ? bgpio_pin2mask_be : bgpio_pin2mask; return 0; } @@ -352,51 +303,22 @@ static int bgpio_setup_accessors(struct platform_device *pdev, * - an input direction register (named "dirin") where a 1 bit indicates * the GPIO is an input. */ -static int bgpio_setup_io(struct platform_device *pdev, - struct bgpio_chip *bgc) +static int bgpio_setup_io(struct bgpio_chip *bgc, + void __iomem *dat, + void __iomem *set, + void __iomem *clr) { - struct resource *res_set; - struct resource *res_clr; - struct resource *res_dat; - resource_size_t dat_sz; - res_dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); - if (!res_dat) - return -EINVAL; - - dat_sz = resource_size(res_dat); - if (!is_power_of_2(dat_sz)) - return -EINVAL; - - bgc->bits = dat_sz * 8; - if (bgc->bits > BITS_PER_LONG) - return -EINVAL; - - bgc->reg_dat = bgpio_request_and_map(&pdev->dev, res_dat); + bgc->reg_dat = dat; if (!bgc->reg_dat) - return -ENOMEM; - - 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) != resource_size(res_dat)) - return -EINVAL; - - bgc->reg_set = bgpio_request_and_map(&pdev->dev, res_set); - bgc->reg_clr = bgpio_request_and_map(&pdev->dev, res_clr); - if (!bgc->reg_set || !bgc->reg_clr) - return -ENOMEM; + return -EINVAL; + if (set && clr) { + bgc->reg_set = set; + bgc->reg_clr = clr; bgc->gc.set = bgpio_set_with_clear; - } else if (res_set && !res_clr) { - if (resource_size(res_set) != resource_size(res_dat)) - return -EINVAL; - - bgc->reg_set = bgpio_request_and_map(&pdev->dev, res_set); - if (!bgc->reg_set) - return -ENOMEM; - + } else if (set && !clr) { + bgc->reg_set = set; bgc->gc.set = bgpio_set_set; } else { bgc->gc.set = bgpio_set; @@ -407,27 +329,18 @@ static int bgpio_setup_io(struct platform_device *pdev, return 0; } -static int bgpio_setup_direction(struct platform_device *pdev, - struct bgpio_chip *bgc) +static int bgpio_setup_direction(struct bgpio_chip *bgc, + void __iomem *dirout, + void __iomem *dirin) { - struct resource *res_dirout; - struct resource *res_dirin; - - res_dirout = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "dirout"); - res_dirin = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirin"); - if (res_dirout && res_dirin) { + if (dirout && dirin) { return -EINVAL; - } else if (res_dirout) { - bgc->reg_dir = bgpio_request_and_map(&pdev->dev, res_dirout); - if (!bgc->reg_dir) - return -ENOMEM; + } else if (dirout) { + bgc->reg_dir = dirout; bgc->gc.direction_output = bgpio_dir_out; bgc->gc.direction_input = bgpio_dir_in; - } else if (res_dirin) { - bgc->reg_dir = bgpio_request_and_map(&pdev->dev, res_dirin); - if (!bgc->reg_dir) - return -ENOMEM; + } else if (dirin) { + bgc->reg_dir = dirin; bgc->gc.direction_output = bgpio_dir_out_inv; bgc->gc.direction_input = bgpio_dir_in_inv; } else { @@ -438,60 +351,166 @@ static int bgpio_setup_direction(struct platform_device *pdev, return 0; } -static int __devinit bgpio_probe(struct platform_device *pdev) +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) { - struct device *dev = &pdev->dev; - struct bgpio_pdata *pdata = dev_get_platdata(dev); - struct bgpio_chip *bgc; int ret; - int ngpio; - bgc = devm_kzalloc(dev, sizeof(*bgc), GFP_KERNEL); - if (!bgc) - return -ENOMEM; + if (!is_power_of_2(sz)) + return -EINVAL; - ret = bgpio_setup_io(pdev, bgc); + 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; + + ret = bgpio_setup_io(bgc, dat, set, clr); if (ret) return ret; - ngpio = bgc->bits; - if (pdata) { - bgc->gc.base = pdata->base; - if (pdata->ngpio > 0) - ngpio = pdata->ngpio; - } else { - bgc->gc.base = -1; - } - - ret = bgpio_setup_accessors(pdev, bgc); + ret = bgpio_setup_accessors(dev, bgc, big_endian); if (ret) return ret; - spin_lock_init(&bgc->lock); - ret = bgpio_setup_direction(pdev, bgc); + ret = bgpio_setup_direction(bgc, dirout, dirin); if (ret) return ret; bgc->data = bgc->read_reg(bgc->reg_dat); - bgc->gc.ngpio = ngpio; - bgc->gc.dev = dev; - bgc->gc.label = dev_name(dev); + return ret; +} +EXPORT_SYMBOL_GPL(bgpio_init); - platform_set_drvdata(pdev, bgc); +#ifdef CONFIG_GPIO_BASIC_MMIO - ret = gpiochip_add(&bgc->gc); - if (ret) - dev_err(dev, "gpiochip_add() failed: %d\n", ret); +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 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); + + 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 gpiochip_remove(&bgc->gc); + return bgpio_remove(bgc); } static const struct platform_device_id bgpio_id_table[] = { @@ -506,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 "); -- cgit v1.1