linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* Endian problem when accessing internel regs on 8347
@ 2008-01-17  6:26 Bruce_Leonard
  2008-01-17 15:01 ` Ben Warren
  2008-01-17 17:39 ` Scott Wood
  0 siblings, 2 replies; 11+ messages in thread
From: Bruce_Leonard @ 2008-01-17  6:26 UTC (permalink / raw)
  To: linuxppc-embedded

Hi,

I've got an MPC8347 running the 2.6.24-rc5 kernel and I'm trying to access 
the GPIO registers.  I already have a driver that's doing related work 
(tho across PCI) and I just want to remap the internal CPU address to a 
kernal address and be able to read/write the register using an IOCTL call 
from user space.  My problem is, every accessor I try does an endian 
conversion before giving me my data.  So here's some code snipits of what 
I'm doing:

request_mem_region(0xe0000c00, 24, "my driver");  // 0xe0000c00 is the 
memory mapped location of the GPIO regs
data_ptr2 = ioremap(0xe0000c00, 24);

Then in the IOCTL function I try to read from the register.  First I 
tried:

ioread32(data_ptr2);

I know that the mapping is working okay because I got the data from the 
GPIO direction register, but it's been byte swapped.  The contents of the 
register are 0xFFFFFFE7, but what my user space app gets back is 
0xE7FFFFFF.  I've tried all the varients I can find, readl(), inl(), 
in_be32(), in_le32(), even __raw_readl().  They all give me back byte 
swapped data.  I'm very confused.  Especially since I've looked at 
in_be32() and in_le32(), they're both inline assembly, and they use 
different instructions.  They can't be giving me the same results.  I'm 
sure I'm not the first person to want to access PowerPC internal registers 
through a driver.  Can anyone give me a hint what I'm doing wrong?

As an additional question related to PowerPC inline assembly, can anyone 
tell me what "%U1%X1" means in the following:

__asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" : "=r" (ret) : 
"m" (*addr));

Thanks.

See 'ya!

Bruce

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Endian problem when accessing internel regs on 8347
  2008-01-17  6:26 Endian problem when accessing internel regs on 8347 Bruce_Leonard
