Re: Custom Driver
From: Grant Likely <hidden>
Date: 2006-01-06 23:03:58
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@secretlab.ca] Sent: Monday, January 02, 2006 10:59 PM To: Brett McNerney Cc: linuxppc-embedded@ozlabs.org Subject: Re: Custom Driver Brett McNerney wrote:quoted
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 supplyCan 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 [off-list ref] */ #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 [off-list ref] * 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 [off-list ref]"); 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