pxa270 GPIO pins configuration and usage

 

Introduction

As part of my work at 8D Technologies, I am responsible for maintaining the ARM linux kernel we use on our boards. This involves writing drivers for our hardware, debugging problems and adapting code for the specifics of our hardware.

I would have found a page like this one useful when I started hacking the Linux kernel on arm targets so I decided to create one. I used mostly the pxa255 processor and 2.6 series kernels but the content of this page should be valid for newer pxa2xx processors.

This page is by no means complete and it is not a substitute to reading the documentation, keeping up with the linux-arm mailing list and reading the source code and pxa2xx dev manual(s).

I'll try to update this page if I notice any of those practises are deprecated or wrong. Feel free to contact me if you think I should change anything.


Content:

  • GPIO pins configuration and usage
    • Input/output configuration
    • Controlling and reading GPIO pins
    • Convering between IRQ numbers and GPIO numbers
    • Configuring a GPIO pin as interrupt source
  • Misc
    • Enabling or Disabling a clock


GPIO pins configuration and usage

To configure a GPIO as input or output:

	pxa_gpio_mode(gpio_number);		/* Configure gpio_number as input */
pxa_gpio_mode(gpio_number | GPIO_OUT); /* Configure gpio_number as output */

Alternates functions are also set with pxa_gpio_mode. For example, pxa_gpio_mode(GPIO81_NSSP_CLK_OUT) will set GPIO981 in alternate function 1, output.

GPIO81 is defined as (81 | GPIO_ALT_FN_1_OUT) in include/asm-arm/arch-pxa/pxa-regs.h. Consult pxa-regs.h for more information.


DO NOT! set and clear bits using GPDR directly like this: (at least, without disabling interrupts)
GPDR0 |= somebit;
// or
GPDR0 &= ~someotherbit;
This is a read-modify-write operation which is racy. Better use the pxa_gpio_mode functions.

Controlling and reading GPIO pins

Once a GPIO is configured to work as input or output, you'll probably want to control it or check it's level. Here's how.

The pxa255 processor provides 85 GPIO pins. Controlling an output pin is done using the GPSR(0-2) and GPCR(0-2) registers.

Normally you would have to choose the register which contains the GPIO pin you want to use but in include/asm-arm/arch-pxa/pxa-regs.h there are macros designed to do this. You just need to pass the GPIO number as argument:
	GPSR(gpio_id) = GPIO_bit(gpio_id);	/* Set a pin (gpio pin = 1) */
GPCR(gpio_id) = GPIO_bit(gpio_id); /* Clear a pin (gpio pin = 0) */
To check the level of a GPIO pin, you must use the GPLR(0-2) registers. Again, there are helpful macros in pxa-regs.h:
	if (GPLR(gpio_id) & GPIO_bit(gpio_id))
{
// pin high (1)
}
else
{
// pin low (0)
}
For more informatin about GPIO registers, consult the PXA255 Processor Developer's Manual chapter 4.

Converting between IRQ numbers and GPIO numbers:

Interrupt numbers and GPIO numbers dont match. There are macros in include/asm-arm/arch-pxa/irqs.h which are useful to convert between interrupt numbers and gpio numbers.

You'll find this useful when using GPIO pins as interrupt sources since interrupt related functions usually take interrupt numbers.

	IRQ_TO_GPIO(irq_number)		/* Get the GPIO number corresponding to irq_number */
IRQ_GPIO(gpio_number) /* Get the IRQ number corresponding to gpio_number */

Configuring a GPIO pin as interrupts source:

First, configure the GPIO pin as input:
	pxa_gpio_mode(gpio_number);
Next, use set_irq_type(unsigned int irq, unsigned int type) to configure the type of IRQ you want. The types I know of are:
  • IRQT_RISING: Interrupt triggered on rising edge. (0 to 1 signal transition)
  • IRQT_FALLING: Interrupt triggered on falling edge. (1 to 0 signal transition)
  • IRQT_BOTHEDGE: Interrupt trigered on rising and falling edges. (signal transitions)
  • IRQT_LOW: Interrupt triggered as long as the signal stays low.
  • IRQT_HIGH: Interrupt triggered as long as the signal stays high.
Example:
	pxa_gpio_mode(gpio_number);				/* Configure GPIO as input */
set_irq_type(IRQ_GPIO(gpio_number), IRQT_FALLING); /* Configure the interrupt type for this GPIO */
Finally, you can use request_irq (from include/linux/interrupt.h) to register your interrupt handler:
	request_irq(IRQ_GPIO(gpio_number), my_irq_handler_func, 0, "myirq name", NULL)
See arch/arm/kernel/irq.c for documentation about the request_irq function.

Misc

Enabling a clock:

The pxa255 has a register named CKEN which can enable or disable the clocks of most of the peripherals unit. To set or clear bits in this register requires a read-modify-write operation, which it racy. You could disable interrupts during this operations but it is simpler to use to use pxa_set_cken(clock, enable).

For instance, to enable the NSSP clock:
	pxa_set_cken(CKEN9_NSSP, 1);

Interrupts and variables

When a variable is accessed from both interrupt and user context, precautions must be taken to garantee the data integrity.

1: The variable should be declared as volatile. This tells the compiler that the value of this variable may be changed at any time. (eg: during an interrupt).

2: The interrupts must be disabled when doing read-modify-write operaions or non-atomic writes. Here is how to disabled interrupts during a block of code.

	#include  	/* local_irq_{save,restore} */

void myfunc(void)
{
unsigned long flags; /* the flags are saved in this variable */
/* other vars... */

local_irq_save(flags); /* save the interrupt enable/disabled flags */

/** critical code ... **/

local_irq_restore(flags); /* restore the interrupt enable/disable flags */
}
Note1: Disable interrupts only when necessary, for the shortest possible time.
Note2: The save_flags(); cli(); restore_flags(); technique is deprecated in 2.6

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值