@ 2008-01-17 15:01 ` Ben Warren
  2008-01-17 18:01   ` Bruce_Leonard
  2008-01-17 17:39 ` Scott Wood
  1 sibling, 1 reply; 11+ messages in thread
From: Ben Warren @ 2008-01-17 15:01 UTC (permalink / raw)
  To: Bruce_Leonard; +Cc: linuxppc-embedded

[-- Attachment #1: Type: text/plain, Size: 2065 bytes --]

Hi Bruce,

Bruce_Leonard@selinc.com wrote:
> Hi,
>
> I've got an MPC8347 running the 2.6.24-rc5 kernel and I'm trying to access 
> the GPIO registers.  I already have a driver that's doing related work 
> (tho across PCI) and I just want to remap the internal CPU address to a 
> kernal address and be able to read/write the register using an IOCTL call 
> from user space.  My problem is, every accessor I try does an endian 
> conversion before giving me my data.  So here's some code snipits of what 
> I'm doing:
>
> request_mem_region(0xe0000c00, 24, "my driver");  // 0xe0000c00 is the 
> memory mapped location of the GPIO regs
> data_ptr2 = ioremap(0xe0000c00, 24);
>
> Then in the IOCTL function I try to read from the register.  First I 
> tried:
>
> ioread32(data_ptr2);
>
> I know that the mapping is working okay because I got the data from the 
> GPIO direction register, but it's been byte swapped.  The contents of the 
> register are 0xFFFFFFE7, but what my user space app gets back is 
> 0xE7FFFFFF.  I've tried all the varients I can find, readl(), inl(), 
> in_be32(), in_le32(), even __raw_readl().  They all give me back byte 
> swapped data.  I'm very confused.  Especially since I've looked at 
> in_be32() and in_le32(), they're both inline assembly, and they use 
> different instructions.  They can't be giving me the same results.  I'm 
> sure I'm not the first person to want to access PowerPC internal registers 
> through a driver.  Can anyone give me a hint what I'm doing wrong?
>
> As an additional question related to PowerPC inline assembly, can anyone 
> tell me what "%U1%X1" means in the following:
>
> __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" : "=r" (ret) : 
> "m" (*addr));
>
> Thanks.
>
> See 'ya!
>
> Bruce
> _______________________________________________
>   
I've attached a poorly-written-yet-functional GPIO driver that I wrote a 
while ago for the MPC8349 (same as what you have for all intents and 
purposes). It uses in_be32() and out_be32().

Let me know if you have any questions.

regards,
Ben

[-- Attachment #2: mpc83xx_gpio.c --]
[-- Type: text/x-csrc, Size: 6289 bytes --]

/******************************************************************************
 * 
 * gpio_driver.c 
 * Copyright Qstreams Networks 2006
 *
 * This file contains a simple Linux device driver for accessing GPIO ports
 * It is originally targeted for the MPC8349 PowerQUICC II Pro processor, 
 * although with simple modifications it should work with lots of CPUs.  Note
 * that all CPU accesses are big-endian.
 *
 * History:
 * 04/11/2006	BAW		Initial Creation
 *
 *****************************************************************************/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include "gpio.h"

struct gpio_dev {
	struct gpio_reg *regs;
	struct cdev cdev;
};

MODULE_AUTHOR("Ben Warren");
MODULE_LICENSE("GPL");

static int gpio_major_num;
static struct gpio_dev *gpio_devices;

static spinlock_t gpioLock[GPIO_NUM_BANKS];
extern phys_addr_t get_immrbase(void);

static u32 readGpioReg(u8 bank, u32 * addr)
{
	u32 value;

	spin_lock(&gpioLock[bank]);
	value = in_be32(addr);
	spin_unlock(&gpioLock[bank]);
	return value;
}

static void writeGpioReg(u8 bank, u32 * addr, u32 val, u32 mask)
{

	spin_lock(&gpioLock[bank]);
	out_be32(addr, (in_be32(addr) & ~mask) | val);
	spin_unlock(&gpioLock[bank]);
}

/***** 
 * 		Kernel-mode GPIO access functions. 
 *		DO NOT CALL FROM USERSPACE
 *****/

int kgpio_set_dir(u8 bank, u8 bit, u8 isOutput)
{
	u32 value = GPIO_BIT(bit);
	if ((bank >= GPIO_NUM_BANKS) || !gpio_devices) {
		return -ENODEV;
	}
	writeGpioReg(bank, &gpio_devices[bank].regs->gpxdir,
		     isOutput ? value : 0, value);
	return 0;
}

EXPORT_SYMBOL(kgpio_set_dir);

int kgpio_get_dir(u8 bank, u8 bit)
{
	u32 value;
	if ((bank >= GPIO_NUM_BANKS) || !gpio_devices) {
		return -ENODEV;
	}
	value = readGpioReg(bank, &gpio_devices[bank].regs->gpxdir);
	return (value & GPIO_BIT(bit) ? 1 : 0);
}

EXPORT_SYMBOL(kgpio_get_dir);

int kgpio_set_data(u8 bank, u8 bit, u8 isHigh)
{
	u32 value = GPIO_BIT(bit);
	if ((bank >= GPIO_NUM_BANKS) || !gpio_devices) {
		return -ENODEV;
	}
	writeGpioReg(bank, &gpio_devices[bank].regs->gpxdat,
		     isHigh ? value : 0, value);
	return 0;
}

EXPORT_SYMBOL(kgpio_set_data);

int kgpio_get_data(u8 bank, u8 bit)
{
	u32 value;
	if ((bank >= GPIO_NUM_BANKS) || !gpio_devices) {
		return -ENODEV;
	}
	value = readGpioReg(bank, &gpio_devices[bank].regs->gpxdat);
	return (value & GPIO_BIT(bit) ? 1 : 0);
}

EXPORT_SYMBOL(kgpio_get_data);

int gpio_open(struct inode *inode, struct file *filp)
{
	struct gpio_dev *dev;

	/* Set things up so other methods can access private data */
	dev = container_of(inode->i_cdev, struct gpio_dev, cdev);
	filp->private_data = dev;
	return 0;
}

int gpio_ioctl(struct inode *inode, struct file *filp,
	       unsigned int cmd, unsigned long arg)
{
	int rc = 0;
	gpioDataAccess gpioData = { 0, 0 };
	struct gpio_dev *dev;
	u8 bank;

	/* Check that the caller really wanted GPIO */
	if (_IOC_TYPE(cmd) != GPIO_IOC_MAGIC)
		return -ENOTTY;
	if (_IOC_NR(cmd) > GPIO_IOC_MAXNR)
		return -ENOTTY;

	/* Check access controls */
	if (_IOC_DIR(cmd) & _IOC_READ)
		rc = !access_ok(VERIFY_WRITE, (void __user *)arg,
				_IOC_SIZE(cmd));
	else if (_IOC_DIR(cmd) & _IOC_WRITE)
		rc = !access_ok(VERIFY_READ, (void __user *)arg,
				_IOC_SIZE(cmd));
	if (rc)
		return -EFAULT;

	bank = (u8) iminor(inode);
	dev = filp->private_data;

	switch (cmd) {
		/* Direction Register access */
	case GPIO_IOC_GET_DIR:
		return readGpioReg(bank, &dev->regs->gpxdir);
	case GPIO_IOC_SET_DIR:
		if (copy_from_user(&gpioData,
				   (const void __user *)arg,
				   sizeof(gpioDataAccess))) {
			printk(KERN_ERR "copy_from_user failed\n");
			return -EFAULT;
		}
		writeGpioReg(bank, &dev->regs->gpxdir,
			     gpioData.value, gpioData.mask);
		break;
		/* Data Register access */
	case GPIO_IOC_GET_DATA:
		return readGpioReg(bank, &dev->regs->gpxdat);
	case GPIO_IOC_SET_DATA:
		if (copy_from_user(&gpioData,
				   (const void __user *)arg,
				   sizeof(gpioDataAccess))) {
			printk(KERN_ERR "copy_from_user failed\n");
			return -EFAULT;
		}
		writeGpioReg(bank, &dev->regs->gpxdat,
			     gpioData.value, gpioData.mask);
		break;
	default:
		return -ENOTTY;
	}
	return rc;
}

/* File operations structure.  Pretty limited on this device */
struct file_operations gpio_fops = {
	.owner = THIS_MODULE,
	.open = gpio_open,
	.ioctl = gpio_ioctl,
};

static void gpio_cleanup(void)
{
	int i;
	u32 addr;
	dev_t devno = MKDEV(gpio_major_num, 0);
	if (gpio_devices) {
		for (i = 0; i < GPIO_NUM_BANKS; i++) {
			cdev_del(&gpio_devices[i].cdev);
		}
		iounmap(gpio_devices);
	}
	addr = get_immrbase() + GPIO1_IO_OFFSET;
	release_mem_region(addr, sizeof(struct gpio_reg));
	addr = get_immrbase() + GPIO2_IO_OFFSET;
	release_mem_region(addr, sizeof(struct gpio_reg));
	unregister_chrdev_region(devno, GPIO_NUM_BANKS);
}

static void gpio_setup_cdev(struct gpio_dev *dev, int index)
{
	int rc, devno = MKDEV(gpio_major_num, index);

	cdev_init(&dev->cdev, &gpio_fops);
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &gpio_fops;
	if ((rc = cdev_add(&dev->cdev, devno, 1)))
		printk(KERN_CRIT "Error %d adding gpio%d", rc, index);
}

static int gpio_init(void)
{
	int i, rc;
	dev_t dev = 0;
	u32 addr;

	/* Grab a dynamic major number and GPIO_NUM_BANKS minors */
	rc = alloc_chrdev_region(&dev, 0, GPIO_NUM_BANKS, "gpio");
	gpio_major_num = MAJOR(dev);
	if (rc < 0) {
		printk(KERN_CRIT "GPIO: couldn't get major number\n");
		return rc;
	}
	if (!(gpio_devices = kmalloc(GPIO_NUM_BANKS * sizeof(struct gpio_dev),
				     GFP_KERNEL))) {
		gpio_cleanup();
		return -ENOMEM;
	}
	for (i = 0; i < GPIO_NUM_BANKS; i++) {
		spin_lock_init(&gpioLock[i]);
		gpio_setup_cdev(&gpio_devices[i], i);
		addr =
		    get_immrbase() + (i ==
				      0 ? GPIO1_IO_OFFSET : GPIO2_IO_OFFSET);
		if (!request_mem_region(addr,
					sizeof(struct gpio_reg), "gpio")) {
			printk(KERN_CRIT "gpio: I/O memory request failed\n");
			return -ENODEV;
		}
		gpio_devices[i].regs = (struct gpio_reg *)ioremap_nocache(addr,
						  sizeof (struct gpio_reg));
		if (!gpio_devices[i].regs) {
			printk(KERN_CRIT "gpio: can't remap I/O memory\n");
			return -ENODEV;
		}

	}
	return 0;
}

module_init(gpio_init);
module_exit(gpio_cleanup);

[-- Attachment #3: gpio.h --]
[-- Type: text/x-chdr, Size: 829 bytes --]

#ifndef GPIO_INCLUDE_H
#define GPIO_INCLUDE_H

#include <asm/ioctl.h>

#define GPIO1_IO_OFFSET  0x00000c00
#define GPIO2_IO_OFFSET  0x00000d00

typedef struct _gpioDatAccess
{
	unsigned int value;
	unsigned int mask;
} gpioDataAccess;

#define GPIO_IOC_MAGIC 0xaa

#define GPIO_IOC_GET_DIR	_IO(GPIO_IOC_MAGIC, 1)
#define GPIO_IOC_SET_DIR	_IOW(GPIO_IOC_MAGIC, 2, gpioDataAccess)
#define GPIO_IOC_GET_DATA	_IO(GPIO_IOC_MAGIC, 3)
#define GPIO_IOC_SET_DATA	_IOW(GPIO_IOC_MAGIC, 4, gpioDataAccess)

#define GPIO_IOC_MAXNR	5

#define GPIO_NUM_BANKS	2
struct gpio_reg
{
	unsigned int	gpxdir;
	unsigned int	gpxdr;
	unsigned int	gpxdat;
	unsigned int	gpxier;
	unsigned int	gpximr;
	unsigned int	gpxicr;
}; 

/* On our CPU, bit 0 is the MS bit, so go from the left */

#define GPIO_BIT(x) (0x80000000 >> (x))

#endif  /* GPIO_INCLUDE_H */

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Endian problem when accessing internel regs on 8347
  2008-01-17  6:26 Endian problem when accessing internel regs on 8347 Bruce_Leonard
  2008-01-17 15:01 ` Ben Warren
