summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpio-dwapb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-dwapb.c')
-rw-r--r--drivers/gpio/gpio-dwapb.c81
1 files changed, 61 insertions, 20 deletions
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
index 6730c6642ce3..226977f78482 100644
--- a/drivers/gpio/gpio-dwapb.c
+++ b/drivers/gpio/gpio-dwapb.c
@@ -8,10 +8,9 @@
* All enquiries to support@picochip.com
*/
#include <linux/acpi.h>
-#include <linux/gpio/driver.h>
-/* FIXME: for gpio_get_value(), replace this with direct register read */
-#include <linux/gpio.h>
+#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -53,9 +52,9 @@
#define GPIO_EXT_PORTD 0x5c
#define DWAPB_MAX_PORTS 4
-#define GPIO_EXT_PORT_SIZE (GPIO_EXT_PORTB - GPIO_EXT_PORTA)
-#define GPIO_SWPORT_DR_SIZE (GPIO_SWPORTB_DR - GPIO_SWPORTA_DR)
-#define GPIO_SWPORT_DDR_SIZE (GPIO_SWPORTB_DDR - GPIO_SWPORTA_DDR)
+#define GPIO_EXT_PORT_STRIDE 0x04 /* register stride 32 bits */
+#define GPIO_SWPORT_DR_STRIDE 0x0c /* register stride 3*32 bits */
+#define GPIO_SWPORT_DDR_STRIDE 0x0c /* register stride 3*32 bits */
#define GPIO_REG_OFFSET_V2 1
@@ -100,6 +99,7 @@ struct dwapb_gpio {
struct irq_domain *domain;
unsigned int flags;
struct reset_control *rst;
+ struct clk *clk;
};
static inline u32 gpio_reg_v2_convert(unsigned int offset)
@@ -153,16 +153,40 @@ static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
return irq_find_mapping(gpio->domain, offset);
}
+static struct dwapb_gpio_port *dwapb_offs_to_port(struct dwapb_gpio *gpio, unsigned int offs)
+{
+ struct dwapb_gpio_port *port;
+ int i;
+
+ for (i = 0; i < gpio->nr_ports; i++) {
+ port = &gpio->ports[i];
+ if (port->idx == offs / 32)
+ return port;
+ }
+
+ return NULL;
+}
+
static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
{
- u32 v = dwapb_read(gpio, GPIO_INT_POLARITY);
+ struct dwapb_gpio_port *port = dwapb_offs_to_port(gpio, offs);
+ struct gpio_chip *gc;
+ u32 pol;
+ int val;
+
+ if (!port)
+ return;
+ gc = &port->gc;
- if (gpio_get_value(gpio->ports[0].gc.base + offs))
- v &= ~BIT(offs);
+ pol = dwapb_read(gpio, GPIO_INT_POLARITY);
+ /* Just read the current value right out of the data register */
+ val = gc->get(gc, offs % 32);
+ if (val)
+ pol &= ~BIT(offs);
else
- v |= BIT(offs);
+ pol |= BIT(offs);
- dwapb_write(gpio, GPIO_INT_POLARITY, v);
+ dwapb_write(gpio, GPIO_INT_POLARITY, pol);
}
static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
@@ -476,11 +500,12 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
return -ENOMEM;
#endif
- dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_SIZE);
- set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_SIZE);
+ dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_STRIDE);
+ set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_STRIDE);
dirout = gpio->regs + GPIO_SWPORTA_DDR +
- (pp->idx * GPIO_SWPORT_DDR_SIZE);
+ (pp->idx * GPIO_SWPORT_DDR_STRIDE);
+ /* This registers 32 GPIO lines per port */
err = bgpio_init(&port->gc, gpio->dev, 4, dat, set, NULL, dirout,
NULL, 0);
if (err) {
@@ -647,6 +672,16 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gpio->regs))
return PTR_ERR(gpio->regs);
+ /* Optional bus clock */
+ gpio->clk = devm_clk_get(&pdev->dev, "bus");
+ if (!IS_ERR(gpio->clk)) {
+ err = clk_prepare_enable(gpio->clk);
+ if (err) {
+ dev_info(&pdev->dev, "Cannot enable clock\n");
+ return err;
+ }
+ }
+
gpio->flags = 0;
if (dev->of_node) {
const struct of_device_id *of_devid;
@@ -689,6 +724,7 @@ static int dwapb_gpio_remove(struct platform_device *pdev)
dwapb_gpio_unregister(gpio);
dwapb_irq_teardown(gpio);
reset_control_assert(gpio->rst);
+ clk_disable_unprepare(gpio->clk);
return 0;
}
@@ -710,13 +746,13 @@ static int dwapb_gpio_suspend(struct device *dev)
BUG_ON(!ctx);
- offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
+ offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
ctx->dir = dwapb_read(gpio, offset);
- offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
+ offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
ctx->data = dwapb_read(gpio, offset);
- offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
+ offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_STRIDE;
ctx->ext = dwapb_read(gpio, offset);
/* Only port A can provide interrupts */
@@ -734,6 +770,8 @@ static int dwapb_gpio_suspend(struct device *dev)
}
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ clk_disable_unprepare(gpio->clk);
+
return 0;
}
@@ -745,6 +783,9 @@ static int dwapb_gpio_resume(struct device *dev)
unsigned long flags;
int i;
+ if (!IS_ERR(gpio->clk))
+ clk_prepare_enable(gpio->clk);
+
spin_lock_irqsave(&gc->bgpio_lock, flags);
for (i = 0; i < gpio->nr_ports; i++) {
unsigned int offset;
@@ -753,13 +794,13 @@ static int dwapb_gpio_resume(struct device *dev)
BUG_ON(!ctx);
- offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
+ offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
dwapb_write(gpio, offset, ctx->data);
- offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
+ offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
dwapb_write(gpio, offset, ctx->dir);
- offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
+ offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_STRIDE;
dwapb_write(gpio, offset, ctx->ext);
/* Only port A can provide interrupts */