Custom Driver
Grant Likely
grant.likely at secretlab.ca
Sat Jan 7 10:02:44 EST 2006
First off, please CC: the mailing list when you email me so others
having the same problem can see the response.
Brett McNerney wrote:
> Yes I can access the register from a no-os program. There a Xilinx specific
> functions to read and write to the registers and those work just fine. I
> can run a full test on the register with no problem. Its just when I go to
> use linux I can not access the register anymore.
> The driver attempted to use is attached along with the sample test program
> to run under linux. I added the reg_driver to the Xilinx_gpio makefile.
> Got everything to compile and create the kernel but from there the /dev/reg
> that should be there is not.
Unless you're using devfs (not recommended), /dev/* entries are not
created automatically, you need to create them yourself with mknod.
Are you using a 2.4 or a 2.6 kernel?
It looks like your doing the right thing with ioremap. However, I would
break things up a bit. Before wiring it up to a char device, see if you
can twiddle the GPIO registers from within the driver itself; then try
to get a user space app to twiddle them.
Also, for the GPIO core, I'd dump the Xilinx headers and libraries. The
register access is so simple that you can just read/write them directly.
The xilinx layer just become another layer of abstraction without a
whole lot of benefit.
Cheers,
g.
>
> -----Original Message-----
> From: Grant Likely [mailto:grant.likely at secretlab.ca]
> Sent: Monday, January 02, 2006 10:59 PM
> To: Brett McNerney
> Cc: linuxppc-embedded at ozlabs.org
> Subject: Re: Custom Driver
>
> Brett McNerney wrote:
>
>>I am need to create a driver to interface to custom hardware on a ml403
>>board. I am first trying to just create a ipif register bank and access
>>the registers from linux but am not having any luck of yet. Has anyone
>>successfully done this and if so explain how or supply a driver to do
>>this and how to build it into the kernel? I am new at this and am
>>having great difficutly right now.
>>
>>Thanks for any help anyone can supply
>
> Can you access your device from a debugger, or a no-os program (like a
> bootloader)? Did you call ioremap() to map the physical register
> address into virtual memory? Can you give more detail?
>
> g.
>
>
>
>
> ------------------------------------------------------------------------
>
> /* my driver header file, defines some consts, structs, etc. */
> /* modeled after/simplified from Xilinx's xgpio */
> /* Author: Joey Rios <rios at soe.ucsc.edu> */
>
> #ifndef __IMPROVED_DRIVER_H
> #define __IMPROVED_DRIVER_H
>
> #define REG_IOCTL_BASE 'r'
>
> #define REG_MINOR 23
>
> struct reg_ioctl_data
> {
> int data;
> };
>
> #define REG_IN _IOWR(REG_IOCTL_BASE, 0, struct reg_ioctl_data)
> #define REG_OUT _IOW (REG_IOCTL_BASE, 1, struct reg_ioctl_data)
>
> #endif
>
>
>
> ------------------------------------------------------------------------
>
> /* test_gpio.c
> * This program will test our driver (based on xgpio)
> * Author: Joseph Rios <rios at soe.ucsc.edu>
> * Date: 08/04/05
> *
> */
>
> #include <stdio.h>
> #include <errno.h>
> #include <fcntl.h>
> #include <linux/ioctl.h>
> /* Somehow you need to make sure the program can find this: */
> #include "reg_driver.h"
>
>
> int main()
> {
> int rtn, i;
> int gpio_fd = -1;
> struct reg_ioctl_data it;
> /* Actual value is not important */
> it.data = 812;
>
> /* Opening */
> gpio_fd = open("/dev/modular_reg", O_RDWR);
> if(gpio_fd == -1){
> perror("Couldn't open /dev/reg");
> return 1;
> }
> printf("Got through opening /dev/reg\n");
> printf("Will write value of it.data (%i) to reg.\n", it.data);
>
> /* ioctl test */
>
>
> rtn = ioctl(gpio_fd, REG_OUT, &it);
> /* rtn = 0 means success */
> if(!rtn) printf("Woo hoo! ioctl(REG_OUT) worked!\n");
> else perror("Dang, ioctl(REG_OUT) didn't work");
>
> /* Again, value isn't important, as long as chagned */
> it.data = 100;
> printf("Now changed it.data to %i.\n", it.data);
> printf("Will read reg into it.data to see 'it' change.\n");
>
> printf("it.data = %i before ioctl(REG_IN)\n", it.data);
> rtn = ioctl(gpio_fd, REG_IN, &it);
>
> if(!rtn) printf("Woo hoo! ioctl(REG_IN) worked!\n");
> else perror("Dang, ioctl(REG_IN) didn't work");
>
> printf("it.data = %i after ioctl(REG_IN)\n", it.data);
>
> /* Closing */
> if(close(gpio_fd)) perror("Couldn't close /dev/reg");
> else printf("Closed /dev/reg\n");
>
> return 0;
> }
>
>
>
> ------------------------------------------------------------------------
>
> /* This is a driver for our register IP */
> /* Completely modeled after the xilinx_gpio driver. This
> * driver currently doesn't work as a loadable module for
> * unknown reasons. I think the ioremap does something
> * goofy to it if called at runtime.
> */
>
> #ifndef __KERNEL__
> #define __KERNEL__
> #endif
>
> #include <linux/config.h>
> #include <linux/module.h>
> #include <linux/miscdevice.h>
> #include <linux/kernel.h> /* printk() */
> #include <linux/init.h> /* module_{init, cleanup}() */
> #include <linux/slab.h>
> #include <asm/system.h> /* maybe don't need? */
> #include <xparameters_ml300.h>
> #include <asm/io.h>
> #include <asm/uaccess.h>
> #include <asm/irq.h>
>
> #include <xio.h>
> #include <xbasic_types.h>
> #include "reg_driver.h"
>
> /* A redefinition to cutdown on some typing later */
> #define REG_BASE XPAR_REGISTER_0_BASEADDR
> #define REG_HIGH XPAR_REGISTER_0_HIGHADDR
>
> /* Some module documentation */
> MODULE_LICENSE("GPL");
> MODULE_AUTHOR("Joey Rios <rios at soe.ucsc.edu>");
> MODULE_DESCRIPTION("Driver for a register device");
> MODULE_SUPPORTED_DEVICE("plb_register");
>
>
> /* Global variables needed across methods */
> static u32 reg_remapped_address;
> const static long remap_size = REG_HIGH - REG_BASE + 1;
>
> /* open/close do nothing special. inc/dec use count */
> int reg_open (struct inode *inode, struct file *filp)
> {
> MOD_INC_USE_COUNT;
> return 0;
> }
>
> int reg_release (struct inode *inode, struct file *filp)
> {
> MOD_DEC_USE_COUNT;
> return 0;
> }
>
> /* here is where all the i/o happens. arg is assumed to be a pointer
> * to the data. currently, that data is a struct reg_ioctl_data.
> * this is probably too complicated at the moment and will
> * eventually be just an int, but not sure. filp and inode are not
> * used here.
> */
> static int
> reg_ioctl(struct inode* inode, struct file* filp, unsigned int cmd,
> unsigned long arg)
> {
> int temp;
> struct reg_ioctl_data reg_data;
>
> if(copy_from_user(®_data, (void*) arg, sizeof(reg_data)))
> return -EFAULT;
>
> switch(cmd){
> case REG_IN:
> reg_data.data = XIo_In32(reg_remapped_address);
> if(copy_to_user((struct reg_ioctl_data*)arg, ®_data,
> sizeof(reg_data)))
> return -EFAULT;
> break;
> case REG_OUT:
> temp = reg_data.data;
> XIo_Out32(reg_remapped_address, (u32) temp);
> break;
> default:
> return -ENOIOCTLCMD;
> }
> return 0;
> }
>
> /* The operations to be registered with this driver */
> struct file_operations reg_fops = {
> ioctl: reg_ioctl,
> open: reg_open,
> release:reg_release,
> owner: THIS_MODULE,
> };
>
> /* For registering a misc device */
> static struct miscdevice miscdev={
> minor:REG_MINOR,
> name:"reg",
> fops:®_fops,
> };
>
> /* init will remap the physical address and save the result
> * in reg_remapped_address (global to the driver) then will
> * register the device as a misc device
> */
> static int reg_init_module(void)
> {
> int rtn;
>
> reg_remapped_address = (u32) ioremap(REG_BASE, remap_size);
> printk(KERN_INFO "%s at 0x%08X mapped to 0x%08X\n", miscdev.name,
> REG_BASE, reg_remapped_address);
>
> rtn = misc_register(&miscdev);
> if(rtn)
> {
> printk(KERN_ERR "%s: Could not register driver. \n",
> miscdev.name);
> return rtn;
> }
> return 0;
> }
>
> /* cleanup simply unmaps the address and deregisters the driver */
> static void reg_cleanup_module(void)
> {
> iounmap(reg_remapped_address);
> misc_deregister(&miscdev);
> }
>
> EXPORT_NO_SYMBOLS;
> module_init(reg_init_module);
> module_exit(reg_cleanup_module);
>
--
Grant Likely, B.Sc. P.Eng.
Secret Lab Technologies Ltd.
(403) 663-0761
More information about the Linuxppc-embedded
mailing list