@ 2008-01-17 17:39 ` Scott Wood
  2008-01-17 18:38   ` Bruce_Leonard
  1 sibling, 1 reply; 11+ messages in thread
From: Scott Wood @ 2008-01-17 17:39 UTC (permalink / raw)
  To: Bruce_Leonard; +Cc: linuxppc-embedded

Bruce_Leonard@selinc.com wrote:
> As an additional question related to PowerPC inline assembly, can anyone 
> tell me what "%U1%X1" means in the following:
> 
> __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" : "=r" (ret) : 
> "m" (*addr));

They allow the compiler to use update and/or index mode for the memory 
operand.

-Scott

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Endian problem when accessing internel regs on 8347
  2008-01-17 15:01 ` Ben Warren
@ 2008-01-17 18:01   ` Bruce_Leonard
  2008-01-17 18:25     ` Ben Warren
  0 siblings, 1 reply; 11+ messages in thread
From: Bruce_Leonard @ 2008-01-17 18:01 UTC (permalink / raw)
  To: Ben Warren; +Cc: linuxppc-embedded

Ben Warren <biggerbadderben@gmail.com> wrote on 01/17/2008 07:01:10 AM:

> > 
> I've attached a poorly-written-yet-functional GPIO driver that I wrote a 

> while ago for the MPC8349 (same as what you have for all intents and 
> purposes). It uses in_be32() and out_be32().
> 
Ben,

Thanks for the answer.  Hummmm, at first glance it looks like you're doing 
the same thing I am.  Only difference I can see is you use get_immrbase() 
to figure out the address to ask for and I've got it hardcoded.  Other 
than that we're both using request_mem_region() in in/out_be32().  Yet 
your's works for you and mine doesn't.  Well, I'll take a closer look 
tonight and see if there's something you're doing that I missed. 
Unfortunately, I've gotten a higher level interrupt this morning and it 
may ba a day or so before I get back to this and can give you any 
feedback.  Thanks again.

See 'ya!

Bruce

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Endian problem when accessing internel regs on 8347
  2008-01-17 18:01   ` Bruce_Leonard
