Skip to content

Commit

Permalink
gpio: ws16c48: Implement and utilize register structures
Browse files Browse the repository at this point in the history
Reduce magic numbers and improve code readability by implementing and
utilizing named register data structures.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Cc: Paul Demetrotion <pdemetrotion@winsystems.com>
Signed-off-by: William Breathitt Gray <william.gray@linaro.org>
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
  • Loading branch information
William Breathitt Gray authored and Bartosz Golaszewski committed Jul 20, 2022
1 parent c4371c5 commit 2c05a0f
Showing 1 changed file with 84 additions and 36 deletions.
120 changes: 84 additions & 36 deletions drivers/gpio/gpio-ws16c48.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* Copyright (C) 2016 William Breathitt Gray
*/
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
Expand All @@ -17,8 +16,9 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/types.h>

#define WS16C48_EXTENT 16
#define WS16C48_EXTENT 10
#define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT)

static unsigned int base[MAX_NUM_WS16C48];
Expand All @@ -30,6 +30,20 @@ static unsigned int irq[MAX_NUM_WS16C48];
module_param_hw_array(irq, uint, irq, NULL, 0);
MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers");

/**
* struct ws16c48_reg - device register structure
* @port: Port 0 through 5 I/O
* @int_pending: Interrupt Pending
* @page_lock: Register page (Bits 7-6) and I/O port lock (Bits 5-0)
* @pol_enab_int_id: Interrupt polarity, enable, and ID
*/
struct ws16c48_reg {
u8 port[6];
u8 int_pending;
u8 page_lock;
u8 pol_enab_int_id[3];
};

/**
* struct ws16c48_gpio - GPIO device private data structure
* @chip: instance of the gpio_chip
Expand All @@ -38,7 +52,7 @@ MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers");
* @lock: synchronization lock to prevent I/O race conditions
* @irq_mask: I/O bits affected by interrupts
* @flow_mask: IRQ flow type mask for the respective I/O bits
* @base: base port address of the GPIO device
* @reg: I/O address offset for the device registers
*/
struct ws16c48_gpio {
struct gpio_chip chip;
Expand All @@ -47,7 +61,7 @@ struct ws16c48_gpio {
raw_spinlock_t lock;
unsigned long irq_mask;
unsigned long flow_mask;
void __iomem *base;
struct ws16c48_reg __iomem *reg;
};

static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
Expand All @@ -73,7 +87,7 @@ static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)

ws16c48gpio->io_state[port] |= mask;
ws16c48gpio->out_state[port] &= ~mask;
iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);

raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);

Expand All @@ -95,7 +109,7 @@ static int ws16c48_gpio_direction_output(struct gpio_chip *chip,
ws16c48gpio->out_state[port] |= mask;
else
ws16c48gpio->out_state[port] &= ~mask;
iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);

raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);

Expand All @@ -118,7 +132,7 @@ static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
return -EINVAL;
}

port_state = ioread8(ws16c48gpio->base + port);
port_state = ioread8(ws16c48gpio->reg->port + port);

raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);

Expand All @@ -131,14 +145,16 @@ static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
unsigned long offset;
unsigned long gpio_mask;
void __iomem *port_addr;
size_t index;
u8 __iomem *port_addr;
unsigned long port_state;

/* clear bits array to a clean slate */
bitmap_zero(bits, chip->ngpio);

for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
port_addr = ws16c48gpio->base + offset / 8;
index = offset / 8;
port_addr = ws16c48gpio->reg->port + index;
port_state = ioread8(port_addr) & gpio_mask;

bitmap_set_value8(bits, port_state, offset);
Expand Down Expand Up @@ -166,7 +182,7 @@ static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
ws16c48gpio->out_state[port] |= mask;
else
ws16c48gpio->out_state[port] &= ~mask;
iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);

raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
Expand All @@ -178,13 +194,13 @@ static void ws16c48_gpio_set_multiple(struct gpio_chip *chip,
unsigned long offset;
unsigned long gpio_mask;
size_t index;
void __iomem *port_addr;
u8 __iomem *port_addr;
unsigned long bitmask;
unsigned long flags;

for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
index = offset / 8;
port_addr = ws16c48gpio->base + index;
port_addr = ws16c48gpio->reg->port + index;

/* mask out GPIO configured for input */
gpio_mask &= ~ws16c48gpio->io_state[index];
Expand Down Expand Up @@ -219,10 +235,15 @@ static void ws16c48_irq_ack(struct irq_data *data)

port_state = ws16c48gpio->irq_mask >> (8*port);

iowrite8(0x80, ws16c48gpio->base + 7);
iowrite8(port_state & ~mask, ws16c48gpio->base + 8 + port);
iowrite8(port_state | mask, ws16c48gpio->base + 8 + port);
iowrite8(0xC0, ws16c48gpio->base + 7);
/* Select Register Page 2; Unlock all I/O ports */
iowrite8(0x80, &ws16c48gpio->reg->page_lock);

/* Clear pending interrupt */
iowrite8(port_state & ~mask, ws16c48gpio->reg->pol_enab_int_id + port);
iowrite8(port_state | mask, ws16c48gpio->reg->pol_enab_int_id + port);

/* Select Register Page 3; Unlock all I/O ports */
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);

raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
Expand All @@ -235,6 +256,7 @@ static void ws16c48_irq_mask(struct irq_data *data)
const unsigned long mask = BIT(offset);
const unsigned port = offset / 8;
unsigned long flags;
unsigned long port_state;

/* only the first 3 ports support interrupts */
if (port > 2)
Expand All @@ -243,10 +265,16 @@ static void ws16c48_irq_mask(struct irq_data *data)
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);

ws16c48gpio->irq_mask &= ~mask;
port_state = ws16c48gpio->irq_mask >> (8 * port);

/* Select Register Page 2; Unlock all I/O ports */
iowrite8(0x80, &ws16c48gpio->reg->page_lock);

iowrite8(0x80, ws16c48gpio->base + 7);
iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
iowrite8(0xC0, ws16c48gpio->base + 7);
/* Disable interrupt */
iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);

/* Select Register Page 3; Unlock all I/O ports */
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);

raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
Expand All @@ -259,6 +287,7 @@ static void ws16c48_irq_unmask(struct irq_data *data)
const unsigned long mask = BIT(offset);
const unsigned port = offset / 8;
unsigned long flags;
unsigned long port_state;

/* only the first 3 ports support interrupts */
if (port > 2)
Expand All @@ -267,10 +296,16 @@ static void ws16c48_irq_unmask(struct irq_data *data)
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);

ws16c48gpio->irq_mask |= mask;
port_state = ws16c48gpio->irq_mask >> (8 * port);

/* Select Register Page 2; Unlock all I/O ports */
iowrite8(0x80, &ws16c48gpio->reg->page_lock);

iowrite8(0x80, ws16c48gpio->base + 7);
iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
iowrite8(0xC0, ws16c48gpio->base + 7);
/* Enable interrupt */
iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);

/* Select Register Page 3; Unlock all I/O ports */
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);

raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
Expand All @@ -283,6 +318,7 @@ static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
const unsigned long mask = BIT(offset);
const unsigned port = offset / 8;
unsigned long flags;
unsigned long port_state;

/* only the first 3 ports support interrupts */
if (port > 2)
Expand All @@ -304,9 +340,16 @@ static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
return -EINVAL;
}

iowrite8(0x40, ws16c48gpio->base + 7);
iowrite8(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port);
iowrite8(0xC0, ws16c48gpio->base + 7);
port_state = ws16c48gpio->flow_mask >> (8 * port);

/* Select Register Page 1; Unlock all I/O ports */
iowrite8(0x40, &ws16c48gpio->reg->page_lock);

/* Set interrupt polarity */
iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);

/* Select Register Page 3; Unlock all I/O ports */
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);

raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);

Expand All @@ -325,25 +368,26 @@ static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
{
struct ws16c48_gpio *const ws16c48gpio = dev_id;
struct gpio_chip *const chip = &ws16c48gpio->chip;
struct ws16c48_reg __iomem *const reg = ws16c48gpio->reg;
unsigned long int_pending;
unsigned long port;
unsigned long int_id;
unsigned long gpio;

int_pending = ioread8(ws16c48gpio->base + 6) & 0x7;
int_pending = ioread8(&reg->int_pending) & 0x7;
if (!int_pending)
return IRQ_NONE;

/* loop until all pending interrupts are handled */
do {
for_each_set_bit(port, &int_pending, 3) {
int_id = ioread8(ws16c48gpio->base + 8 + port);
int_id = ioread8(reg->pol_enab_int_id + port);
for_each_set_bit(gpio, &int_id, 8)
generic_handle_domain_irq(chip->irq.domain,
gpio + 8*port);
}

int_pending = ioread8(ws16c48gpio->base + 6) & 0x7;
int_pending = ioread8(&reg->int_pending) & 0x7;
} while (int_pending);

return IRQ_HANDLED;
Expand All @@ -369,12 +413,16 @@ static int ws16c48_irq_init_hw(struct gpio_chip *gc)
{
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc);

/* Disable IRQ by default */
iowrite8(0x80, ws16c48gpio->base + 7);
iowrite8(0, ws16c48gpio->base + 8);
iowrite8(0, ws16c48gpio->base + 9);
iowrite8(0, ws16c48gpio->base + 10);
iowrite8(0xC0, ws16c48gpio->base + 7);
/* Select Register Page 2; Unlock all I/O ports */
iowrite8(0x80, &ws16c48gpio->reg->page_lock);

/* Disable interrupts for all lines */
iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[0]);
iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[1]);
iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[2]);

/* Select Register Page 3; Unlock all I/O ports */
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);

return 0;
}
Expand All @@ -396,8 +444,8 @@ static int ws16c48_probe(struct device *dev, unsigned int id)
return -EBUSY;
}

ws16c48gpio->base = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
if (!ws16c48gpio->base)
ws16c48gpio->reg = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
if (!ws16c48gpio->reg)
return -ENOMEM;

ws16c48gpio->chip.label = name;
Expand Down

0 comments on commit 2c05a0f

Please sign in to comment.