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 */
next prev parent 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 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).