@ 2008-01-17 18:25     ` Ben Warren
  0 siblings, 0 replies; 11+ messages in thread
From: Ben Warren @ 2008-01-17 18:25 UTC (permalink / raw)
  To: Bruce_Leonard; +Cc: linuxppc-embedded

Bruce_Leonard@selinc.com wrote:
> Ben Warren <biggerbadderben@gmail.com> wrote on 01/17/2008 07:01:10 AM:
>
>   
>> I've attached a poorly-written-yet-functional GPIO driver that I wrote a 
>>     
>
>   
>> while ago for the MPC8349 (same as what you have for all intents and 
>> purposes). It uses in_be32() and out_be32().
>>
>>     
> Ben,
>
> Thanks for the answer.  Hummmm, at first glance it looks like you're doing 
> the same thing I am.  Only difference I can see is you use get_immrbase() 
> to figure out the address to ask for and I've got it hardcoded.  Other 
> than that we're both using request_mem_region() in in/out_be32().  Yet 
> your's works for you and mine doesn't.  Well, I'll take a closer look 
> tonight and see if there's something you're doing that I missed. 
> Unfortunately, I've gotten a higher level interrupt this morning and it 
> may ba a day or so before I get back to this and can give you any 
> feedback.  Thanks again.
>
> See 'ya!
>
> Bruce
>
>   
Something else to consider is that I've never tried this beyond 2.6.19. 
Maybe things have changed since then.

Anyway, glad to help if I can.

cheers,
Ben

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Endian problem when accessing internel regs on 8347
  2008-01-17 17:39 ` Scott Wood
