Commit 02b3f84d authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Bartosz Golaszewski

gpio: xilinx: Switch to use bitmap APIs

It seems that Xilinx GPIO driver operates with bit arrays longer than 32 and
thus can leverage bitmap APIs for that. It makes code better to understand.

The ->probe() function is modified to try read properties for both channels
since is_dual check makes only sense for the amount of pins used for the second
channel. On top of that kzalloc() guarantees zero initial values for the fields
in the private data structure, hence drop unneeded conditionals and assignments.

The change is inspired by Syed Nayyar Waris' ideas about bitmap API extension.
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Tested-by: default avatarNeeli Srinivas <sneeli@xilinx.com>
Signed-off-by: default avatarBartosz Golaszewski <bgolaszewski@baylibre.com>
parent 043aa3db
......@@ -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;
spin_lock_irqsave(&chip->gpio_lock, flags);
bitmap_remap(hw_mask, mask, chip->sw_map, chip->hw_map, 64);
bitmap_remap(hw_bits, bits, chip->sw_map, chip->hw_map, 64);
/* 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);
}
}
xgpio_write_chan(chip, XGPIO_DATA_OFFSET, index, chip->gpio_state[index]);
bitmap_replace(state, chip->state, hw_bits, hw_mask, 64);
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)
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;
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);
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];
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 (width[1] > 32)
return -EINVAL;
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;
/* Setup software pin mapping */
bitmap_set(chip->sw_map, 0, width[0] + width[1]);
/* 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;
/* Setup hardware pin mapping */
bitmap_set(chip->hw_map, 0, width[0]);
bitmap_set(chip->hw_map, 32, width[1]);
/*
* 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;
}
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment