diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpio/gpio-xilinx.c | 356 |
1 files changed, 178 insertions, 178 deletions
diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 5828743a9f1d..01e6459b27e6 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -5,6 +5,7 @@ * Copyright 2008 - 2013 Xilinx, Inc. */ +#include <linux/bitmap.h> #include <linux/bitops.h> #include <linux/clk.h> #include <linux/errno.h> @@ -44,40 +45,61 @@ * struct xgpio_instance - Stores information about GPIO device * @gc: GPIO chip * @regs: register block - * @gpio_width: GPIO width for every channel - * @gpio_state: GPIO write state shadow register - * @gpio_last_irq_read: GPIO read state register from last interrupt - * @gpio_dir: GPIO direction shadow register + * @hw_map: GPIO pin mapping on hardware side + * @sw_map: GPIO pin mapping on software side + * @state: GPIO write state shadow register + * @last_irq_read: GPIO read state register from last interrupt + * @dir: GPIO direction shadow register * @gpio_lock: Lock used for synchronization * @irq: IRQ used by GPIO device * @irqchip: IRQ chip - * @irq_enable: GPIO IRQ enable/disable bitfield - * @irq_rising_edge: GPIO IRQ rising edge enable/disable bitfield - * @irq_falling_edge: GPIO IRQ falling edge enable/disable bitfield + * @enable: GPIO IRQ enable/disable bitfield + * @rising_edge: GPIO IRQ rising edge enable/disable bitfield + * @falling_edge: GPIO IRQ falling edge enable/disable bitfield * @clk: clock resource for this driver */ struct xgpio_instance { struct gpio_chip gc; void __iomem *regs; - unsigned int gpio_width[2]; - u32 gpio_state[2]; - u32 gpio_last_irq_read[2]; - u32 gpio_dir[2]; + DECLARE_BITMAP(hw_map, 64); + DECLARE_BITMAP(sw_map, 64); + DECLARE_BITMAP(state, 64); + DECLARE_BITMAP(last_irq_read, 64); + DECLARE_BITMAP(dir, 64); spinlock_t gpio_lock; /* For serializing operations */ int irq; struct irq_chip irqchip; - u32 irq_enable[2]; - u32 irq_rising_edge[2]; - u32 irq_falling_edge[2]; + DECLARE_BITMAP(enable, 64); + DECLARE_BITMAP(rising_edge, 64); + DECLARE_BITMAP(falling_edge, 64); struct clk *clk; }; -static inline int xgpio_index(struct xgpio_instance *chip, int gpio) +static inline int xgpio_from_bit(struct xgpio_instance *chip, int bit) { - if (gpio >= chip->gpio_width[0]) - return 1; + return bitmap_bitremap(bit, chip->hw_map, chip->sw_map, 64); +} - return 0; +static inline int xgpio_to_bit(struct xgpio_instance *chip, int gpio) +{ + return bitmap_bitremap(gpio, chip->sw_map, chip->hw_map, 64); +} + +static inline u32 xgpio_get_value32(const unsigned long *map, int bit) +{ + const size_t index = BIT_WORD(bit); + const unsigned long offset = (bit % BITS_PER_LONG) & BIT(5); + + return (map[index] >> offset) & 0xFFFFFFFFul; +} + +static inline void xgpio_set_value32(unsigned long *map, int bit, u32 v) +{ + const size_t index = BIT_WORD(bit); + const unsigned long offset = (bit % BITS_PER_LONG) & BIT(5); + + map[index] &= ~(0xFFFFFFFFul << offset); + map[index] |= v << offset; } static inline int xgpio_regoffset(struct xgpio_instance *chip, int ch) @@ -92,22 +114,32 @@ static inline int xgpio_regoffset(struct xgpio_instance *chip, int ch) } } -static inline u32 xgpio_read_chan(struct xgpio_instance *chip, int reg, int ch) +static void xgpio_read_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a) { - return xgpio_readreg(chip->regs + reg + xgpio_regoffset(chip, ch)); + void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + xgpio_set_value32(a, bit, xgpio_readreg(addr)); } -static inline void xgpio_write_chan(struct xgpio_instance *chip, int reg, int ch, u32 v) +static void xgpio_write_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a) { - xgpio_writereg(chip->regs + reg + xgpio_regoffset(chip, ch), v); + void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + xgpio_writereg(addr, xgpio_get_value32(a, bit)); } -static inline int xgpio_offset(struct xgpio_instance *chip, int gpio) +static void xgpio_read_ch_all(struct xgpio_instance *chip, int reg, unsigned long *a) { - if (xgpio_index(chip, gpio)) - return gpio - chip->gpio_width[0]; + int bit, lastbit = xgpio_to_bit(chip, chip->gc.ngpio - 1); - return gpio; + for (bit = 0; bit <= lastbit ; bit += 32) + xgpio_read_ch(chip, reg, bit, a); +} + +static void xgpio_write_ch_all(struct xgpio_instance *chip, int reg, unsigned long *a) +{ + int bit, lastbit = xgpio_to_bit(chip, chip->gc.ngpio - 1); + + for (bit = 0; bit <= lastbit ; bit += 32) + xgpio_write_ch(chip, reg, bit, a); } /** @@ -124,13 +156,12 @@ static inline int xgpio_offset(struct xgpio_instance *chip, int gpio) static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) { struct xgpio_instance *chip = gpiochip_get_data(gc); - int index = xgpio_index(chip, gpio); - int offset = xgpio_offset(chip, gpio); - u32 val; + int bit = xgpio_to_bit(chip, gpio); + DECLARE_BITMAP(state, 64); - val = xgpio_read_chan(chip, XGPIO_DATA_OFFSET, index); + xgpio_read_ch(chip, XGPIO_DATA_OFFSET, bit, state); - return !!(val & BIT(offset)); + return test_bit(bit, state); } /** @@ -146,18 +177,14 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long flags; struct xgpio_instance *chip = gpiochip_get_data(gc); - int index = xgpio_index(chip, gpio); - int offset = xgpio_offset(chip, gpio); + int bit = xgpio_to_bit(chip, gpio); spin_lock_irqsave(&chip->gpio_lock, flags); /* Write to GPIO signal and set its direction to output */ - if (val) - chip->gpio_state[index] |= BIT(offset); - else - chip->gpio_state[index] &= ~BIT(offset); + __assign_bit(bit, chip->state, val); - xgpio_write_chan(chip, XGPIO_DATA_OFFSET, index, chip->gpio_state[index]); + xgpio_write_ch(chip, XGPIO_DATA_OFFSET, bit, chip->state); spin_unlock_irqrestore(&chip->gpio_lock, flags); } @@ -174,35 +201,22 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { + DECLARE_BITMAP(hw_mask, 64); + DECLARE_BITMAP(hw_bits, 64); + DECLARE_BITMAP(state, 64); unsigned long flags; struct xgpio_instance *chip = gpiochip_get_data(gc); - int index = xgpio_index(chip, 0); - int offset, i; + + bitmap_remap(hw_mask, mask, chip->sw_map, chip->hw_map, 64); + bitmap_remap(hw_bits, bits, chip->sw_map, chip->hw_map, 64); spin_lock_irqsave(&chip->gpio_lock, flags); - /* Write to GPIO signals */ - for (i = 0; i < gc->ngpio; i++) { - if (*mask == 0) - break; - /* Once finished with an index write it out to the register */ - if (index != xgpio_index(chip, i)) { - xgpio_write_chan(chip, XGPIO_DATA_OFFSET, index, - chip->gpio_state[index]); - spin_unlock_irqrestore(&chip->gpio_lock, flags); - index = xgpio_index(chip, i); - spin_lock_irqsave(&chip->gpio_lock, flags); - } - if (__test_and_clear_bit(i, mask)) { - offset = xgpio_offset(chip, i); - if (test_bit(i, bits)) - chip->gpio_state[index] |= BIT(offset); - else - chip->gpio_state[index] &= ~BIT(offset); - } - } + bitmap_replace(state, chip->state, hw_bits, hw_mask, 64); - xgpio_write_chan(chip, XGPIO_DATA_OFFSET, index, chip->gpio_state[index]); + xgpio_write_ch_all(chip, XGPIO_DATA_OFFSET, state); + + bitmap_copy(chip->state, state, 64); spin_unlock_irqrestore(&chip->gpio_lock, flags); } @@ -220,14 +234,13 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) { unsigned long flags; struct xgpio_instance *chip = gpiochip_get_data(gc); - int index = xgpio_index(chip, gpio); - int offset = xgpio_offset(chip, gpio); + int bit = xgpio_to_bit(chip, gpio); spin_lock_irqsave(&chip->gpio_lock, flags); /* Set the GPIO bit in shadow register and set direction as input */ - chip->gpio_dir[index] |= BIT(offset); - xgpio_write_chan(chip, XGPIO_TRI_OFFSET, index, chip->gpio_dir[index]); + __set_bit(bit, chip->dir); + xgpio_write_ch(chip, XGPIO_TRI_OFFSET, bit, chip->dir); spin_unlock_irqrestore(&chip->gpio_lock, flags); @@ -250,21 +263,17 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long flags; struct xgpio_instance *chip = gpiochip_get_data(gc); - int index = xgpio_index(chip, gpio); - int offset = xgpio_offset(chip, gpio); + int bit = xgpio_to_bit(chip, gpio); spin_lock_irqsave(&chip->gpio_lock, flags); /* Write state of GPIO signal */ - if (val) - chip->gpio_state[index] |= BIT(offset); - else - chip->gpio_state[index] &= ~BIT(offset); - xgpio_write_chan(chip, XGPIO_DATA_OFFSET, index, chip->gpio_state[index]); + __assign_bit(bit, chip->state, val); + xgpio_write_ch(chip, XGPIO_DATA_OFFSET, bit, chip->state); /* Clear the GPIO bit in shadow register and set direction as output */ - chip->gpio_dir[index] &= ~BIT(offset); - xgpio_write_chan(chip, XGPIO_TRI_OFFSET, index, chip->gpio_dir[index]); + __clear_bit(bit, chip->dir); + xgpio_write_ch(chip, XGPIO_TRI_OFFSET, bit, chip->dir); spin_unlock_irqrestore(&chip->gpio_lock, flags); @@ -277,14 +286,8 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) */ static void xgpio_save_regs(struct xgpio_instance *chip) { - xgpio_write_chan(chip, XGPIO_DATA_OFFSET, 0, chip->gpio_state[0]); - xgpio_write_chan(chip, XGPIO_TRI_OFFSET, 0, chip->gpio_dir[0]); - - if (!chip->gpio_width[1]) - return; - - xgpio_write_chan(chip, XGPIO_DATA_OFFSET, 1, chip->gpio_state[1]); - xgpio_write_chan(chip, XGPIO_TRI_OFFSET, 1, chip->gpio_dir[1]); + xgpio_write_ch_all(chip, XGPIO_DATA_OFFSET, chip->state); + xgpio_write_ch_all(chip, XGPIO_TRI_OFFSET, chip->dir); } static int xgpio_request(struct gpio_chip *chip, unsigned int offset) @@ -399,18 +402,17 @@ static void xgpio_irq_mask(struct irq_data *irq_data) unsigned long flags; struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); int irq_offset = irqd_to_hwirq(irq_data); - int index = xgpio_index(chip, irq_offset); - int offset = xgpio_offset(chip, irq_offset); + int bit = xgpio_to_bit(chip, irq_offset); + u32 mask = BIT(bit / 32), temp; spin_lock_irqsave(&chip->gpio_lock, flags); - chip->irq_enable[index] &= ~BIT(offset); + __clear_bit(bit, chip->enable); - if (!chip->irq_enable[index]) { + if (xgpio_get_value32(chip->enable, bit) == 0) { /* Disable per channel interrupt */ - u32 temp = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET); - - temp &= ~BIT(index); + temp = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET); + temp &= ~mask; xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, temp); } spin_unlock_irqrestore(&chip->gpio_lock, flags); @@ -425,29 +427,26 @@ static void xgpio_irq_unmask(struct irq_data *irq_data) unsigned long flags; struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); int irq_offset = irqd_to_hwirq(irq_data); - int index = xgpio_index(chip, irq_offset); - int offset = xgpio_offset(chip, irq_offset); - u32 old_enable = chip->irq_enable[index]; + int bit = xgpio_to_bit(chip, irq_offset); + u32 old_enable = xgpio_get_value32(chip->enable, bit); + u32 mask = BIT(bit / 32), val; spin_lock_irqsave(&chip->gpio_lock, flags); - chip->irq_enable[index] |= BIT(offset); + __set_bit(bit, chip->enable); - if (!old_enable) { + if (old_enable == 0) { /* Clear any existing per-channel interrupts */ - u32 val = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET) & - BIT(index); - - if (val) - xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, val); + val = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET); + val &= mask; + xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, val); /* Update GPIO IRQ read data before enabling interrupt*/ - val = xgpio_read_chan(chip, XGPIO_DATA_OFFSET, index); - chip->gpio_last_irq_read[index] = val; + xgpio_read_ch(chip, XGPIO_DATA_OFFSET, bit, chip->last_irq_read); /* Enable per channel interrupt */ val = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET); - val |= BIT(index); + val |= mask; xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, val); } @@ -466,8 +465,7 @@ static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type) { struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data); int irq_offset = irqd_to_hwirq(irq_data); - int index = xgpio_index(chip, irq_offset); - int offset = xgpio_offset(chip, irq_offset); + int bit = xgpio_to_bit(chip, irq_offset); /* * The Xilinx GPIO hardware provides a single interrupt status @@ -477,16 +475,16 @@ static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type) */ switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_BOTH: - chip->irq_rising_edge[index] |= BIT(offset); - chip->irq_falling_edge[index] |= BIT(offset); + __set_bit(bit, chip->rising_edge); + __set_bit(bit, chip->falling_edge); break; case IRQ_TYPE_EDGE_RISING: - chip->irq_rising_edge[index] |= BIT(offset); - chip->irq_falling_edge[index] &= ~BIT(offset); + __set_bit(bit, chip->rising_edge); + __clear_bit(bit, chip->falling_edge); break; case IRQ_TYPE_EDGE_FALLING: - chip->irq_rising_edge[index] &= ~BIT(offset); - chip->irq_falling_edge[index] |= BIT(offset); + __clear_bit(bit, chip->rising_edge); + __set_bit(bit, chip->falling_edge); break; default: return -EINVAL; @@ -503,45 +501,45 @@ static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type) static void xgpio_irqhandler(struct irq_desc *desc) { struct xgpio_instance *chip = irq_desc_get_handler_data(desc); + struct gpio_chip *gc = &chip->gc; struct irq_chip *irqchip = irq_desc_get_chip(desc); - u32 num_channels = chip->gpio_width[1] ? 2 : 1; - u32 offset = 0, index; - u32 status = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET); + DECLARE_BITMAP(rising, 64); + DECLARE_BITMAP(falling, 64); + DECLARE_BITMAP(all, 64); + int irq_offset; + u32 status; + u32 bit; + unsigned long flags; + status = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET); xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, status); chained_irq_enter(irqchip, desc); - for (index = 0; index < num_channels; index++) { - if ((status & BIT(index))) { - unsigned long rising_events, falling_events, all_events; - unsigned long flags; - u32 data, bit; - unsigned int irq; - - spin_lock_irqsave(&chip->gpio_lock, flags); - data = xgpio_read_chan(chip, XGPIO_DATA_OFFSET, index); - rising_events = data & - ~chip->gpio_last_irq_read[index] & - chip->irq_enable[index] & - chip->irq_rising_edge[index]; - falling_events = ~data & - chip->gpio_last_irq_read[index] & - chip->irq_enable[index] & - chip->irq_falling_edge[index]; - dev_dbg(chip->gc.parent, - "IRQ chan %u rising 0x%lx falling 0x%lx\n", - index, rising_events, falling_events); - all_events = rising_events | falling_events; - chip->gpio_last_irq_read[index] = data; - spin_unlock_irqrestore(&chip->gpio_lock, flags); - - for_each_set_bit(bit, &all_events, 32) { - irq = irq_find_mapping(chip->gc.irq.domain, - offset + bit); - generic_handle_irq(irq); - } - } - offset += chip->gpio_width[index]; + + spin_lock_irqsave(&chip->gpio_lock, flags); + + xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, all); + + bitmap_complement(rising, chip->last_irq_read, 64); + bitmap_and(rising, rising, all, 64); + bitmap_and(rising, rising, chip->enable, 64); + bitmap_and(rising, rising, chip->rising_edge, 64); + + bitmap_complement(falling, all, 64); + bitmap_and(falling, falling, chip->last_irq_read, 64); + bitmap_and(falling, falling, chip->enable, 64); + bitmap_and(falling, falling, chip->falling_edge, 64); + + bitmap_copy(chip->last_irq_read, all, 64); + bitmap_or(all, rising, falling, 64); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + dev_dbg(gc->parent, "IRQ rising %*pb falling %*pb\n", 64, rising, 64, falling); + + for_each_set_bit(bit, all, 64) { + irq_offset = xgpio_from_bit(chip, bit); + generic_handle_irq(irq_find_mapping(gc->irq.domain, irq_offset)); } chained_irq_exit(irqchip, desc); @@ -562,6 +560,9 @@ static int xgpio_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; u32 is_dual = 0; u32 cells = 2; + u32 width[2]; + u32 state[2]; + u32 dir[2]; struct gpio_irq_chip *girq; u32 temp; @@ -571,13 +572,25 @@ static int xgpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); + /* First, check if the device is dual-channel */ + of_property_read_u32(np, "xlnx,is-dual", &is_dual); + + /* Setup defaults */ + memset32(width, 0, ARRAY_SIZE(width)); + memset32(state, 0, ARRAY_SIZE(state)); + memset32(dir, 0xFFFFFFFF, ARRAY_SIZE(dir)); + /* Update GPIO state shadow register with default value */ - if (of_property_read_u32(np, "xlnx,dout-default", &chip->gpio_state[0])) - chip->gpio_state[0] = 0x0; + of_property_read_u32(np, "xlnx,dout-default", &state[0]); + of_property_read_u32(np, "xlnx,dout-default-2", &state[1]); + + bitmap_from_arr32(chip->state, state, 64); /* Update GPIO direction shadow register with default value */ - if (of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir[0])) - chip->gpio_dir[0] = 0xFFFFFFFF; + of_property_read_u32(np, "xlnx,tri-default", &dir[0]); + of_property_read_u32(np, "xlnx,tri-default-2", &dir[1]); + + bitmap_from_arr32(chip->dir, dir, 64); /* Update cells with gpio-cells value */ if (of_property_read_u32(np, "#gpio-cells", &cells)) @@ -592,42 +605,29 @@ static int xgpio_probe(struct platform_device *pdev) * Check device node and parent device node for device width * and assume default width of 32 */ - if (of_property_read_u32(np, "xlnx,gpio-width", &chip->gpio_width[0])) - chip->gpio_width[0] = 32; + if (of_property_read_u32(np, "xlnx,gpio-width", &width[0])) + width[0] = 32; - if (chip->gpio_width[0] > 32) + if (width[0] > 32) return -EINVAL; - spin_lock_init(&chip->gpio_lock); + if (is_dual && of_property_read_u32(np, "xlnx,gpio2-width", &width[1])) + width[1] = 32; - if (of_property_read_u32(np, "xlnx,is-dual", &is_dual)) - is_dual = 0; - - if (is_dual) { - /* Update GPIO state shadow register with default value */ - if (of_property_read_u32(np, "xlnx,dout-default-2", - &chip->gpio_state[1])) - chip->gpio_state[1] = 0x0; - - /* Update GPIO direction shadow register with default value */ - if (of_property_read_u32(np, "xlnx,tri-default-2", - &chip->gpio_dir[1])) - chip->gpio_dir[1] = 0xFFFFFFFF; - - /* - * Check device node and parent device node for device width - * and assume default width of 32 - */ - if (of_property_read_u32(np, "xlnx,gpio2-width", - &chip->gpio_width[1])) - chip->gpio_width[1] = 32; - - if (chip->gpio_width[1] > 32) - return -EINVAL; - } + if (width[1] > 32) + return -EINVAL; + + /* Setup software pin mapping */ + bitmap_set(chip->sw_map, 0, width[0] + width[1]); + + /* Setup hardware pin mapping */ + bitmap_set(chip->hw_map, 0, width[0]); + bitmap_set(chip->hw_map, 32, width[1]); + + spin_lock_init(&chip->gpio_lock); chip->gc.base = -1; - chip->gc.ngpio = chip->gpio_width[0] + chip->gpio_width[1]; + chip->gc.ngpio = bitmap_weight(chip->hw_map, 64); chip->gc.parent = &pdev->dev; chip->gc.direction_input = xgpio_dir_in; chip->gc.direction_output = xgpio_dir_out; |