@ 2008-01-17 18:38   ` Bruce_Leonard
  2008-01-17 22:18     ` Scott Wood
  0 siblings, 1 reply; 11+ messages in thread
From: Bruce_Leonard @ 2008-01-17 18:38 UTC (permalink / raw)
  To: Scott Wood; +Cc: linuxppc-embedded

> > __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" : "=r" (ret) 
: 
> > "m" (*addr));
> 
> They allow the compiler to use update and/or index mode for the memory 
> operand.

Well that makes sense, U for update and X for index, but I'm not sure 
they're applicable to this particular instruction and I'm not sure that 
the memory operand makes sense for PowerPC.  However, before I post 
anything that makes me look like I've got hoof-n-mouth disease I'm going 
to do some more digging.  Is the GCC manual the only "official" 
documentation on this (I've found a couple of web sites but none of them 
gun.org) or is there another manual I should be looking for?

Thanks for the pointers.

Bruce

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Endian problem when accessing internel regs on 8347
  2008-01-17 18:38   ` Bruce_Leonard
@ 2008-01-17 22:18     ` Scott Wood
  2008-01-21  7:21       ` Bruce_Leonard
  0 siblings, 1 reply; 11+ messages in thread
From: Scott Wood @ 2008-01-17 22:18 UTC (permalink / raw)
  To: Bruce_Leonard; +Cc: linuxppc-embedded

Bruce_Leonard@selinc.com wrote:
>>> __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" : "=r" (ret) 
> : 
>>> "m" (*addr));
>> They allow the compiler to use update and/or index mode for the memory 
>> operand.
> 
> Well that makes sense, U for update and X for index, but I'm not sure 
> they're applicable to this particular instruction and I'm not sure that 
> the memory operand makes sense for PowerPC.

Why not?

> However, before I post 
> anything that makes me look like I've got hoof-n-mouth disease I'm going 
> to do some more digging.  Is the GCC manual the only "official" 
> documentation on this (I've found a couple of web sites but none of them 
> gun.org)

I believe so... other than the sources, of course. :-)

-Scott

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Endian problem when accessing internel regs on 8347
  2008-01-17 22:18     ` Scott Wood
