All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ben Warren <biggerbadderben@gmail.com>
To: Bruce_Leonard@selinc.com
Cc: linuxppc-embedded@ozlabs.org
Subject: Re: Endian problem when accessing internel regs on 8347
Date: Thu, 17 Jan 2008 10:01:10 -0500	[thread overview]
Message-ID: <478F6DB6.8050907@gmail.com> (raw)
In-Reply-To: <OF03A8BF8B.4AC46CCD-ON882573D3.00216F5B-882573D3.00236B8D@selinc.com>

[-- 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 */

  reply	other threads:[~2008-01-17 15:01 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-01-17  6:26 Endian problem when accessing internel regs on 8347 Bruce_Leonard
2008-01-17 15:01 ` Ben Warren [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=478F6DB6.8050907@gmail.com \
    --to=biggerbadderben@gmail.com \
    --cc=Bruce_Leonard@selinc.com \
    --cc=linuxppc-embedded@ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.