Commit 0bf0dfda authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull GPIO fixes from Linus Walleij:
 "Some late GPIO fixes for the v5.9 series:

   - Fix compiler warnings on the OMAP when PM is disabled

   - Clear the interrupt when setting edge sensitivity on the Spreadtrum
     driver.

   - Fix up spurious interrupts on the TC35894.

   - Support threaded interrupts on the Siox controller.

   - Fix resource leaks on the mockup driver.

   - Fix line event handling in syscall compatible mode for the
     character device.

   - Fix an unitialized variable in the PCA953A driver.

   - Fix access to all GPIO IRQs on the Aspeed AST2600.

   - Fix line direction on the AMD FCH driver.

   - Use the bitmap API instead of compiler intrinsics for bit
     manipulation in the PCA953x driver"

* tag 'gpio-v5.9-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio:
  gpio: pca953x: Correctly initialize registers 6 and 7 for PCA957x
  gpio: pca953x: Use bitmap API over implicit GCC extension
  gpio: amd-fch: correct logic of GPIO_LINE_DIRECTION
  gpio: aspeed: fix ast2600 bank properties
  gpio/aspeed-sgpio: don't enable all interrupts by default
  gpio/aspeed-sgpio: enable access to all 80 input & output sgpios
  gpio: pca953x: Fix uninitialized pending variable
  gpiolib: Fix line event handling in syscall compatible mode
  gpio: mockup: fix resource leak in error path
  gpio: siox: explicitly support only threaded irqs
  gpio: tc35894: fix up tc35894 interrupt configuration
  gpio: sprd: Clear interrupt when setting the type as edge
  gpio: omap: Fix warnings if PM is disabled
parents 2270b890 8c1f1c34
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -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

+1 −1
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@ static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
	ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION);
	spin_unlock_irqrestore(&priv->lock, flags);

	return ret ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
	return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
}

static void amd_fch_gpio_set(struct gpio_chip *gc,
+87 −47
Original line number Diff line number Diff line
@@ -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

@@ -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 {
@@ -111,31 +121,69 @@ 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);
@@ -143,22 +191,31 @@ static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
	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)
@@ -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,
@@ -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;
@@ -409,17 +452,15 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
	irq->parents = &gpio->irq;
	irq->num_parents = 1;

	/* set IRQ settings and Enable Interrupt */
	/* Apply default IRQ settings */
	for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
		bank = &aspeed_sgpio_banks[i];
		/* set falling or level-low irq */
		iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type0));
		/* trigger type is edge */
		iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type1));
		/* dual edge trigger mode. */
		iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type2));
		/* enable irq */
		iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_enable));
		/* single edge trigger */
		iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type2));
	}

	return 0;
@@ -452,11 +493,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) {
@@ -497,7 +539,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;
@@ -509,9 +552,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);
+2 −2
Original line number Diff line number Diff line
@@ -1114,8 +1114,8 @@ static const struct aspeed_gpio_config ast2500_config =

static const struct aspeed_bank_props ast2600_bank_props[] = {
	/*     input	  output   */
	{5, 0xffffffff,  0x0000ffff}, /* U/V/W/X */
	{6, 0xffff0000,  0x0fff0000}, /* Y/Z */
	{5, 0xffffffff,  0xffffff00}, /* U/V/W/X */
	{6, 0x0000ffff,  0x0000ffff}, /* Y/Z */
	{ },
};

+2 −0
Original line number Diff line number Diff line
@@ -552,6 +552,7 @@ static int __init gpio_mockup_init(void)
	err = platform_driver_register(&gpio_mockup_driver);
	if (err) {
		gpio_mockup_err("error registering platform driver\n");
		debugfs_remove_recursive(gpio_mockup_dbg_dir);
		return err;
	}

@@ -582,6 +583,7 @@ static int __init gpio_mockup_init(void)
			gpio_mockup_err("error registering device");
			platform_driver_unregister(&gpio_mockup_driver);
			gpio_mockup_unregister_pdevs();
			debugfs_remove_recursive(gpio_mockup_dbg_dir);
			return PTR_ERR(pdev);
		}

Loading