@ 2008-01-21  7:21       ` Bruce_Leonard
  2008-01-21 15:16         ` Ben Warren
  2008-01-21 16:40         ` Scott Wood
  0 siblings, 2 replies; 11+ messages in thread
From: Bruce_Leonard @ 2008-01-21  7:21 UTC (permalink / raw)
  To: Scott Wood; +Cc: linuxppc-embedded

> >>> __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" : "=r" 
(ret) 
> > : 
> >>> "m" (*addr));
> >> They allow the compiler to use update and/or index mode for the 
memory 
> >> operand.
> > 
> > Well that makes sense, U for update and X for index, but I'm not sure 
> > they're applicable to this particular instruction and I'm not sure 
that 
> > the memory operand makes sense for PowerPC.
> 
> Why not?

Okay, after much digging and experimenting, I'll agree that the memory 
operand makes sense.  When I first said that I was thinking of direct 
manipulation of memory, which the PPC doesn't support.  (Don't ask, my 
brain sometimes goes off into la-la land without me :-> )  Also, update 
mode is applicable and makes sense.  But I do still have a problem with 
the index mode, though it could be compiler magic.  Here's why: the index 
mode of the 'lwz' instruction requires three operands, i.e., 'lwzx 
rD,rA,rB', and there's only place holders for two operands.  Is the 
compiler smart enough to add the third operand for the index mode?  If so, 
what does it put for the third operand?

Another question is how does the compiler know which mode to pick?  And 
what is the significance of the trailing number?  Some places in the code 
have %U1%X1 and others have %U2%X2?  I've found documentation for the # 
and ## tokens, but I can't find anything for the %U or %X tokens.

Now this has all been very interesting to learn but doesn't solve my 
underlying problem which I've finally drilled down to.  At first I thought 
in_be32() might be broken because I wasn't getting back the value I knew 
to be in the internal register.  I knew I had the address and the mapping 
to kernel space correct because I could use in_le32 and get the right 
value though it was byte swapped.  The value in the register was 
0xFFFFFFE7 but what I was getting from in_be32 was 0xFFFFFFFF.  Then I 
started playing and here's what I found:

Register value  in_be32 value
0x12345678              0x1234567
0xff345678              0xff345678
0xffff5678              0xffff5678
0xfffff678              0xfffff678
0xfffffe78              0xffffffff
0xffffff78              0xffffffff

This isn't an exhastive list but I tried about twenty values and pretty 
much what I found was that if bits 0 through 22 are set to 1 then in_be32 
reads 0xffffffff.  I've also tried it at a variety of addresses and the 
behaviour is the same.  in_le32 works fine as does in_be16.  I've got no 
ideas as to what may be wrong.  I don't want to just arbitrarily point to 
that %U1%X1 parameter list, but I get compiler errors if I try to remove 
them so I can't prove or disprove it and I can't find any documentation on 
it I can't even form a theroy.  Has anyone ever seen anything like this? 
Can't anyone suggest anything I can try?

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Endian problem when accessing internel regs on 8347
  2008-01-21  7:21       ` Bruce_Leonard
@ 2008-01-21 15:16         ` Ben Warren
  2008-01-21 16:40         ` Scott Wood
  1 sibling, 0 replies; 11+ messages in thread
From: Ben Warren @ 2008-01-21 15:16 UTC (permalink / raw)
  To: Bruce_Leonard; +Cc: Scott Wood, linuxppc-embedded

Hey Bruce,

