/******************************************************************************* HWDD Support FPGA module Copyright (c) 2011 Network Appliance, Inc. hwdd_support_fpga.c: initialization and support fpga specific support routines ********************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hwdd_support_fpga.h" #include "hwdd_sfpga_common.h" #define DRV_VERSION HWDD_SUPPORT_FPGA_VERSION #define HWDD_DEVNAME "hwdd_support_fpga" //#define IOREMAP_PHY_ADDR 0xF8000000 //0xC0000000 // get it runtime thru SPAR registers #define IOREMAP_SIZE 0x3FFF #define HWD_THRESHOLD 40 #define PASS 0 #define SCRATCH_PAD_SIZE_IN_BYTE 16384UL #define NUM_PORTS 1 /* 1 port - port 80 only! */ #define HWDD_SFPGA_PORT80 "hwdd_sfpga_port80" dev_t hwddsfpga_dev; struct cdev *sfpga_cdev ; static int hwdd_support_fpga_major = 0; static void *ioremap_ptr = NULL; static unsigned long scpr_addr; static unsigned int scpr_mem_region; static unsigned short sfpga_port80_base = 0x80; // port 80h static __u32 spad_mem_data_bus_test(void); static __u32 spad_mem_addr_bus_test(void); static __u32 spad_mem_dev_test(void); static __u32 spad_mem_dev_test2(void); static __u32 spad_mem_dev_test3(void); static __u32 spad_mem_data_bus_test(void) { unsigned char data_pat; //walk with 1's @ spad base addr for (data_pat = 1; data_pat != 0; data_pat <<= 1) { writeb(data_pat, ioremap_ptr + 0x0); // Read back //printk(KERN_INFO "ioremap_ptr.value = 0x%02x, data-pattern = 0x%02x\n", readb(ioremap_ptr + 0x0), data_pat); if ( (unsigned char)(readb(ioremap_ptr + 0x0)) != data_pat) return ((__u32)data_pat); //defect! } //return (0); return ((__u32) (0xFFFFFFFF) ); // success (scratchpad can never be this BIG, this is to accomodate the case of fault at 0x0 location also if any by not returning 0 or null on success) } static __u32 spad_mem_addr_bus_test(void) { unsigned long loc =0, testloc=0; unsigned char test_pat = (unsigned char) 0xAAAAAAAA, test_anti_pat; test_anti_pat = (unsigned char) (~test_pat); //Write at pow-of-2 locations. for (loc = sizeof(unsigned char); (loc & (SCRATCH_PAD_SIZE_IN_BYTE-1)) != 0; loc <<= 1) writeb(test_pat, ioremap_ptr + loc); // write anti_pat at loc=0 and see if any addr bits stuck high writeb(test_anti_pat, ioremap_ptr + 0x0); //check for previous writes at loc=1 onwards for (loc = sizeof(unsigned char); (loc & (SCRATCH_PAD_SIZE_IN_BYTE-1)) != 0; loc <<= 1) { //printk(KERN_INFO "ioremap_ptr.value[ %d ] = 0x%02x, test-pattern = 0x%02x\n", loc, readb(ioremap_ptr + loc), test_pat); if ( (unsigned char)(readb(ioremap_ptr + loc)) != test_pat) { //return ((unsigned char *) (ioremap_ptr + loc) ); return ((__u32) (loc) ); // return the actual address of the scratchpad (and not the logical) } } // write pat at loc=0 writeb(test_pat, ioremap_ptr + 0x0); //and see if any addr bits stuck low/shorted for (testloc = sizeof(unsigned char); (testloc & (SCRATCH_PAD_SIZE_IN_BYTE-1)) != 0; testloc <<= 1) { writeb(test_anti_pat, ioremap_ptr + testloc); for (loc = sizeof(unsigned char); (loc & (SCRATCH_PAD_SIZE_IN_BYTE-1)) != 0; loc <<= 1) { if (loc != testloc) { //printk(KERN_INFO "ioremap_ptr.value[ %d ] = 0x%02x, test-pattern = 0x%02x\n", loc, readb(ioremap_ptr + loc), test_pat); if ( (unsigned char)(readb(ioremap_ptr + loc)) != test_pat) { //return ((unsigned char *) (ioremap_ptr + testloc) ); return ((__u32) (testloc) ); // return the actual address of the scratchpad (and not the logical) } } } writeb(test_pat, ioremap_ptr + testloc); } //return NULL; return ((__u32) (0xFFFFFFFF) ); // success (scratchpad can never be this BIG, this is to accomodate the case of fault at 0x0 location also if any by not returning 0 or null on success) } /* -Increment test for the entire Scratchpad region. -Every bit in the region will be tested as 0 and 1. -Data-pattern changes with the change in address. */ static __u32 spad_mem_dev_test(void) { unsigned long loc = 0, max; unsigned char test_pat, test_anti_pat; max = SCRATCH_PAD_SIZE_IN_BYTE / sizeof(unsigned char); //fill the mem for (test_pat = 1, loc = 0; loc < max; test_pat++, loc++) { writeb(test_pat, ioremap_ptr + loc); } //Check & invert for (test_pat = 1, loc = 0; loc < max; test_pat++, loc++) { if ( (unsigned char)(readb(ioremap_ptr + loc)) != test_pat) { //return ((unsigned char *) (ioremap_ptr + loc) ); return ((__u32) (loc) ); // return the actual address of the scratchpad (and not the logical) } test_anti_pat = ~test_pat; writeb(test_anti_pat, ioremap_ptr + loc); } //Check and 0xFF it. for (test_pat = 1, loc = 0; loc < max; test_pat++, loc++) { test_anti_pat = ~test_pat; if ( (unsigned char)(readb(ioremap_ptr + loc)) != test_anti_pat) { //return ((unsigned char *) (ioremap_ptr + loc) ); return ((__u32) (loc) ); // return the actual address of the scratchpad (and not the logical) } writeb(0xFF, ioremap_ptr + loc); } return ((__u32) (0xFFFFFFFF) ); // success (scratchpad can never be this BIG, this is to accomodate the case of fault at 0x0 location also if any by not returning 0 or null on success) } /* --> Change-many-bits-at-once (test for large no. of bits change at once) loop for 16384 times (this is in itself a stress test) Wr 0xffff_ffff at 0x0000 Wr 0xffff at 0xffff Rd them Wr 0x0000 at 0x0000 Wr 0x0000 at 0xffff Rd them end */ static __u32 spad_mem_dev_test2(void) { unsigned long loc = 0, max; // cnt_lo_addr =0, cnt_hi_addr =0; //unsigned char test_pat, test_anti_pat; max = SCRATCH_PAD_SIZE_IN_BYTE / sizeof(unsigned char); for (loc = 0; loc < max; loc++) // loop for 16384 times (this is in itself a stress test) { writeb(0xFF, ioremap_ptr + 0x0); writeb(0xFF, ioremap_ptr + (max-1)); if ( (unsigned char)(readb(ioremap_ptr + 0x0)) != 0xFF) { //return ((unsigned char *) (ioremap_ptr + 0x0) ); //cnt_lo_addr++; // do we want to return from here? return ((__u32) (0x0) ); } if ( (unsigned char)(readb(ioremap_ptr + (max-1))) != 0xFF) { //return ((unsigned char *) (ioremap_ptr + (max-1)) ); //cnt_hi_addr++; return ((__u32) (max-1) ); } writeb(0x00, ioremap_ptr + 0x0); writeb(0x00, ioremap_ptr + (max-1)); if ( (unsigned char)(readb(ioremap_ptr + 0x0)) != 0x00) { //return ((unsigned char *) (ioremap_ptr + 0x0) ); //cnt_lo_addr++;// do we want to return from here? return ((__u32) (0x0) ); } if ( (unsigned char)(readb(ioremap_ptr + (max-1))) != 0x00) { //return ((unsigned char *) (ioremap_ptr + (max-1)) ); //cnt_hi_addr++;// return ((__u32) (max-1) ); } } return ((__u32) (0xFFFFFFFF) ); // success (scratchpad can never be this BIG, this is to accomodate the case of fault at 0x0 location also if any by not returning 0 or null on success) } /* --> MARCH-X test: loop (loop may not be req. here) Wr 0 with up addressing order Rd 0 and Wr 1 with up addressing order Rd 1 and Wr 0 with down addressing order Rd 0 with down addressing order end */ static __u32 spad_mem_dev_test3(void) { unsigned long loc = 0, max; //, cnt1 =0, cnt2 =0; unsigned char test_pat, test_anti_pat; max = SCRATCH_PAD_SIZE_IN_BYTE / sizeof(unsigned char); //fill the mem (write 0) for (test_pat = 0, loc = 0; loc < max; loc++) { writeb(test_pat, ioremap_ptr + loc); } //Check & write 1 for (test_pat = 0, loc = 0; loc < max; loc++) { if ( (unsigned char)(readb(ioremap_ptr + loc)) != test_pat) { //return ((unsigned char *) (ioremap_ptr + loc) ); return ((__u32) (loc) ); // return the actual address of the scratchpad (and not the logical) } test_anti_pat = ~test_pat; writeb(test_anti_pat, ioremap_ptr + loc); } //Down addressing, Check it and write 0. for (test_pat = 0, loc = max-1; loc >0; loc--) { test_anti_pat = ~test_pat; if ( (unsigned char)(readb(ioremap_ptr + loc)) != test_anti_pat) { //return ((unsigned char *) (ioremap_ptr + loc) ); return ((__u32) (loc) ); // return the actual address of the scratchpad (and not the logical) } writeb(test_pat, ioremap_ptr + loc); } //Down addressing, Check it. for (test_pat = 0, loc = max-1; loc >0; loc--) { if ( (unsigned char)(readb(ioremap_ptr + loc)) != test_pat) { //return ((unsigned char *) (ioremap_ptr + loc) ); return ((__u32) (loc) ); // return the actual address of the scratchpad (and not the logical) } } return ((__u32) (0xFFFFFFFF) ); // success (scratchpad can never be this BIG, this is to accomodate the case of fault at 0x0 location also if any by not returning 0 or null on success) } /* * hwdd_support_fpga_open */ static int hwdd_support_fpga_open(struct inode *inode, struct file *fp) { return PASS; } /* * hwdd_support_fpga_close */ static int hwdd_support_fpga_close(struct inode *inode, struct file *fp) { return PASS; } /* * hwdd_support_fpga_ioctl :- ioctl interface */ static long hwdd_support_fpga_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { if (_IOC_TYPE(cmd) != HDD_SUPPORT_FPGA_MAGIC) { printk(KERN_ERR "HWDD: bad ioctl !\n"); return -EINVAL; } switch (cmd) { case HDD_SCRATCH_PAD_DUMP: { int local_var; __u32 *tmp_values; _cmd_val cmd_val_info; if(copy_from_user(&cmd_val_info, (_cmd_val *) arg, sizeof(_cmd_val))) { printk(KERN_ERR "HWDD: copy_from_user failed !\n"); return -EFAULT; } //printk(KERN_INFO "cmd_val_info.size = 0x%02x\n", cmd_val_info.size); //printk(KERN_INFO "cmd_val_info.start = 0x%02x\n", cmd_val_info.start); tmp_values = (__u32 *)kmalloc(cmd_val_info.size * sizeof(__u32), GFP_KERNEL); if(tmp_values == NULL) { printk(KERN_ERR "HWDD: Memory allocation for size of scratch pad memory size requested is failed\n"); return -ENOMEM; } if(copy_from_user(tmp_values, (__u32 *)cmd_val_info.val.value, cmd_val_info.size * sizeof(__u32))) { printk(KERN_ERR "HWDD: copy_from_user failed !\n"); kfree(tmp_values); return -EFAULT; } for (local_var = 0; local_var < cmd_val_info.size ; local_var++) { tmp_values[local_var] = readb(ioremap_ptr + (cmd_val_info.start + local_var)); } if(copy_to_user( (__u32*) cmd_val_info.val.value, tmp_values, cmd_val_info.size * sizeof(__u32))) { printk(KERN_ERR "HWDD: copy_to_user failed !\n"); kfree(tmp_values); return -EFAULT; } kfree(tmp_values); break; } case HDD_DATA_BUS_TEST: { __u32 ret; __u32 *tmp_value; _cmd_val cmd_val_info; if(copy_from_user(&cmd_val_info, (_cmd_val *) arg, sizeof(_cmd_val))) { printk(KERN_ERR "HWDD: data bus: copy_from_user failed !\n"); return -EFAULT; } //printk(KERN_INFO "data bus cmd_val_info.size = 0x%02x\n", cmd_val_info.size); //printk(KERN_INFO "cmd_val_info.start = 0x%02x\n", cmd_val_info.start); tmp_value = (__u32 *)kmalloc(cmd_val_info.size * sizeof(__u32), GFP_KERNEL); if(tmp_value == NULL) { printk(KERN_ERR "HWDD: Memory allocation for size of scratch pad memory size requested is failed\n"); return -ENOMEM; } if(copy_from_user(tmp_value, (__u32 *)cmd_val_info.val.value, cmd_val_info.size * sizeof(__u32))) { printk(KERN_ERR "HWDD: data bus copy_from_user2 failed !\n"); kfree(tmp_value); return -EFAULT; } ret = spad_mem_data_bus_test(); *tmp_value = ret; if (ret == 0xFFFFFFFF) printk (KERN_INFO "HWDD: scratchpad data bus test passed\n"); else { printk (KERN_INFO "HWDD: scratchpad data bus test failed at pattern: 0x%X\n", (unsigned char)*tmp_value); } //printk(KERN_INFO "temp_value = 0x%02x\n", *tmp_value); if(copy_to_user( (__u32*) cmd_val_info.val.value, tmp_value, cmd_val_info.size * sizeof(__u32))) { printk(KERN_ERR "HWDD: copy_to_user failed !\n"); kfree(tmp_value); return -EFAULT; } kfree(tmp_value); break; } case HDD_ADDR_BUS_TEST: { __u32 ret; __u32 *tmp_value; _cmd_val cmd_val_info; if(copy_from_user(&cmd_val_info, (_cmd_val *) arg, sizeof(_cmd_val))) { printk(KERN_ERR "HWDD: addr bus: copy_from_user failed !\n"); return -EFAULT; } //printk(KERN_INFO "addr bus cmd_val_info.size = 0x%02x\n", cmd_val_info.size); //printk(KERN_INFO "cmd_val_info.start = 0x%02x\n", cmd_val_info.start); tmp_value = (__u32 *)kmalloc(cmd_val_info.size * sizeof(__u32), GFP_KERNEL); if(tmp_value == NULL) { printk(KERN_ERR "HWDD: Memory allocation for size of scratch pad memory size requested is failed\n"); return -ENOMEM; } if(copy_from_user(tmp_value, (__u32 *)cmd_val_info.val.value, cmd_val_info.size * sizeof(__u32))) { printk(KERN_ERR "HWDD: addr bus copy_from_user2 failed !\n"); kfree(tmp_value); return -EFAULT; } ret = spad_mem_addr_bus_test(); if (ret == 0xFFFFFFFF) { printk (KERN_INFO "HWDD: scratchpad addr test passed\n"); tmp_value[0] = 0x0; tmp_value[1] = 0x0; } else { tmp_value[0] = 0x1; tmp_value[1] = ret; //(uintptr_t)ret; printk (KERN_INFO "HWDD: scratchpad addr test failed at: 0x%02X\n", tmp_value[1]); } //printk(KERN_INFO "temp_value0 = 0x%02X, temp_value1 = 0x%02X\n", tmp_value[0], tmp_value[1]); if(copy_to_user( (__u32*) cmd_val_info.val.value, tmp_value, cmd_val_info.size * sizeof(__u32))) { printk(KERN_ERR "HWDD: addr copy_to_user failed !\n"); kfree(tmp_value); return -EFAULT; } kfree(tmp_value); break; } case HDD_DEV_TEST: { __u32 ret; __u32 *tmp_value; _cmd_val cmd_val_info; if(copy_from_user(&cmd_val_info, (_cmd_val *) arg, sizeof(_cmd_val))) { printk(KERN_ERR "HWDD: addr bus: copy_from_user failed !\n"); return -EFAULT; } //printk(KERN_INFO "dev bus test cmd_val_info.size = 0x%02x\n", cmd_val_info.size); //printk(KERN_INFO "cmd_val_info.start = 0x%02x\n", cmd_val_info.start); tmp_value = (__u32 *)kmalloc(cmd_val_info.size * sizeof(__u32), GFP_KERNEL); if(tmp_value == NULL) { printk(KERN_ERR "HWDD: Memory allocation for size of scratch pad memory size requested is failed\n"); return -ENOMEM; } if(copy_from_user(tmp_value, (__u32 *)cmd_val_info.val.value, cmd_val_info.size * sizeof(__u32))) { printk(KERN_ERR "HWDD: addr bus copy_from_user2 failed !\n"); kfree(tmp_value); return -EFAULT; } ret = spad_mem_dev_test(); if (ret == 0xFFFFFFFF) { printk (KERN_INFO "HWDD: scratchpad memory (increment) test passed\n"); tmp_value[0] = 0x0; tmp_value[1] = 0x0; } else { tmp_value[0] = 0x1; tmp_value[1] = ret; //(uintptr_t)ret; printk (KERN_INFO "HWDD: scratchpad memory (increment) test failed at: 0x%02X\n", tmp_value[1]); } ret = spad_mem_dev_test2(); if (ret == 0xFFFFFFFF) { printk (KERN_INFO "HWDD: scratchpad memory (Change-many-bits-at-once) test passed\n"); tmp_value[2] = 0x0; tmp_value[3] = 0x0; } else { tmp_value[2] = 0x1; tmp_value[3] = (__u32)ret; //(uintptr_t)ret; printk (KERN_INFO "HWDD: scratchpad memory (Change-many-bits-at-once) test failed at: 0x%02X\n", tmp_value[3]); } ret = spad_mem_dev_test3(); if (ret == 0xFFFFFFFF) { printk (KERN_INFO "HWDD: scratchpad memory (march-x) test passed\n"); tmp_value[4] = 0x0; tmp_value[5] = 0x0; } else { tmp_value[4] = 0x1; tmp_value[5] = ret; //(uintptr_t)ret; printk (KERN_INFO "HWDD: scratchpad memory (march-x) test failed at: 0x%02X\n", tmp_value[5]); } if(copy_to_user( (__u32*) cmd_val_info.val.value, tmp_value, cmd_val_info.size * sizeof(__u32))) { printk(KERN_ERR "HWDD: addr copy_to_user failed !\n"); kfree(tmp_value); return -EFAULT; } kfree(tmp_value); break; } case HDD_SCRATCH_PAD_WRITE: { __u32 *tmp_values; _cmd_val cmd_val_info; if(copy_from_user(&cmd_val_info, (_cmd_val *) arg, sizeof(_cmd_val))) { printk(KERN_ERR "HWDD: write: copy_from_user failed1 !\n"); return -EFAULT; } tmp_values = (__u32 *)kmalloc(cmd_val_info.size * sizeof(__u32), GFP_KERNEL); if(tmp_values == NULL) { printk(KERN_ERR "HWDD: Memory allocation for size of scratch pad memory size requested is failed\n"); return -ENOMEM; } //printk(KERN_ERR "HWDD: write: cmd_val_info.val.value: %d\n", *(cmd_val_info.val.value)); //printk(KERN_ERR "HWDD: write: cmd_val_info.val.size: %d\n", cmd_val_info.size); if(copy_from_user(tmp_values, (__u32 *)cmd_val_info.val.value, cmd_val_info.size * sizeof(__u32))) { printk(KERN_ERR "HWDD: write: copy_from_user failed2 !\n"); kfree(tmp_values); return -EFAULT; } //printk(KERN_ERR "HWDD: write: value to write: %d\n", *tmp_values); writeb( *(tmp_values), ioremap_ptr + (cmd_val_info.start)); kfree(tmp_values); break; } case HDD_POST_PORT80_WRITE: { unsigned char kbuf; int i=0, j=0; _cmd_port80_val cmd_port80_val_info; if(copy_from_user(&cmd_port80_val_info, (_cmd_port80_val *) arg, sizeof(_cmd_port80_val))) { printk(KERN_ERR "HWDD: write_port80: copy_from_user failed1 !\n"); return -EFAULT; } //printk(KERN_INFO "HWDD: write_port80: cmd_port80_val_info.val.value: %d\n", *(cmd_port80_val_info.pval.value)); //printk(KERN_INFO "HWDD: write_port80: cmd_port80_val_info.size: %d\n", cmd_port80_val_info.size); if(copy_from_user(&kbuf, (unsigned char *)cmd_port80_val_info.pval.value, 1 * sizeof(unsigned char))) { printk(KERN_ERR "HWDD: write_port80: copy_from_user failed2 !\n"); return -EFAULT; } //printk(KERN_INFO "HWDD: write_port80: value to write: %d, %c\n", kbuf, kbuf); if ((cmd_port80_val_info.size) == 1) { outb(kbuf, sfpga_port80_base); // Writing to the port /* enforce non-optimization/non-caching at compiler and CPU level and ensure the completion of the writes in the necessary order, may not require on x86 arch (controller side) since writes (unlike reads) outside the processor are not reordered.*/ //wmb(); // order the write! - check if smp_wmb() is required! ??? } else { for (i = (cmd_port80_val_info.size - 1); i >= 0; i--) { //printk(KERN_INFO "HWDD: write_port80: value to write[ %d ]: %d, %c\n", i, (unsigned char)i, (unsigned char)i); // Writing to the port outb( (unsigned char)i, sfpga_port80_base); /* enforce non-optimization/non-caching at compiler and CPU level and ensure the completion of the writes in the necessary order, may not require on x86 arch (controller side) since writes (unlike reads) outside the processor are not reordered.*/ //wmb(); // order the write! - check if smp_wmb() is required! ??? for (j=0; j<150000; j++) // dummy read - to induce delay - will decrease the loop times (those many ticks not required...) { inb( sfpga_port80_base); } //printk(KERN_INFO "HWDD: write_port80: read value [ %d ]: %d, %c, time=%ld \n", i, inb( sfpga_port80_base), inb( sfpga_port80_base), jiffies ); } } break; } default: printk(KERN_ERR "Unsupported ioctl !\n"); return -EINVAL; } return 0; } /* * HDD Support FPGA, File Operation Structure */ struct file_operations hwddsupportfpga_fops = { .owner = THIS_MODULE, .unlocked_ioctl = hwdd_support_fpga_ioctl, .open = hwdd_support_fpga_open, .release = hwdd_support_fpga_close, }; /* * hwdd_support_fpga_init- HDD Support FPGA, module entry point */ static int __init hwdd_support_fpga_init(void) { int ret; //, i; scpr_mem_region = 0; // no need actually hwddsfpga_dev = MKDEV(hwdd_support_fpga_major, 0); ret = alloc_chrdev_region(&hwddsfpga_dev, 0, 1, HWDD_DEVNAME); if (ret != 0) { printk(KERN_ERR "alloc_chrdev_region failed !!\n"); return ret; } printk(KERN_INFO "HWDD, S-FPGA Module Major Num :%d\n", MAJOR(hwddsfpga_dev)); sfpga_cdev = cdev_alloc(); if (!sfpga_cdev) { printk(KERN_ERR "HWDD, S-FPGA cdev_alloc failed !\n"); unregister_chrdev_region(hwddsfpga_dev, 1); return -1; } sfpga_cdev->owner = THIS_MODULE; sfpga_cdev->ops = &hwddsupportfpga_fops; if ( cdev_add(sfpga_cdev, hwddsfpga_dev, 1) < 0) { printk(KERN_ERR "HWDD, S-FPGA cdev_add failed \n"); //goto fail2; cdev_del(sfpga_cdev); unregister_chrdev_region(hwddsfpga_dev, 1); return -1; } printk(KERN_INFO "HWDD: write_port80: Requesting the IO port region for access at I/O port address 0x%X\n", sfpga_port80_base); if (! request_region(sfpga_port80_base, NUM_PORTS, HWDD_SFPGA_PORT80)) { printk(KERN_INFO "hwdd_sfpga_port80: I/O port 0x%X is reserved by DMA1 engine at bootup, request region may fail, but DMA1 never uses it, hence the port is available!\n", sfpga_port80_base); //return -ENODEV; // not returning } scpr_addr = (unsigned long)(inw(0x0700)); //printk(KERN_INFO "**********ioremap phy SPAR[1..0] = 0x%02x\n", scpr_addr ); scpr_addr = (unsigned long)((unsigned long)(scpr_addr << 8UL) << 8UL); //printk(KERN_INFO "**********ioremap phy = 0x%02x\n", scpr_addr ); if( request_mem_region((unsigned long) scpr_addr, (unsigned long) IOREMAP_SIZE, (char *)"Scratchpad") == NULL ) //if( request_mem_region((unsigned long)0x50000000, (unsigned long) IOREMAP_SIZE, (char *)"SPAD") == NULL ) { //printk(KERN_ALERT "SPAD phy address request error: unable to obtain I/O memory address 0x%02X\n", scpr_addr ); printk(KERN_ERR "request for memory region failed for Scratchpad\n"); //goto fail2;//return -EBUSY; cdev_del(sfpga_cdev); unregister_chrdev_region(hwddsfpga_dev, 1); return -EBUSY; } scpr_mem_region = 1; ioremap_ptr = ioremap_nocache( scpr_addr, IOREMAP_SIZE); if(ioremap_ptr == NULL) { printk(KERN_ERR "SPAD ioremap failed for probably no device\n"); //goto fail2; cdev_del(sfpga_cdev); unregister_chrdev_region(hwddsfpga_dev, 1); return -1; } //printk(KERN_INFO "**********ioremap = 0x%02x\n", ioremap_ptr ); //for (i = 0; i < 20; i++) // printk(KERN_INFO "ioremap[%d] = 0x%02x\n", i, ioread8(ioremap_ptr + i) ); return 0; } /* * hwdd_support_fpga_exit- Driver exit cleanup routine */ static void __exit hwdd_support_fpga_exit(void) { if(ioremap_ptr != NULL) iounmap(ioremap_ptr); //release_region(sfpga_port80_base, NUM_PORTS); if (scpr_mem_region ==1) release_mem_region((unsigned long) scpr_addr, (unsigned long) IOREMAP_SIZE); cdev_del(sfpga_cdev); unregister_chrdev_region(hwddsfpga_dev, 1); } module_init(hwdd_support_fpga_init); module_exit(hwdd_support_fpga_exit); MODULE_AUTHOR("NetApp, "); MODULE_DESCRIPTION("Support FPGA Hardware Diagnostics Driver"); MODULE_LICENSE("GPL"); //MODULE_VERSION(DRV_VERSION);