[PATCH 05/11] ARM: imx: Add GPIO support for i.MX28

Uwe Kleine-König u.kleine-koenig at pengutronix.de
Mon Nov 15 11:43:36 EST 2010


On Mon, Nov 15, 2010 at 10:36:29PM +0800, Shawn Guo wrote:
> SoC i.MX28 implements GPIO functions in block PINCTRL. It adds
> the support in the same file used by other i.MX SoCs, and uses
> cpu_is_mx28() to distinguish the PINCTRL from GPIO used by other
> i.MX SoCs.
> 
> Signed-off-by: Shawn Guo <shawn.guo at freescale.com>
> ---
>  arch/arm/plat-mxc/gpio.c |  186 +++++++++++++++++++++++++++++++++++++--------
>  1 files changed, 153 insertions(+), 33 deletions(-)
> 
> diff --git a/arch/arm/plat-mxc/gpio.c b/arch/arm/plat-mxc/gpio.c
> index 9c3e362..f0d94af 100644
> --- a/arch/arm/plat-mxc/gpio.c
> +++ b/arch/arm/plat-mxc/gpio.c
> @@ -30,6 +30,17 @@
>  static struct mxc_gpio_port *mxc_gpio_ports;
>  static int gpio_table_size;
>  
> +/* PINCTRL */
> +#define GPIO_PORT_ID(p)	(((void *)p - (void *)mxc_gpio_ports) / sizeof(p))
> +#define GPIO_DOUT(p)		(0x0700 + GPIO_PORT_ID(p) * 0x10)
> +#define GPIO_DIN(p)		(0x0900 + GPIO_PORT_ID(p) * 0x10)
> +#define GPIO_DOE(p)		(0x0b00 + GPIO_PORT_ID(p) * 0x10)
> +#define GPIO_PIN2IRQ(p)		(0x1000 + GPIO_PORT_ID(p) * 0x10)
> +#define GPIO_IRQEN(p)		(0x1100 + GPIO_PORT_ID(p) * 0x10)
> +#define GPIO_LEV(p)		(0x1200 + GPIO_PORT_ID(p) * 0x10)
> +#define GPIO_POL(p)		(0x1300 + GPIO_PORT_ID(p) * 0x10)
> +#define GPIO_IRQSTAT(p)		(0x1400 + GPIO_PORT_ID(p) * 0x10)
> +
>  #define cpu_is_mx1_mx2()	(cpu_is_mx1() || cpu_is_mx2())
>  
>  #define GPIO_DR		(cpu_is_mx1_mx2() ? 0x1c : 0x00)
> @@ -40,17 +51,25 @@ static int gpio_table_size;
>  #define GPIO_IMR	(cpu_is_mx1_mx2() ? 0x30 : 0x14)
>  #define GPIO_ISR	(cpu_is_mx1_mx2() ? 0x34 : 0x18)
>  
> -#define GPIO_INT_LOW_LEV	(cpu_is_mx1_mx2() ? 0x3 : 0x0)
> -#define GPIO_INT_HIGH_LEV	(cpu_is_mx1_mx2() ? 0x2 : 0x1)
> -#define GPIO_INT_RISE_EDGE	(cpu_is_mx1_mx2() ? 0x0 : 0x2)
> -#define GPIO_INT_FALL_EDGE	(cpu_is_mx1_mx2() ? 0x1 : 0x3)
> +#define GPIO_INT_LOW_LEV \
> +	(cpu_is_mx28() ? 0x1 : (cpu_is_mx1_mx2() ? 0x3 : 0x0))