Bruce_Leonard@selinc.com wrote:
>>>>> __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" : "=r" 
>>>>>           
> (ret) 
>   
>>> : 
>>>       
>>>>> "m" (*addr));
>>>>>           
>>>> They allow the compiler to use update and/or index mode for the 
>>>>         
> memory 
>   
>>>> operand.
>>>>         
>>> Well that makes sense, U for update and X for index, but I'm not sure 
>>> they're applicable to this particular instruction and I'm not sure 
>>>       
> that 
>   
>>> the memory operand makes sense for PowerPC.
>>>       
>> Why not?
>>     
>
> Okay, after much digging and experimenting, I'll agree that the memory 
> operand makes sense.  When I first said that I was thinking of direct 
> manipulation of memory, which the PPC doesn't support.  (Don't ask, my 
> brain sometimes goes off into la-la land without me :-> )  Also, update 
> mode is applicable and makes sense.  But I do still have a problem with 
> the index mode, though it could be compiler magic.  Here's why: the index 
> mode of the 'lwz' instruction requires three operands, i.e., 'lwzx 
> rD,rA,rB', and there's only place holders for two operands.  Is the 
> compiler smart enough to add the third operand for the index mode?  If so, 
> what does it put for the third operand?
>
> Another question is how does the compiler know which mode to pick?  And 
> what is the significance of the trailing number?  Some places in the code 
> have %U1%X1 and others have %U2%X2?  I've found documentation for the # 
> and ## tokens, but I can't find anything for the %U or %X tokens.
>
> Now this has all been very interesting to learn but doesn't solve my 
> underlying problem which I've finally drilled down to.  At first I thought 
> in_be32() might be broken because I wasn't getting back the value I knew 
> to be in the internal register.  I knew I had the address and the mapping 
> to kernel space correct because I could use in_le32 and get the right 
> value though it was byte swapped.  The value in the register was 
> 0xFFFFFFE7 but what I was getting from in_be32 was 0xFFFFFFFF.  Then I 
> started playing and here's what I found:
>
> Register value  in_be32 value
> 0x12345678              0x1234567
> 0xff345678              0xff345678
> 0xffff5678              0xffff5678
> 0xfffff678              0xfffff678
> 0xfffffe78              0xffffffff
> 0xffffff78              0xffffffff
>
> This isn't an exhastive list but I tried about twenty values and pretty 
> much what I found was that if bits 0 through 22 are set to 1 then in_be32 
> reads 0xffffffff.  I've also tried it at a variety of addresses and the 
> behaviour is the same.  in_le32 works fine as does in_be16.  I've got no 
> ideas as to what may be wrong.  I don't want to just arbitrarily point to 
> that %U1%X1 parameter list, but I get compiler errors if I try to remove 
> them so I can't prove or disprove it and I can't find any documentation on 
> it I can't even form a theroy.  Has anyone ever seen anything like this? 
> Can't anyone suggest anything I can try?
>   
Are you sure that all the bits are configured as GPIO? Almost every 
peripheral pin on these chips is muxed at least two ways. Maybe some of 
the bits that you think are GPIO are configured as some other bus, 
possibly explaining goofy behavior and perceived volatility of data. 
This is pretty well-vetted code on a reasonably mature product, and in 
my experience it's very rarely the tools ...

Just a thought.

regards,
Ben

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Endian problem when accessing internel regs on 8347
  2008-01-21  7:21       ` Bruce_Leonard
  2008-01-21 15:16         ` Ben Warren
@ 2008-01-21 16:40         ` Scott Wood
  2008-01-22  5:33           ` Bruce_Leonard
  1 sibling, 1 reply; 11+ messages in thread
From: Scott Wood @ 2008-01-21 16:40 UTC (permalink / raw)
  To: Bruce_Leonard; +Cc: linuxppc-embedded

