Subject: Yosemite/440EP why are readl()/ioread32() setup to read little-endian?

From: David Hawkins <[email-masked]>
Date: Tue, 24 Jan 2006 10:08:06 -0800

Hi all,

I was writing a simple driver to test IRQ handling on the
AMCC Yosemite board. The board has an 8x2 header with several
GPIO pins, a number of which can be configured as IRQ inputs.

I plan to setup GPIO46 as output, and GPIO47 as input, and
select IRQ8 on that pin.

The GPIO46 and GPIO47 are configured via the GPIO1 registers
at 0xEF600C00. U-Boot can be used to read the register values:

=> md ef600c00 b
ef600c00: 00000000 c2000000 50080000 00000000    ........P.......
ef600c10: 00000000 00000000 00000000 3ffcfffd    ............?...
ef600c20: 00000000 00000000 00000000    ............

=> md ef600c30 6
ef600c30: 00000400 00000000 00010000 00000000    ................
ef600c40: 00000000 00000000    ........
=>

So

ef600c00  GPIO1_OR    = 00000000
ef600c04  GPIO1_TCR   = c2000000
ef600c30  GPIO1_ISR1L = 00000400

However, if I read those same registers back under Linux,
using the driver code pasted at the end of this message
I get:

# insmod yosemite_gpio.ko
- remap the GPIO registers
- remapped to address 0xD1008C00

- Read some GPIO1 registers using readl()
- GPIO1_OR    = 0x00000000
- GPIO1_TCR   = 0x000000C2
- GPIO1_ISR1L = 0x00040000

- Read some GPIO1 registers using ioread32()
- GPIO1_OR    = 0x00000000
- GPIO1_TCR   = 0x000000C2
- GPIO1_ISR1L = 0x00040000

- Read some GPIO1 registers using an integer pointer
- GPIO1_OR    = 0x00000000
- GPIO1_TCR   = 0xC2000000
- GPIO1_ISR1L = 0x00000400

readl() and ioread32() read the registers in little-endian format!

Looking at asm-ppc/io.h

  - ioread32() is just a readl()
  - line 180 (2.6.13) has readl() as in_le32()
    (the code is the same in the 2.6.15-denx kernel too)

So, this explains why the data is read in little-endian format,
but not why this was done.

If the processor was reading from the PCI bus, then sure, I
could understand why this might be used, but even then, that
should be up to the user, eg. by using cpu_to_le32 etc.

Should I just be using pointers for remapped processor
registers, and only use readl(), ioread32(), etc, on external
memory?

I know this is just a big-endian/little-endian issue, I'm
really just asking for the driver writing 'best practices'
in this regard.

Looking forward to enlightenment :)

Cheers
Dave

--------------------------driver code----------------------------

/* yosemite_gpio.c */

#include <linux/module.h>   /* kernel modules                    */
#include <asm/io.h>         /* ioremap64(), iounmap(), readl()   */

static unsigned long long base = 0x0EF600C00; // 36-bit address
static unsigned int size = 0x44;
static char *kernel;

static int __init simple_init(void)
{
	int *p;
	
	/* Get the GPIO control registers */
	printk(" - remap the GPIO registers\n");
	kernel = ioremap64(base, size);
	printk(" - remapped to address 0x%.8X\n", (int)kernel);

	printk(" - Read some GPIO1 registers using readl()\n");
	printk(" - GPIO1_OR    = 0x%.8X\n", readl(kernel));
	printk(" - GPIO1_TCR   = 0x%.8X\n", readl(kernel+0x04));
	printk(" - GPIO1_ISR1L = 0x%.8X\n", readl(kernel+0x30));

	printk(" - Read some GPIO1 registers using ioread32()\n");
	printk(" - GPIO1_OR    = 0x%.8X\n", ioread32(kernel));
	printk(" - GPIO1_TCR   = 0x%.8X\n", ioread32(kernel+0x04));
	printk(" - GPIO1_ISR1L = 0x%.8X\n", ioread32(kernel+0x30));
	
	p = (int *)kernel;
	printk(" - Read some GPIO1 registers using an integer pointer\n");
	printk(" - GPIO1_OR    = 0x%.8X\n", p[0]);
	printk(" - GPIO1_TCR   = 0x%.8X\n", p[1]);
	printk(" - GPIO1_ISR1L = 0x%.8X\n", p[12]);
	
	/* Don't load */
	iounmap(kernel);
	return -EINVAL;
	
}
module_init(simple_init);
MODULE_LICENSE("GPL");