Skip to content

Commit

Permalink
gpio/aspeed-sgpio: enable access to all 80 input & output sgpios
Browse files Browse the repository at this point in the history
[ Upstream commit ac67b07 ]

Currently, the aspeed-sgpio driver exposes up to 80 GPIO lines,
corresponding to the 80 status bits available in hardware. Each of these
lines can be configured as either an input or an output.

However, each of these GPIOs is actually an input *and* an output; we
actually have 80 inputs plus 80 outputs.

This change expands the maximum number of GPIOs to 160; the lower half
of this range are the input-only GPIOs, the upper half are the outputs.
We fix the GPIO directions to correspond to this mapping.

This also fixes a bug when setting GPIOs - we were reading from the
input register, making it impossible to set more than one output GPIO.

Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
Fixes: 7db47fa ("gpio: aspeed: Add SGPIO driver")
Reviewed-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
jk-ozlabs authored and gregkh committed Oct 7, 2020
1 parent eddeff7 commit 8323d1e
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 44 deletions.
5 changes: 3 additions & 2 deletions Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ Required properties:
- gpio-controller : Marks the device node as a GPIO controller
- interrupts : Interrupt specifier, see interrupt-controller/interrupts.txt
- interrupt-controller : Mark the GPIO controller as an interrupt-controller
- ngpios : number of GPIO lines, see gpio.txt
(should be multiple of 8, up to 80 pins)
- ngpios : number of *hardware* GPIO lines, see gpio.txt. This will expose
2 software GPIOs per hardware GPIO: one for hardware input, one for hardware
output. Up to 80 pins, must be a multiple of 8.
- clocks : A phandle to the APB clock for SGPM clock division
- bus-frequency : SGPM CLK frequency

Expand Down
126 changes: 84 additions & 42 deletions drivers/gpio/sgpio-aspeed.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,17 @@
#include <linux/spinlock.h>
#include <linux/string.h>

#define MAX_NR_SGPIO 80
/*
* MAX_NR_HW_GPIO represents the number of actual hardware-supported GPIOs (ie,
* slots within the clocked serial GPIO data). Since each HW GPIO is both an
* input and an output, we provide MAX_NR_HW_GPIO * 2 lines on our gpiochip
* device.
*
* We use SGPIO_OUTPUT_OFFSET to define the split between the inputs and
* outputs; the inputs start at line 0, the outputs start at OUTPUT_OFFSET.
*/
#define MAX_NR_HW_SGPIO 80
#define SGPIO_OUTPUT_OFFSET MAX_NR_HW_SGPIO

#define ASPEED_SGPIO_CTRL 0x54

Expand All @@ -30,8 +40,8 @@ struct aspeed_sgpio {
struct clk *pclk;
spinlock_t lock;
void __iomem *base;
uint32_t dir_in[3];
int irq;
int n_sgpio;
};

struct aspeed_sgpio_bank {
Expand Down Expand Up @@ -111,54 +121,101 @@ static void __iomem *bank_reg(struct aspeed_sgpio *gpio,
}
}

#define GPIO_BANK(x) ((x) >> 5)
#define GPIO_OFFSET(x) ((x) & 0x1f)
#define GPIO_BANK(x) ((x % SGPIO_OUTPUT_OFFSET) >> 5)
#define GPIO_OFFSET(x) ((x % SGPIO_OUTPUT_OFFSET) & 0x1f)
#define GPIO_BIT(x) BIT(GPIO_OFFSET(x))

static const struct aspeed_sgpio_bank *to_bank(unsigned int offset)
{
unsigned int bank = GPIO_BANK(offset);
unsigned int bank;

bank = GPIO_BANK(offset);

WARN_ON(bank >= ARRAY_SIZE(aspeed_sgpio_banks));
return &aspeed_sgpio_banks[bank];
}

static int aspeed_sgpio_init_valid_mask(struct gpio_chip *gc,
unsigned long *valid_mask, unsigned int ngpios)
{
struct aspeed_sgpio *sgpio = gpiochip_get_data(gc);
int n = sgpio->n_sgpio;
int c = SGPIO_OUTPUT_OFFSET - n;

WARN_ON(ngpios < MAX_NR_HW_SGPIO * 2);

/* input GPIOs in the lower range */
bitmap_set(valid_mask, 0, n);
bitmap_clear(valid_mask, n, c);

/* output GPIOS above SGPIO_OUTPUT_OFFSET */
bitmap_set(valid_mask, SGPIO_OUTPUT_OFFSET, n);
bitmap_clear(valid_mask, SGPIO_OUTPUT_OFFSET + n, c);

return 0;
}

static void aspeed_sgpio_irq_init_valid_mask(struct gpio_chip *gc,
unsigned long *valid_mask, unsigned int ngpios)
{
struct aspeed_sgpio *sgpio = gpiochip_get_data(gc);
int n = sgpio->n_sgpio;

WARN_ON(ngpios < MAX_NR_HW_SGPIO * 2);

/* input GPIOs in the lower range */
bitmap_set(valid_mask, 0, n);
bitmap_clear(valid_mask, n, ngpios - n);
}