On Sun, Jan 20, 2008 at 11:21:48PM -0800, Bruce_Leonard@selinc.com wrote:
> Okay, after much digging and experimenting, I'll agree that the memory 
> operand makes sense.  When I first said that I was thinking of direct 
> manipulation of memory, which the PPC doesn't support.  (Don't ask, my 
> brain sometimes goes off into la-la land without me :-> )  Also, update 
> mode is applicable and makes sense.  But I do still have a problem with 
> the index mode, though it could be compiler magic.  Here's why: the index 
> mode of the 'lwz' instruction requires three operands, i.e., 'lwzx 
> rD,rA,rB', and there's only place holders for two operands.  Is the 
> compiler smart enough to add the third operand for the index mode?  If so, 
> what does it put for the third operand?

When the compiler decides to use index mode, the second "operand" is a
string containing "rX, rY", just as when it decides not to, the second
operand is a string containing "offset(rX)".

> Another question is how does the compiler know which mode to pick?

The same way it decides which mode to use for internally generated loads
and stores.  Compiler optimization implementation is beyond the scope of
this list. :-)

> And what is the significance of the trailing number?  Some places in
> the code have %U1%X1 and others have %U2%X2?  I've found documentation
> for the # and ## tokens, but I can't find anything for the %U or %X
> tokens.

The number is an index into the operand list at the end of the asm
statement.

> Now this has all been very interesting to learn but doesn't solve my 
> underlying problem which I've finally drilled down to.  At first I thought 
> in_be32() might be broken because I wasn't getting back the value I knew 
> to be in the internal register.  I knew I had the address and the mapping 
> to kernel space correct because I could use in_le32 and get the right 
> value though it was byte swapped.

Are you absolutely sure that in_le32 to in_be32 is the only thing that
changed?  If you change it back now, does it resume returning a byte-swap
of the correct value?

If that is indeed what is making the difference, then perhaps it's some
subtle timing (or memory corruption) difference caused by different code
generation because the compiler is forced to use index mode for in_le32
(though it appears that the same operand list is given to GCC in either
case -- is GCC smart enough to optimize away preperation of inputs that
aren't actually referenced in the asm statement?).  Is there any
difference in the generated assembly besides the specific load
instruction used?

Alternatively, maybe your chip is just fried. :-)

> I don't want to just arbitrarily point to that %U1%X1 parameter list,

So please don't.

> but I get compiler errors if I try to remove them 

That's why it's there. :-)

> Can't anyone suggest anything I can try?

Beer.

-Scott

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Endian problem when accessing internel regs on 8347
  2008-01-21 16:40         ` Scott Wood
@ 2008-01-22  5:33           ` Bruce_Leonard
  0 siblings, 0 replies; 11+ messages in thread
From: Bruce_Leonard @ 2008-01-22  5:33 UTC (permalink / raw)
  To: Scott Wood; +Cc: linuxppc-embedded

> > Can't anyone suggest anything I can try?
> 
> Beer.
> 
> -Scott

Right there with you Scott!  I have to apologize for the noise on the 
list.  Turns out my problem doesn't have anything to do with in_be32.  I 
didn't have print statements in enough places.  I was only printing out 
what was returned from the ioctl call in user space and assumed (we know 
what that means ;)  ) that what was coming back from the ioctl call was 
what was coming back from io_read32.  With better instrumenting I find 
that io_read32 is just fine.  Apparently my return value from the ioctl 
function in my driver is getting corrupted by the time it gets to my user 
space app.  I don't have an answer for that one yet, but now that I'm 
turned in the correct direction hopefully it'll fall out pretty quickly. 
Thanks for the patience.

See 'ya!

Bruce

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2008-01-22  5:34 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-01-17  6:26 Endian problem when accessing internel regs on 8347 Bruce_Leonard
2008-01-17 15:01 ` Ben Warren
2008-01-17 18:01   ` Bruce_Leonard
2008-01-17 18:25     ` Ben Warren
2008-01-17 17:39 ` Scott Wood
2008-01-17 18:38   ` Bruce_Leonard
2008-01-17 22:18     ` Scott Wood
2008-01-21  7:21       ` Bruce_Leonard
2008-01-21 15:16         ` Ben Warren
2008-01-21 16:40         ` Scott Wood
2008-01-22  5:33           ` Bruce_Leonard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).