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

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:
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