static bool aspeed_sgpio_is_input(unsigned int offset)
{
return offset < SGPIO_OUTPUT_OFFSET;
}

static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
{
struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
const struct aspeed_sgpio_bank *bank = to_bank(offset);
unsigned long flags;
enum aspeed_sgpio_reg reg;
bool is_input;
int rc = 0;

spin_lock_irqsave(&gpio->lock, flags);

is_input = gpio->dir_in[GPIO_BANK(offset)] & GPIO_BIT(offset);
reg = is_input ? reg_val : reg_rdata;
reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata;
rc = !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset));

spin_unlock_irqrestore(&gpio->lock, flags);

return rc;
}

static void sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val)
static int sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val)
{
struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
const struct aspeed_sgpio_bank *bank = to_bank(offset);
void __iomem *addr;
void __iomem *addr_r, *addr_w;
u32 reg = 0;

addr = bank_reg(gpio, bank, reg_val);
reg = ioread32(addr);
if (aspeed_sgpio_is_input(offset))
return -EINVAL;

/* Since this is an output, read the cached value from rdata, then
* update val. */
addr_r = bank_reg(gpio, bank, reg_rdata);
addr_w = bank_reg(gpio, bank, reg_val);

reg = ioread32(addr_r);

if (val)
reg |= GPIO_BIT(offset);
else
reg &= ~GPIO_BIT(offset);

iowrite32(reg, addr);
iowrite32(reg, addr_w);

return 0;
}

static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val)
Expand All @@ -175,43 +232,28 @@ static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val)

static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset)
{
struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
unsigned long flags;

spin_lock_irqsave(&gpio->lock, flags);
gpio->dir_in[GPIO_BANK(offset)] |= GPIO_BIT(offset);
spin_unlock_irqrestore(&gpio->lock, flags);

return 0;
return aspeed_sgpio_is_input(offset) ? 0 : -EINVAL;
}

static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val)
{
struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
unsigned long flags;
int rc;

spin_lock_irqsave(&gpio->lock, flags);

gpio->dir_in[GPIO_BANK(offset)] &= ~GPIO_BIT(offset);
sgpio_set_value(gc, offset, val);
/* No special action is required for setting the direction; we'll
* error-out in sgpio_set_value if this isn't an output GPIO */

spin_lock_irqsave(&gpio->lock, flags);
rc = sgpio_set_value(gc, offset, val);
spin_unlock_irqrestore(&gpio->lock, flags);

return 0;
return rc;
}

static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset)
{
int dir_status;
struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
unsigned long flags;

spin_lock_irqsave(&gpio->lock, flags);
dir_status = gpio->dir_in[GPIO_BANK(offset)] & GPIO_BIT(offset);
spin_unlock_irqrestore(&gpio->lock, flags);

return dir_status;

return !!aspeed_sgpio_is_input(offset);
}

static void irqd_to_aspeed_sgpio_data(struct irq_data *d,
Expand Down Expand Up @@ -402,6 +444,7 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,

irq = &gpio->chip.irq;
irq->chip = &aspeed_sgpio_irqchip;
irq->init_valid_mask = aspeed_sgpio_irq_init_valid_mask;
irq->handler = handle_bad_irq;
irq->default_type = IRQ_TYPE_NONE;
irq->parent_handler = aspeed_sgpio_irq_handler;
Expand Down Expand Up @@ -452,11 +495,12 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
if (rc < 0) {
dev_err(&pdev->dev, "Could not read ngpios property\n");
return -EINVAL;
} else if (nr_gpios > MAX_NR_SGPIO) {
} else if (nr_gpios > MAX_NR_HW_SGPIO) {
dev_err(&pdev->dev, "Number of GPIOs exceeds the maximum of %d: %d\n",
MAX_NR_SGPIO, nr_gpios);
MAX_NR_HW_SGPIO, nr_gpios);
return -EINVAL;
}
gpio->n_sgpio = nr_gpios;

rc = of_property_read_u32(pdev->dev.of_node, "bus-frequency", &sgpio_freq);
if (rc < 0) {
Expand Down Expand Up @@ -497,7 +541,8 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
spin_lock_init(&gpio->lock);

gpio->chip.parent = &pdev->dev;
gpio->chip.ngpio = nr_gpios;
gpio->chip.ngpio = MAX_NR_HW_SGPIO * 2;
gpio->chip.init_valid_mask = aspeed_sgpio_init_valid_mask;
gpio->chip.direction_input = aspeed_sgpio_dir_in;
gpio->chip.direction_output = aspeed_sgpio_dir_out;
gpio->chip.get_direction = aspeed_sgpio_get_direction;
Expand All @@ -509,9 +554,6 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
gpio->chip.label = dev_name(&pdev->dev);
gpio->chip.base = -1;

/* set all SGPIO pins as input (1). */
memset(gpio->dir_in, 0xff, sizeof(gpio->dir_in));

aspeed_sgpio_setup_irqs(gpio, pdev);

rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
Expand Down

0 comments on commit 8323d1e

Please sign in to comment.