I'm more and more conviced that it doesn't make sense to fold mx28 into
the existing mxc code.
> +#define GPIO_INT_HIGH_LEV \
> +	(cpu_is_mx28() ? 0x3 : (cpu_is_mx1_mx2() ? 0x2 : 0x1))
> +#define GPIO_INT_RISE_EDGE \
> +	(cpu_is_mx1_mx2() ? 0x0 : 0x2)
> +#define GPIO_INT_FALL_EDGE \
> +	(cpu_is_mx28() ? 0x0 : (cpu_is_mx1_mx2() ? 0x1 : 0x3))
>  #define GPIO_INT_NONE		0x4
>  
>  /* Note: This driver assumes 32 GPIOs are handled in one register */
>  
>  static void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index)
>  {
> -	__raw_writel(1 << index, port->base + GPIO_ISR);
> +	if (cpu_is_mx28())
> +		__raw_writel(1 << index,
> +				port->base + GPIO_IRQSTAT(port) + CLR_ADDR);
> +	else
> +		__raw_writel(1 << index, port->base + GPIO_ISR);
>  }
>  
>  static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index,
> @@ -58,9 +77,23 @@ static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index,
>  {
>  	u32 l;
>  
> -	l = __raw_readl(port->base + GPIO_IMR);
> -	l = (l & (~(1 << index))) | (!!enable << index);
> -	__raw_writel(l, port->base + GPIO_IMR);
> +	if (cpu_is_mx28()) {
> +		if (enable == 0) {
> +			__raw_writel(1 << index,
> +				port->base + GPIO_PIN2IRQ(port) + CLR_ADDR);
> +			__raw_writel(1 << index,
> +				port->base + GPIO_IRQEN(port) + CLR_ADDR);
> +		} else {
> +			__raw_writel(1 << index,
> +				port->base + GPIO_PIN2IRQ(port) + SET_ADDR);
> +			__raw_writel(1 << index,
> +				port->base + GPIO_IRQEN(port) + SET_ADDR);
> +		}
> +	} else {
> +		l = __raw_readl(port->base + GPIO_IMR);
> +		l = (l & (~(1 << index))) | (!!enable << index);
> +		__raw_writel(l, port->base + GPIO_IMR);
> +	}
>  }
>  
>  static void gpio_ack_irq(u32 irq)
> @@ -120,10 +153,28 @@ static int gpio_set_irq_type(u32 irq, u32 type)
>  		return -EINVAL;
>  	}
>  
> -	reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
> -	bit = gpio & 0xf;
> -	val = __raw_readl(reg) & ~(0x3 << (bit << 1));
> -	__raw_writel(val | (edge << (bit << 1)), reg);
> +	if (cpu_is_mx28()) {
> +		/* set level or edge */
> +		if (edge & 0x1)
> +			__raw_writel(1 << (gpio & 31),
> +					port->base + GPIO_LEV(port) + SET_ADDR);
> +		else
> +			__raw_writel(1 << (gpio & 31),
> +					port->base + GPIO_LEV(port) + CLR_ADDR);
> +		/* set polarity */
> +		if ((edge >> 1) & 0x1)
> +			__raw_writel(1 << (gpio & 31),
> +					port->base + GPIO_POL(port) + SET_ADDR);
> +		else
> +			__raw_writel(1 << (gpio & 31),
> +					port->base + GPIO_POL(port) + CLR_ADDR);
> +	} else {
> +		reg += GPIO_ICR1 + ((gpio & 0x10) >> 2);
> +		bit = gpio & 0xf;
> +		val = __raw_readl(reg) & ~(0x3 << (bit << 1));
> +		__raw_writel(val | (edge << (bit << 1)), reg);
> +	}
> +
>  	_clear_gpio_irqstatus(port, gpio & 0x1f);
>  
>  	return 0;
> @@ -135,23 +186,36 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
>  	u32 bit, val;
>  	int edge;
>  
> -	reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
> -	bit = gpio & 0xf;
> -	val = __raw_readl(reg);
> -	edge = (val >> (bit << 1)) & 3;
> -	val &= ~(0x3 << (bit << 1));
> -	if (edge == GPIO_INT_HIGH_LEV) {
> -		edge = GPIO_INT_LOW_LEV;
> -		pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
> -	} else if (edge == GPIO_INT_LOW_LEV) {
> -		edge = GPIO_INT_HIGH_LEV;
> -		pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
> +	if (cpu_is_mx28()) {
> +		edge = 1 << (gpio & 31);
> +		val = __raw_readl(port->base + GPIO_LEV(port));
> +		if (val & edge) {
> +			/* level is invalid for this function */
> +			pr_err("mxc: invalid configuration for GPIO %d: %x\n",
> +				gpio, edge);
> +			return;
> +		}
> +		__raw_writel(1 << (gpio & 31),
> +				port->base + GPIO_POL(port) + TOG_ADDR);
>  	} else {
> -		pr_err("mxc: invalid configuration for GPIO %d: %x\n",
> -		       gpio, edge);
> -		return;
> +		reg += GPIO_ICR1 + ((gpio & 0x10) >> 2);
> +		bit = gpio & 0xf;
> +		val = __raw_readl(reg);
> +		edge = (val >> (bit << 1)) & 3;
> +		val &= ~(0x3 << (bit << 1));
> +		if (edge == GPIO_INT_HIGH_LEV) {
> +			edge = GPIO_INT_LOW_LEV;
> +			pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
> +		} else if (edge == GPIO_INT_LOW_LEV) {
> +			edge = GPIO_INT_HIGH_LEV;
> +			pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
> +		} else {
> +			pr_err("mxc: invalid configuration for GPIO %d: %x\n",
> +			       gpio, edge);
> +			return;
> +		}
> +		__raw_writel(val | (edge << (bit << 1)), reg);
>  	}
> -	__raw_writel(val | (edge << (bit << 1)), reg);
>  }
>  
>  /* handle 32 interrupts in one status register */
> @@ -183,6 +247,29 @@ static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
>  	mxc_gpio_irq_handler(port, irq_stat);
>  }
>  
> +/* MX28 has one interrupt *per* gpio port */
> +static void mx28_gpio_irq_handler(u32 irq, struct irq_desc *desc)
> +{
> +	u32 irq_stat;
> +	struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq);
> +	u32 gpio_irq_no_base = port->virtual_irq_start;
> +
> +	irq_stat = __raw_readl(port->base + GPIO_IRQSTAT(port)) &
> +			__raw_readl(port->base + GPIO_IRQEN(port)) &
> +			__raw_readl(port->base + GPIO_PIN2IRQ(port));
> +
> +	while (irq_stat != 0) {
> +		int irqoffset = fls(irq_stat) - 1;
> +
> +		if (port->both_edges & (1 << irqoffset))
> +			mxc_flip_edge(port, irqoffset);
> +
> +		generic_handle_irq(gpio_irq_no_base + irqoffset);
> +
> +		irq_stat &= ~(1 << irqoffset);
> +	}
> +}
> +
>  /* MX2 has one interrupt *for all* gpio ports */
>  static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
>  {
> @@ -247,14 +334,20 @@ static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset,
>  		container_of(chip, struct mxc_gpio_port, chip);
>  	u32 l;
>  	unsigned long flags;
> +	void __iomem *reg;
> +
> +	if (cpu_is_mx28())
> +		reg = port->base + GPIO_DOE(port);
> +	else
> +		reg = port->base + GPIO_GDIR;
>  
>  	spin_lock_irqsave(&port->lock, flags);
> -	l = __raw_readl(port->base + GPIO_GDIR);
> +	l = __raw_readl(reg);
>  	if (dir)
>  		l |= 1 << offset;
>  	else
>  		l &= ~(1 << offset);
> -	__raw_writel(l, port->base + GPIO_GDIR);
> +	__raw_writel(l, reg);
>  	spin_unlock_irqrestore(&port->lock, flags);
>  }
>  
> @@ -262,9 +355,14 @@ static void mxc_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
>  {
>  	struct mxc_gpio_port *port =
>  		container_of(chip, struct mxc_gpio_port, chip);
> -	void __iomem *reg = port->base + GPIO_DR;
>  	u32 l;
>  	unsigned long flags;
> +	void __iomem *reg;
> +
> +	if (cpu_is_mx28())
> +		reg = port->base + GPIO_DOUT(port);
> +	else
> +		reg = port->base + GPIO_DR;
Here again, you clutter several hot paths with if(cpu_is_...()) tests.

gpiolib is generic enough not to need this.
>  
>  	spin_lock_irqsave(&port->lock, flags);
>  	l = (__raw_readl(reg) & (~(1 << offset))) | (!!value << offset);
> @@ -276,8 +374,14 @@ static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset)
>  {
>  	struct mxc_gpio_port *port =
>  		container_of(chip, struct mxc_gpio_port, chip);
> +	void __iomem *reg;
> +
> +	if (cpu_is_mx28())
> +		reg = port->base + GPIO_DIN(port);
> +	else
> +		reg = port->base + GPIO_PSR;
>  
> -	return (__raw_readl(port->base + GPIO_PSR) >> offset) & 1;
> +	return (__raw_readl(reg) >> offset) & 1;
>  }
>  
>  static int mxc_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
> @@ -306,8 +410,19 @@ int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
>  
>  	for (i = 0; i < cnt; i++) {
>  		/* disable the interrupt and clear the status */
> -		__raw_writel(0, port[i].base + GPIO_IMR);
> -		__raw_writel(~0, port[i].base + GPIO_ISR);
> +		if (cpu_is_mx28()) {
> +			__raw_writel(0, port[i].base +
> +						GPIO_PIN2IRQ(&port[i]));
> +			__raw_writel(0, port[i].base +
> +						GPIO_IRQEN(&port[i]));
> +			__raw_writel(~0, port[i].base +
> +						GPIO_IRQSTAT(&port[i]) +
> +						CLR_ADDR);
> +		} else {
> +			__raw_writel(0, port[i].base + GPIO_IMR);
> +			__raw_writel(~0, port[i].base + GPIO_ISR);
> +		}
> +
>  		for (j = port[i].virtual_irq_start;
>  			j < port[i].virtual_irq_start + 32; j++) {
>  			set_irq_chip(j, &gpio_irq_chip);
> @@ -338,6 +453,11 @@ int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
>  						mx3_gpio_irq_handler);
>  				set_irq_data(port[i].irq_high, &port[i]);
>  			}
> +		} else if (cpu_is_mx28()) {
> +			/* setup one handler for each entry */
> +			set_irq_chained_handler(port[i].irq,
> +						mx28_gpio_irq_handler);
> +			set_irq_data(port[i].irq, &port[i]);
>  		}
>  	}
>  
> -- 
> 1.7.1
> 
> 
> 

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |



More information about the linux-arm-kernel mailing list