diff options
Diffstat (limited to 'drivers/gpio/gpio-realtek-otto.c')
| -rw-r--r-- | drivers/gpio/gpio-realtek-otto.c | 166 | 
1 files changed, 85 insertions, 81 deletions
| diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c index 63dcf42f7c20..d6418f89d3f6 100644 --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c @@ -46,10 +46,20 @@   * @lock: Lock for accessing the IRQ registers and values   * @intr_mask: Mask for interrupts lines   * @intr_type: Interrupt type selection + * @bank_read: Read a bank setting as a single 32-bit value + * @bank_write: Write a bank setting as a single 32-bit value + * @imr_line_pos: Bit shift of an IRQ line's IMR value. + * + * The DIR, DATA, and ISR registers consist of four 8-bit port values, packed + * into a single 32-bit register. Use @bank_read (@bank_write) to get (assign) + * a value from (to) these registers. The IMR register consists of four 16-bit + * port values, packed into two 32-bit registers. Use @imr_line_pos to get the + * bit shift of the 2-bit field for a line's IMR settings. Shifts larger than + * 32 overflow into the second register.   *   * Because the interrupt mask register (IMR) combines the function of IRQ type   * selection and masking, two extra values are stored. @intr_mask is used to - * mask/unmask the interrupts for a GPIO port, and @intr_type is used to store + * mask/unmask the interrupts for a GPIO line, and @intr_type is used to store   * the selected interrupt types. The logical AND of these values is written to   * IMR on changes.   */ @@ -59,10 +69,11 @@ struct realtek_gpio_ctrl {  	void __iomem *cpumask_base;  	struct cpumask cpu_irq_maskable;  	raw_spinlock_t lock; -	u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK]; -	u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK]; -	unsigned int (*port_offset_u8)(unsigned int port); -	unsigned int (*port_offset_u16)(unsigned int port); +	u8 intr_mask[REALTEK_GPIO_MAX]; +	u8 intr_type[REALTEK_GPIO_MAX]; +	u32 (*bank_read)(void __iomem *reg); +	void (*bank_write)(void __iomem *reg, u32 value); +	unsigned int (*line_imr_pos)(unsigned int line);  };  /* Expand with more flags as devices with other quirks are added */ @@ -101,14 +112,22 @@ static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data)   * port. The two interrupt mask registers store two bits per GPIO, so use u16   * values.   */ -static unsigned int realtek_gpio_port_offset_u8(unsigned int port) +static u32 realtek_gpio_bank_read_swapped(void __iomem *reg)  { -	return port; +	return ioread32be(reg);  } -static unsigned int realtek_gpio_port_offset_u16(unsigned int port) +static void realtek_gpio_bank_write_swapped(void __iomem *reg, u32 value)  { -	return 2 * port; +	iowrite32be(value, reg); +} + +static unsigned int realtek_gpio_line_imr_pos_swapped(unsigned int line) +{ +	unsigned int port_pin = line % 8; +	unsigned int port = line / 8; + +	return 2 * (8 * (port ^ 1) + port_pin);  }  /* @@ -119,66 +138,67 @@ static unsigned int realtek_gpio_port_offset_u16(unsigned int port)   * per GPIO, so use u16 values. The first register contains ports 1 and 0, the   * second ports 3 and 2.   */ -static unsigned int realtek_gpio_port_offset_u8_rev(unsigned int port) +static u32 realtek_gpio_bank_read(void __iomem *reg)  { -	return 3 - port; +	return ioread32(reg);  } -static unsigned int realtek_gpio_port_offset_u16_rev(unsigned int port) +static void realtek_gpio_bank_write(void __iomem *reg, u32 value)  { -	return 2 * (port ^ 1); +	iowrite32(value, reg);  } -static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl, -	unsigned int port, u16 irq_type, u16 irq_mask) +static unsigned int realtek_gpio_line_imr_pos(unsigned int line)  { -	iowrite16(irq_type & irq_mask, -		ctrl->base + REALTEK_GPIO_REG_IMR + ctrl->port_offset_u16(port)); +	return 2 * line;  } -static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl, -	unsigned int port, u8 mask) +static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl, u32 mask)  { -	iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port)); +	ctrl->bank_write(ctrl->base + REALTEK_GPIO_REG_ISR, mask);  } -static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port) +static u32 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl)  { -	return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port)); +	return ctrl->bank_read(ctrl->base + REALTEK_GPIO_REG_ISR);  } -/* Set the rising and falling edge mask bits for a GPIO port pin */ -static u16 realtek_gpio_imr_bits(unsigned int pin, u16 value) +/* Set the rising and falling edge mask bits for a GPIO pin */ +static void realtek_gpio_update_line_imr(struct realtek_gpio_ctrl *ctrl, unsigned int line)  { -	return (value & REALTEK_GPIO_IMR_LINE_MASK) << 2 * pin; +	void __iomem *reg = ctrl->base + REALTEK_GPIO_REG_IMR; +	unsigned int line_shift = ctrl->line_imr_pos(line); +	unsigned int shift = line_shift % 32; +	u32 irq_type = ctrl->intr_type[line]; +	u32 irq_mask = ctrl->intr_mask[line]; +	u32 reg_val; + +	reg += 4 * (line_shift / 32); +	reg_val = ioread32(reg); +	reg_val &= ~(REALTEK_GPIO_IMR_LINE_MASK << shift); +	reg_val |= (irq_type & irq_mask & REALTEK_GPIO_IMR_LINE_MASK) << shift; +	iowrite32(reg_val, reg);  }  static void realtek_gpio_irq_ack(struct irq_data *data)  {  	struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);  	irq_hw_number_t line = irqd_to_hwirq(data); -	unsigned int port = line / 8; -	unsigned int port_pin = line % 8; -	realtek_gpio_clear_isr(ctrl, port, BIT(port_pin)); +	realtek_gpio_clear_isr(ctrl, BIT(line));  }  static void realtek_gpio_irq_unmask(struct irq_data *data)  {  	struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);  	unsigned int line = irqd_to_hwirq(data); -	unsigned int port = line / 8; -	unsigned int port_pin = line % 8;  	unsigned long flags; -	u16 m;  	gpiochip_enable_irq(&ctrl->gc, line);  	raw_spin_lock_irqsave(&ctrl->lock, flags); -	m = ctrl->intr_mask[port]; -	m |= realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); -	ctrl->intr_mask[port] = m; -	realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m); +	ctrl->intr_mask[line] = REALTEK_GPIO_IMR_LINE_MASK; +	realtek_gpio_update_line_imr(ctrl, line);  	raw_spin_unlock_irqrestore(&ctrl->lock, flags);  } @@ -186,16 +206,11 @@ static void realtek_gpio_irq_mask(struct irq_data *data)  {  	struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);  	unsigned int line = irqd_to_hwirq(data); -	unsigned int port = line / 8; -	unsigned int port_pin = line % 8;  	unsigned long flags; -	u16 m;  	raw_spin_lock_irqsave(&ctrl->lock, flags); -	m = ctrl->intr_mask[port]; -	m &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); -	ctrl->intr_mask[port] = m; -	realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m); +	ctrl->intr_mask[line] = 0; +	realtek_gpio_update_line_imr(ctrl, line);  	raw_spin_unlock_irqrestore(&ctrl->lock, flags);  	gpiochip_disable_irq(&ctrl->gc, line); @@ -205,10 +220,8 @@ static int realtek_gpio_irq_set_type(struct irq_data *data, unsigned int flow_ty  {  	struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);  	unsigned int line = irqd_to_hwirq(data); -	unsigned int port = line / 8; -	unsigned int port_pin = line % 8;  	unsigned long flags; -	u16 type, t; +	u8 type;  	switch (flow_type & IRQ_TYPE_SENSE_MASK) {  	case IRQ_TYPE_EDGE_FALLING: @@ -227,11 +240,8 @@ static int realtek_gpio_irq_set_type(struct irq_data *data, unsigned int flow_ty  	irq_set_handler_locked(data, handle_edge_irq);  	raw_spin_lock_irqsave(&ctrl->lock, flags); -	t = ctrl->intr_type[port]; -	t &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); -	t |= realtek_gpio_imr_bits(port_pin, type); -	ctrl->intr_type[port] = t; -	realtek_gpio_write_imr(ctrl, port, t, ctrl->intr_mask[port]); +	ctrl->intr_type[line] = type; +	realtek_gpio_update_line_imr(ctrl, line);  	raw_spin_unlock_irqrestore(&ctrl->lock, flags);  	return 0; @@ -242,28 +252,21 @@ static void realtek_gpio_irq_handler(struct irq_desc *desc)  	struct gpio_chip *gc = irq_desc_get_handler_data(desc);  	struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);  	struct irq_chip *irq_chip = irq_desc_get_chip(desc); -	unsigned int lines_done; -	unsigned int port_pin_count;  	unsigned long status;  	int offset;  	chained_irq_enter(irq_chip, desc); -	for (lines_done = 0; lines_done < gc->ngpio; lines_done += 8) { -		status = realtek_gpio_read_isr(ctrl, lines_done / 8); -		port_pin_count = min(gc->ngpio - lines_done, 8U); -		for_each_set_bit(offset, &status, port_pin_count) -			generic_handle_domain_irq(gc->irq.domain, offset + lines_done); -	} +	status = realtek_gpio_read_isr(ctrl); +	for_each_set_bit(offset, &status, gc->ngpio) +		generic_handle_domain_irq(gc->irq.domain, offset);  	chained_irq_exit(irq_chip, desc);  } -static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl, -	unsigned int port, int cpu) +static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl, int cpu)  { -	return ctrl->cpumask_base + ctrl->port_offset_u8(port) + -		REALTEK_GPIO_PORTS_PER_BANK * cpu; +	return ctrl->cpumask_base + REALTEK_GPIO_PORTS_PER_BANK * cpu;  }  static int realtek_gpio_irq_set_affinity(struct irq_data *data, @@ -271,12 +274,10 @@ static int realtek_gpio_irq_set_affinity(struct irq_data *data,  {  	struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);  	unsigned int line = irqd_to_hwirq(data); -	unsigned int port = line / 8; -	unsigned int port_pin = line % 8;  	void __iomem *irq_cpu_mask;  	unsigned long flags;  	int cpu; -	u8 v; +	u32 v;  	if (!ctrl->cpumask_base)  		return -ENXIO; @@ -284,15 +285,15 @@ static int realtek_gpio_irq_set_affinity(struct irq_data *data,  	raw_spin_lock_irqsave(&ctrl->lock, flags);  	for_each_cpu(cpu, &ctrl->cpu_irq_maskable) { -		irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu); -		v = ioread8(irq_cpu_mask); +		irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, cpu); +		v = ctrl->bank_read(irq_cpu_mask);  		if (cpumask_test_cpu(cpu, dest)) -			v |= BIT(port_pin); +			v |= BIT(line);  		else -			v &= ~BIT(port_pin); +			v &= ~BIT(line); -		iowrite8(v, irq_cpu_mask); +		ctrl->bank_write(irq_cpu_mask, v);  	}  	raw_spin_unlock_irqrestore(&ctrl->lock, flags); @@ -305,16 +306,17 @@ static int realtek_gpio_irq_set_affinity(struct irq_data *data,  static int realtek_gpio_irq_init(struct gpio_chip *gc)  {  	struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); -	unsigned int port; +	u32 mask_all = GENMASK(gc->ngpio - 1, 0); +	unsigned int line;  	int cpu; -	for (port = 0; (port * 8) < gc->ngpio; port++) { -		realtek_gpio_write_imr(ctrl, port, 0, 0); -		realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0)); +	for (line = 0; line < gc->ngpio; line++) +		realtek_gpio_update_line_imr(ctrl, line); -		for_each_cpu(cpu, &ctrl->cpu_irq_maskable) -			iowrite8(GENMASK(7, 0), realtek_gpio_irq_cpu_mask(ctrl, port, cpu)); -	} +	realtek_gpio_clear_isr(ctrl, mask_all); + +	for_each_cpu(cpu, &ctrl->cpu_irq_maskable) +		ctrl->bank_write(realtek_gpio_irq_cpu_mask(ctrl, cpu), mask_all);  	return 0;  } @@ -387,12 +389,14 @@ static int realtek_gpio_probe(struct platform_device *pdev)  	if (dev_flags & GPIO_PORTS_REVERSED) {  		bgpio_flags = 0; -		ctrl->port_offset_u8 = realtek_gpio_port_offset_u8_rev; -		ctrl->port_offset_u16 = realtek_gpio_port_offset_u16_rev; +		ctrl->bank_read = realtek_gpio_bank_read; +		ctrl->bank_write = realtek_gpio_bank_write; +		ctrl->line_imr_pos = realtek_gpio_line_imr_pos;  	} else {  		bgpio_flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; -		ctrl->port_offset_u8 = realtek_gpio_port_offset_u8; -		ctrl->port_offset_u16 = realtek_gpio_port_offset_u16; +		ctrl->bank_read = realtek_gpio_bank_read_swapped; +		ctrl->bank_write = realtek_gpio_bank_write_swapped; +		ctrl->line_imr_pos = realtek_gpio_line_imr_pos_swapped;  	}  	err = bgpio_init(&ctrl->gc, dev, 4, | 
