Index: linux-3.12.33-rt47-i386/drivers/uio/uio_scu2.c =================================================================== @ linux-3.12.33-rt47-i386/drivers/uio/uio_scu2.c:4 @ +/* + * uio_scu2.c + * + * Copyright(C) 2008, Thomas Husterer <Thomas.Husterer@heidelberg.com> + * + * Userspace IO driver for HDM-Scu2 Hardwarecomponents + * + * Licensed under the GPLv2 only. + */ + +#define DRV_NAME "uio_scu2" +#define DRV_VERSION "1.0.0" +#define PFX DRV_NAME ": " + + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/uio_driver.h> +//#include "scu2Adr.h" +#include <linux/serial_core.h> +#include <linux/serial_8250.h> + + +//unsigned addrBase[16]; +static char* addrBase_0; +static char* addrBase_7; +static char* addrBase_c; +static char* addrBase_d; +#define SCU2_FPGA_ADR(range,ofs) (addrBase_##range +((ofs)&0xfffff)) +#define SCU2_FPGA_REG16(range,offset) *(unsigned short*)SCU2_FPGA_ADR(range,offset) +#define SCU2_FPGA_REG08(range,offset) *SCU2_FPGA_ADR(range,offset) + +//#define SCU2_SIO1_OFS 0x800000 +#define SCU2_VENDOR_ID 0x10ee +#define SCU2_DEVICE_ID 0x0300 +#define SCU2_FPGA_MEM_SIZE 0x1000000 +#define SCU2_FI_STATUS_REG SCU2_FPGA_REG16(0,0x000000) +#define SCU2_FI_EOI_REG SCU2_FPGA_REG16(0,0x000004) +#define SCU2_LED_REG SCU2_FPGA_REG08(7,0x700002) +#define SCU2_INT_STATUS_REG SCU2_FPGA_REG16(c,0xc00000) +#define SCU2_INT_FRG_REG SCU2_FPGA_REG16(c,0xc00002) +#define SCU2_ID_REG SCU2_FPGA_REG08(d,0xd00002) +#define SCU2_ID_SCU2 0x1 +#define SCU2_ID_SCU2B 0x2 +#define SCU2_ID_SCU2B_BB 0x4 + +//#define SCU2_FPGA_ADR(range,ofs) (addrBase[ofs>>20] +((ofs)&0xfffff)) +#ifndef CONFIG_HDM_SCU2_SIO_8250_PCI +int sioLine; +#endif +unsigned initState=0; +static void uio_scu2_remove(struct pci_dev *dev); + + +#define LED_ACCESS(num) \ +static ssize_t show_led##num(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + int msk = 1<<(num-1); \ + return sprintf(buf, "%c\n", SCU2_LED_REG & msk ? '1' : '0' ); \ +} \ + \ +static ssize_t store_led##num(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + int msk= 1<<(num-1); \ + unsigned char kbuf = buf[0]&0xf; \ + if(count<1) \ + return -EFAULT; \ + if(kbuf){ \ + SCU2_LED_REG |= msk; \ + }else { \ + SCU2_LED_REG &= ~msk; \ + } \ + return count; \ +} \ +static DEVICE_ATTR(scu2_led##num, S_IRUGO | S_IWUGO, show_led##num, store_led##num); +LED_ACCESS(1) +LED_ACCESS(2) +LED_ACCESS(3) +LED_ACCESS(4) + +static ssize_t show_leds(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "0x%02x\n", SCU2_LED_REG ); +} + +static ssize_t store_leds(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if(count<1) + return -EFAULT; + SCU2_LED_REG = simple_strtoul(buf,0,16); + return count; +} +static DEVICE_ATTR(scu2_leds, S_IRUGO | S_IWUGO, show_leds, store_leds); + + + +//extern int nmi_watchdog_counter; +//static ssize_t show_wdg(struct device *dev, +// struct device_attribute *attr, +// char *buf) +//{ +// return sprintf(buf, "%d\n", nmi_watchdog_counter ); +//} +// +//static ssize_t store_wdg(struct device *dev, +// struct device_attribute *attr, +// const char *buf, size_t count) +//{ +// if(count<1) +// return -EFAULT; +// nmi_watchdog_counter = simple_strtoul(buf,0,10); +// return count; +//} +//static DEVICE_ATTR(scu2_wdg, S_IRUGO | S_IWUGO, show_wdg, store_wdg); + + + + +static ssize_t show_id(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const char *idstr="UNKNOWN ID"; + switch(SCU2_ID_REG) + { + case SCU2_ID_SCU2: idstr="SCU2\n"; break; + case SCU2_ID_SCU2B: idstr="SCU2B\n"; break; + case SCU2_ID_SCU2B_BB: idstr="SCU2B_BB\n"; break; + } + return sprintf(buf, "0x%02x %s\n", SCU2_ID_REG, idstr); +} +static DEVICE_ATTR(scu2_id, S_IRUGO, show_id,0); + +static irqreturn_t uio_scu2_handler(int irq, struct uio_info *dev_info) +{ + //char* scu2BaseAdr = dev_info->mem[0].internal_addr; + u16 req=((SCU2_INT_STATUS_REG & 0xff) << 8) | (SCU2_FI_STATUS_REG & 0xff); + + if(!req) /* shared irq? */ + { + return IRQ_NONE; + } + //SCU2_FI_FRG_REG = 0; nicht sperren damit weiterhin alle flanken sichtbar + SCU2_FI_EOI_REG = 0xff; //Fast Input Ints mit EOI beseitigen + SCU2_INT_FRG_REG = 0; //Hier gehts nicht mit EOI weil die Quellen noch anstehen + return IRQ_HANDLED; +/* Fast IO register +Achtung!! see Fast_input_bit.vhd +1. komisches interupt-Sperrverhalten. +2. kein Intrequest Register + + +1. Beim sperren der ints mit SCU2_FI_FRG_REG=0 wird die Int-erkennung halb +ausgeschaltet. Zwischenzeitlich anfallende Ints werden bei der wiederfreigabe +genau dann gemeldet wenn der Pegel vor der Sperre ungleich dem Pegel hinter der +Sperre ist. +T 1 2 3 3 5 6 7 8 9 +INPUT_REG --______---- -----____---------- ---____--_______ + v v v v +FRG_REG ------------ -------______--- -----______- + v v v v v v v kein int +STATUS_REG __i--__i--__ _____i-______i---__ ___i-___________ + ^ ^ ^ +EOI_REG _____-____-_ _ __-_ + +Im Bsp. wird also zum Zeitpunkt 5 ein Int ausgeloest, zum Zeitpunkt 9 +jedoch nicht, weil der Pegel vor der Int-Sperre einmal ungleich (T=5) und beim +zweiten Mal identisch (T=9) mit dem aktuellen Pegel ist. + +Dies fuehrt im schlimmsten Fall dazu, dass die Software auf dem falschen Pegel +festklemmt. Im Bsp. erkennt die Pollingloop die Pegel bei T=6 u. T=7, aber +T=8 kommt ausgerechnet in dem Zeitraum zwischen Pollingloop Ende und +Int-Freigabe. +Abhilfe: FRG_REG nicht verwenden. (Lediglich zur Komplettsperre) + + +2. Das STATUS_REG ist nur dann verfuegbar wenn das FRG_REG=1 und solange noch +kein EOI ausgeloest wurde. +*/ +} + +static int uio_scu2_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int ret = -ENODEV; + struct uio_info *info=0; + resource_size_t mmio_start; //resource_size_t = phys_addr_t = u64/u32 + //unsigned long mmio_end; + unsigned long mmio_flags; + unsigned long mmio_len; +#ifndef CONFIG_HDM_SCU2_SIO_8250_PCI + struct uart_8250_port serial_port; +#endif + printk(KERN_INFO PFX "probing...\n"); + + if(MAX_UIO_MAPS<16 ) + { + printk(KERN_ERR PFX "less than 16 UIO_MAPS\n"); + goto out_err; + } + + info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); + if (!info){ + printk(KERN_ERR PFX "kzalloc failed\n"); + ret = -ENOMEM; + goto out_err; + } + pci_set_drvdata(dev, info); + initState|=1; + + if (pci_enable_device(dev)) + { + printk(KERN_ERR PFX "device already enabled .. ignoring\n"); +#ifndef CONFIG_HDM_SCU2_SIO_8250_PCI + goto out_err; +#endif + }else{ + initState|=2; + } + + info->name = DRV_NAME; + info->version = DRV_VERSION; + info->irq = dev->irq; //veraendert durch pci_enable_device + // info->irq_flags = IRQF_DISABLED | IRQF_SHARED; + info->irq_flags = IRQF_SHARED; + info->handler = uio_scu2_handler; + + + + mmio_start = pci_resource_start(dev, 0); + //mmio_end = pci_resource_end(dev, 0); + mmio_flags = pci_resource_flags(dev, 0); + mmio_len = pci_resource_len(dev, 0); + + if (!mmio_start){ + printk(KERN_ERR PFX "mmio_start==0\n"); + goto out_err; + } + + if (!(mmio_flags & IORESOURCE_MEM)) { + printk(KERN_ERR PFX "region #0 not an MMIO resource, aborting\n"); + goto out_err; + } + if (mmio_len != SCU2_FPGA_MEM_SIZE) { + printk(KERN_ERR PFX "Invalid PCI mem region size(s), aborting %lx!=%x\n",mmio_len,SCU2_FPGA_MEM_SIZE); + goto out_err; + } + + initState|=4; + + {//dummy block + int k; + struct Tab{unsigned ofs,size;char**pBase;}; + struct Tab sizes[]={ + { 0x000000, 0x1000, &addrBase_0 },// 10 + { 0x100000, 0x1000, 0 },// 4 + { 0x200000, 0x4000, 0 },// 0x4000 + { 0x300000, 0x1000, 0 },// 0x80 + { 0x400000, 0x1000, 0 },// 0x80 + //0x500000, 0, 0, + //0x600000, 0, 0, + { 0x700000, 0x1000, &addrBase_7 },// 4 + //0x800000, 0, 0,//sioA printf serial8250_register_port + { 0x900000, 0x1000, 0 },// 8 + { 0xa00000,0x10000, 0 },//0x10000 + { 0xb00000, 0x1000, 0 },// 0x1e + { 0xc00000, 0x1000, &addrBase_c },// 6 + { 0xd00000, 0x1000, &addrBase_d },// ,6 + { 0xe00000, 0x4000, 0 } // 0x4000 + //0xf00000, 0, 0 + }; + + for( k=0; k < sizeof(sizes)/sizeof(sizes[0]); k++) + { + unsigned ofs = sizes[k].ofs ;//i<<20; + unsigned size = sizes[k].size; + resource_size_t addr = mmio_start + ofs; + char **pBase = sizes[k].pBase; //(unsigned*)sizes[k*3+2]; + struct uio_mem *mem = &info->mem[k]; + BUG_ON(size == 0); + + //printk(KERN_INFO PFX "region %d %08x %06x\n",k,mem->addr,mem->size); + + if (!request_mem_region(addr, size,"scu2")) + { + printk(KERN_INFO PFX "pci_request_region %d not allowed \n",k); + goto out_err; + } + mem->addr = addr; + mem->size = size; + mem->memtype = UIO_MEM_PHYS; + + if(pBase){ + mem->internal_addr = ioremap(mem->addr,size); + *pBase = mem->internal_addr; + if (!*pBase) + { + printk(KERN_ERR PFX "bad ioremap %d\n",k); + goto out_err; + } + } + } + } + if (uio_register_device(&dev->dev, info)) { + printk(KERN_ERR PFX "bad uio_register_device\n"); + goto out_err; + } + initState|=8; + + +#ifndef CONFIG_HDM_SCU2_SIO_8250_PCI + //see drivers/serial/8250_pci.c:1651 + memset(&serial_port, 0, sizeof(struct uart_8250_port)); + serial_port.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | + UPF_SHARE_IRQ | UPF_IOREMAP; + serial_port.port.uartclk = 230400*16;//board->base_baud * 16; + serial_port.port.irq = 0;//get_pci_irq(dev, board); + serial_port.port.dev = &dev->dev; + + //see drivers/serial/8250_pci.c:94 (setup_port) + serial_port.port.iotype = UPIO_MEM; + serial_port.port.iobase = 0; + serial_port.port.mapbase = mmio_start+0x800000;//base + offset; + serial_port.port.regshift = 0; + //serial_port.membase = scu2BaseAdr+0x800000;// serial8250_request_std_resource macht das selbst UPF_IOREMAP + + sioLine=serial8250_register_8250_port(&serial_port); + if (sioLine < 0) { + printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), sioLine); + goto out_err; + } + initState|=0x10; +#endif + + + + ///home/husteret/build/SfcLeanOs-RT/trunk/Src/kernel/linux-2.6.22.1-rt9-lttng/include/linux/pci.h + // class_create_file(&dev->bus->class_dev, &dev_attr_led1); + device_create_file(&dev->dev, &dev_attr_scu2_led1); + device_create_file(&dev->dev, &dev_attr_scu2_led2); + device_create_file(&dev->dev, &dev_attr_scu2_led3); + device_create_file(&dev->dev, &dev_attr_scu2_led4); + device_create_file(&dev->dev, &dev_attr_scu2_leds); + //device_create_file(&dev->dev, &dev_attr_scu2_wdg); + device_create_file(&dev->dev, &dev_attr_scu2_id); + initState|=0x20; + + printk(KERN_INFO PFX "successfully probed. adr=%p size=0x%lx irq=%ld\n" + ,(void*)info->mem[0].addr + ,info->mem[0].size + ,info->irq + ); + return 0; + + out_err: + uio_scu2_remove(dev);//uio_scu2_cleanup(dev,info); + printk(KERN_ERR PFX "load error\n"); + return ret; +} +static void uio_scu2_remove(struct pci_dev *dev) +{ + struct uio_info *info = pci_get_drvdata(dev); + if(initState & 0x20) + { + device_remove_file(&dev->dev, &dev_attr_scu2_led1); + device_remove_file(&dev->dev, &dev_attr_scu2_led2); + device_remove_file(&dev->dev, &dev_attr_scu2_led3); + device_remove_file(&dev->dev, &dev_attr_scu2_led4); + device_remove_file(&dev->dev, &dev_attr_scu2_leds); + //device_remove_file(&dev->dev, &dev_attr_scu2_wdg); + device_remove_file(&dev->dev, &dev_attr_scu2_id); + } + +#ifndef CONFIG_HDM_SCU2_SIO_8250_PCI + if(initState & 0x10) serial8250_unregister_port(sioLine); +#endif + if(initState & 0x8) uio_unregister_device(info); + if(initState & 0x4) + { + int k; + for( k=0; k < MAX_UIO_MAPS; k++) + { + if(info->mem[k].addr) release_mem_region(info->mem[k].addr, + info->mem[k].size); + if(info->mem[k].internal_addr) iounmap(info->mem[k].internal_addr); + } + } + if(initState & 0x2) pci_disable_device(dev); + if(initState & 0x1) pci_set_drvdata(dev, NULL); + if(initState & 0x1) kfree(info); +} + +// static void uio_scu2_remove(struct pci_dev *dev) +// { +// struct uio_info *info = pci_get_drvdata(dev); +// uio_scu2_cleanup(dev,info); +// +// } + +static struct pci_device_id scu2_pci_tbl[] = {// + { + .vendor = SCU2_VENDOR_ID, + .device = SCU2_DEVICE_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + {0,} +}; +//MODULE_DEVICE_TABLE (pci, scu2_pci_tbl); +#ifndef CONFIG_HDM_SCU2_SIO_8250_PCI +static struct pci_driver uio_scu2_driver = { + .name = DRV_NAME, + .id_table = scu2_pci_tbl, + .probe = uio_scu2_probe, + .remove = uio_scu2_remove, +}; +#endif + + + +/* + * Main initialization/remove routines + */ +extern void (*g_timer_int_callback)(void); + +struct pci_dev *pciDev = NULL; +static int __init uio_scu2_init_module(void) +{ + printk (KERN_INFO DRV_NAME "loading..\n"); +#ifndef CONFIG_HDM_SCU2_SIO_8250_PCI + return pci_register_driver(&uio_scu2_driver); +#else + { + if((pciDev = pci_get_device(SCU2_VENDOR_ID, SCU2_DEVICE_ID, 0))) + { + printk (KERN_INFO DRV_NAME "found scu2 hardware..\n"); + return uio_scu2_probe(pciDev,&scu2_pci_tbl[0]); + } + } + return -ENODEV; +#endif + +} + +static void __exit uio_scu2_exit_module(void) +{ +#ifndef CONFIG_HDM_SCU2_SIO_8250_PCI + pci_unregister_driver(&uio_scu2_driver); +#else + if(pciDev) + { + printk (KERN_INFO DRV_NAME "release pci-device..\n"); + uio_scu2_remove(pciDev); + pci_dev_put(pciDev); + } +#endif + printk(KERN_INFO PFX "successfully unloaded.\n"); +} + +module_init(uio_scu2_init_module); +module_exit(uio_scu2_exit_module); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Husterer"); +MODULE_DESCRIPTION("UIO scu2 driver"); Index: linux-3.12.33-rt47-i386/drivers/uio/uio_scu3.c =================================================================== --- /dev/null +++ linux-3.12.33-rt47-i386/drivers/uio/uio_scu3.c @ linux-3.12.33-rt47-i386/drivers/uio/uio_scu2.c:4 @ +/* + * uio_scu3.c + * + * Copyright(C) 2008, Thomas Husterer <Thomas.Husterer@heidelberg.com> + * + * Userspace IO driver for HDM-Scu3 Hardwarecomponents + * + * Licensed under the GPLv2 only. + */ + +#define DRV_NAME "uio_scu3" +#define DRV_VERSION "2.1.0" +#define PFX DRV_NAME ": " + + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/uio_driver.h> +#include <linux/serial_core.h> +#include <linux/serial_8250.h> + + +static char* addrBase_7; +static char* addrBase_c; +static char* addrBase_d; +#define SCU3_FPGA_ADR(range,ofs) (addrBase_##range +((ofs)&0xfffff)) +#define SCU3_FPGA_REG16(range,offset) *(unsigned short*)SCU3_FPGA_ADR(range,offset) +#define SCU3_FPGA_REG08(range,offset) *(unsigned char*)SCU3_FPGA_ADR(range,offset) + +#define PCI_VENDOR_ID_SCU3 0x163d +#define SCU3_DEVICE_ID 0x1961 +#define SCU3_FPGA_MEM_SIZE 0x1000000 +#define SCU3_LED_REG SCU3_FPGA_REG08(7,0x700002) +#define SCU3_INT_STATUS_REG SCU3_FPGA_REG16(c,0xc00000) +#define SCU3_INT_FRG_REG SCU3_FPGA_REG16(c,0xc00002) +#define SCU3_ID_REG SCU3_FPGA_REG08(d,0xd00002) +#define SCU3_ID_SCU2 0x1 +#define SCU3_ID_SCU2B 0x2 +#define SCU3_ID_SCU2B_BB 0x4 +#define SCU3_ID_SCU3 0x3 + + +#define SCU3_INT_TACHO (1<<0) +#define SCU3_INT_SER_1 (1<<1) +#define SCU3_INT_DPR_A (1<<2) +#define SCU3_INT_SJA_1 (1<<3) +#define SCU3_INT_SJA_2 (1<<4) +#define SCU3_INT_DPR_B (1<<5) +#define SCU3_INT_SER_2 (1<<6) +#define SCU3_INT_SER_3 (1<<7) +#define SCU3_INT_SER_A (1<<8) +#define SCU3_INT_SER_B (1<<9) +#define SCU3_INT_FINP (1<<10) + +#define MY_INTS ( 0 \ + | SCU3_INT_TACHO \ + | SCU3_INT_DPR_A \ + | SCU3_INT_SJA_1 \ + | SCU3_INT_SJA_2 \ + | SCU3_INT_DPR_B \ + | SCU3_INT_SER_A \ + | SCU3_INT_SER_B \ + | SCU3_INT_FINP \ +) + +static unsigned initState=0; +static void uio_scu3_remove(struct pci_dev *dev); + + +#define LED_ACCESS(num) \ +static ssize_t show_led##num(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + int msk = 1<<(num-1); \ + return sprintf(buf, "%c\n", SCU3_LED_REG & msk ? '1' : '0' ); \ +} \ + \ +static ssize_t store_led##num(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + int msk= 1<<(num-1); \ + unsigned char kbuf = buf[0]&0xf; \ + if(count<1) \ + return -EFAULT; \ + if(kbuf){ \ + SCU3_LED_REG |= msk; \ + }else { \ + SCU3_LED_REG &= ~msk; \ + } \ + return count; \ +} \ +static DEVICE_ATTR(scu3_led##num, S_IRUGO | S_IWUGO, show_led##num, store_led##num); +LED_ACCESS(1) +LED_ACCESS(2) +LED_ACCESS(3) +LED_ACCESS(4) + +static ssize_t show_leds(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "0x%02x\n", SCU3_LED_REG ); +} + +static ssize_t store_leds(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if(count<1) + return -EFAULT; + SCU3_LED_REG = simple_strtoul(buf,0,16); + return count; +} +static DEVICE_ATTR(scu3_leds, S_IRUGO | S_IWUGO, show_leds, store_leds); + + + +//extern int nmi_watchdog_counter; +//static ssize_t show_wdg(struct device *dev, +// struct device_attribute *attr, +// char *buf) +//{ +// return sprintf(buf, "%d\n", nmi_watchdog_counter ); +//} +// +//static ssize_t store_wdg(struct device *dev, +// struct device_attribute *attr, +// const char *buf, size_t count) +//{ +// if(count<1) +// return -EFAULT; +// nmi_watchdog_counter = simple_strtoul(buf,0,10); +// return count; +//} +//static DEVICE_ATTR(scu3_wdg, S_IRUGO | S_IWUGO, show_wdg, store_wdg); + + + + +static ssize_t show_id(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const char *idstr="UNKNOWN ID"; + switch(SCU3_ID_REG) + { + case SCU3_ID_SCU2: idstr="SCU2\n"; break; + case SCU3_ID_SCU2B: idstr="SCU2B\n"; break; + case SCU3_ID_SCU2B_BB: idstr="SCU2B_BB\n"; break; + case SCU3_ID_SCU3: idstr="SCU3\n"; break; + } + return sprintf(buf, "0x%02x %s\n", SCU3_ID_REG, idstr); +} +static DEVICE_ATTR(scu3_id, S_IRUGO, show_id,0); + +static irqreturn_t uio_scu3_handler(int irq, struct uio_info *dev_info) +{ + u16 req=SCU3_INT_STATUS_REG & MY_INTS; //INT_MASK; + + if(!req) /* shared irq? */ + { + return IRQ_NONE; + } + SCU3_INT_FRG_REG &= ~(MY_INTS); //INT_OFF_MASK; + return IRQ_HANDLED; + +// old +// static irqreturn_t uio_scu3_handler(int irq, struct uio_info *dev_info) +// { +// u16 req=((SCU3_INT_STATUS_REG & 0xff) << 8) | (SCU3_FI_STATUS_REG & 0xff); +// +// if(!req) /* shared irq? */ +// { +// return IRQ_NONE; +// } +// //SCU3_FI_FRG_REG = 0; nicht sperren damit weiterhin alle flanken sichtbar +// SCU3_FI_EOI_REG = 0xff; //Fast Input Ints mit EOI beseitigen +// SCU3_INT_FRG_REG = 0; //Hier gehts nicht mit EOI weil die Quellen noch anstehen +// return IRQ_HANDLED; +/* Fast IO register +Achtung!! see Fast_input_bit.vhd +1. komisches interupt-Sperrverhalten. +2. kein Intrequest Register + + +1. Beim sperren der ints mit SCU3_FI_FRG_REG=0 wird die Int-erkennung halb +ausgeschaltet. Zwischenzeitlich anfallende Ints werden bei der wiederfreigabe +genau dann gemeldet wenn der Pegel vor der Sperre ungleich dem Pegel hinter der +Sperre ist. +T 1 2 3 4 5 6 7 8 9 +INPUT_REG --______---- -----____---------- ---____--_______ + + | | | | + v v v v + +FRG_REG _------------ -------______--- -----______- + + | | | | | | | + v v v v v v v kein int + +STATUS_REG __i--__i--__ _____i-______i---__ ___i-___________ + ^ ^ ^ +EOI_REG _____-____-_ _________________-_ + +Im Bsp. wird also zum Zeitpunkt 5 ein Int ausgeloest, zum Zeitpunkt 9 +jedoch nicht, weil der Pegel vor der Int-Sperre einmal ungleich (T=5) und beim +zweiten Mal identisch (T=9) mit dem aktuellen Pegel ist. + +Dies fuehrt im schlimmsten Fall dazu, dass die Software auf dem falschen Pegel +festklemmt. Im Bsp. erkennt die Pollingloop die Pegel bei T=6 u. T=7, aber +T=8 kommt ausgerechnet in dem Zeitraum zwischen Pollingloop Ende und +Int-Freigabe. +Abhilfe: FRG_REG nicht verwenden. (Lediglich zur Komplettsperre) + + +2. Das STATUS_REG ist nur dann verfuegbar wenn das FRG_REG=1 und solange noch +kein EOI ausgeloest wurde. +*/ +} + +static struct uio_info *uinfo2 = 0; +static dma_addr_t dmem_baddr = 0; //bus addr +static void *dmem_vaddr = 0; + +static int dmem_init(struct pci_dev *pdev) +{ + int ret = -ENODEV; + + uinfo2 = kzalloc(sizeof(struct uio_info), GFP_KERNEL); + if (!uinfo2){ + printk(KERN_ERR PFX "kzalloc failed\n"); + ret = -ENOMEM; + goto out_err1; + } + if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { + printk(KERN_WARNING PFX "No suitable DMA available.\n"); + goto out_err2; + } +#define DMEMSIZE_K "64" +#define DMEMSIZE (64*1024) + dmem_vaddr = dma_alloc_coherent(&pdev->dev, DMEMSIZE, &dmem_baddr, GFP_KERNEL ); + // TODO we should free this dma memory + if (dmem_vaddr==0) { + printk(KERN_WARNING PFX "dma_alloc_coherent failed.\n"); + goto out_err2; + } + + printk(KERN_INFO PFX "allocated DMA-Memory size %x virt:%p bus:%p\n" + ,DMEMSIZE + ,dmem_vaddr + ,(void*)dmem_baddr + ); + uinfo2->name = "uio_scu3_dmem"; + uinfo2->version = DRV_VERSION; + uinfo2->irq = UIO_IRQ_NONE; + uinfo2->irq_flags = 0; + uinfo2->handler = 0; + { + struct uio_mem *uioMemSlot = &uinfo2->mem[0]; + uioMemSlot->addr = dmem_baddr; + uioMemSlot->size = DMEMSIZE; + uioMemSlot->memtype = UIO_MEM_PHYS; + uioMemSlot->name = "DMEM" DMEMSIZE_K; + } + if (uio_register_device(&pdev->dev, uinfo2)) { + printk(KERN_ERR PFX "bad uio_register_device\n"); + goto out_err3; + } + return 0; + + out_err3: + dma_free_coherent(&pdev->dev, DMEMSIZE, dmem_vaddr, dmem_baddr); + out_err2: + kfree(uinfo2); + out_err1: + return ret; +} + +static int dmem_remove(struct pci_dev *pdev) +{ + dma_free_coherent(&pdev->dev, DMEMSIZE, dmem_vaddr, dmem_baddr); + kfree(uinfo2); + return 0; +} + + +//#define NUM_RESOURCES 2 +#define NUM_UARTLITES 3 +//static struct resource uart_res[NUM_UARTLITES][NUM_RESOURCES]; +static struct platform_device *uart_devices[NUM_UARTLITES]; + + +static int sio_init(unsigned int num, resource_size_t base, int irq, int intmsk) +{ + int ret; + // struct resource *pres = uart_res[num]; + struct resource uart_res[] = { + {.flags = IORESOURCE_MEM, .start = base, .end = base + 16,}, + {.flags = IORESOURCE_IRQ, .start = irq, .end = irq, } + }; + //struct resource *pres = uart_res; + struct platform_device *pdev; + if(num > NUM_UARTLITES) + { + printk(KERN_ERR PFX "sio_init() index out of range %d\n",num); + return -ENOENT; + } + + printk(KERN_INFO PFX "sio_init %d: pres %p\n", + num,&uart_res); + +// pres[0].flags = IORESOURCE_MEM; +// pres[0].start = base; +// pres[0].end = base + 16; +// pres[1].flags = IORESOURCE_IRQ; +// pres[1].start = irq; +// pres[1].end = irq; + + SCU3_INT_FRG_REG |= intmsk; //sio_int_masks[num]; + +// pdev = platform_device_register_simple("uartlite", num, pres, 2); + pdev = platform_device_register_simple("uartlite", num, + uart_res, sizeof(uart_res)/sizeof(uart_res[0])); + uart_devices[num]=pdev; + + ret = PTR_ERR(pdev); + if (IS_ERR(pdev)) + { + printk(KERN_ERR PFX "Unable to register platform device '%s'#%d: err=%d\n", + "uartlite", num, ret); + SCU3_INT_FRG_REG &= ~intmsk;//sio_int_masks[num]; + } + else + { + printk(KERN_INFO PFX "Registered platform device '%s'#%d\n", + pdev->name,pdev->id); + } + return ret; +} + +static int uio_scu3_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int ret = -ENODEV; + struct uio_info *info=0; + resource_size_t mmio_start; //resource_size_t = phys_addr_t = u64/u32 + //resource_size_t mmio_end; + unsigned long mmio_flags; + unsigned long mmio_len; + int pci_irq; + printk(KERN_INFO PFX "probing...\n"); + + + if(MAX_UIO_MAPS<16 ) + { + printk(KERN_ERR PFX "less than 16 UIO_MAPS\n"); + goto out_err; + } + + info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); + if (!info){ + printk(KERN_ERR PFX "kzalloc failed\n"); + ret = -ENOMEM; + goto out_err; + } + pci_set_drvdata(pdev, info); + initState|=1; //drv-object memory initialized + + + if (pci_enable_device(pdev)) + { + printk(KERN_ERR PFX "device already enabled .. aborting\n"); + goto out_err; + }else{ + initState|=2; //enable ok + } + + pci_irq = pdev->irq; //veraendert durch pci_enable_device + info->name = DRV_NAME; + info->version = DRV_VERSION; + info->irq = pci_irq; + printk(KERN_INFO PFX "1 pdev->irq = %d\n",pdev->irq); + //info->irq_flags = IRQF_DISABLED | IRQF_SHARED; + // see below: SCU3_INT_FRG_REG = 0; + info->irq_flags = IRQF_SHARED; + info->handler = uio_scu3_handler; + + mmio_start = pci_resource_start(pdev, 0); //resource_size_t = phys_addr_t = u64/u32 + //mmio_end = pci_resource_end(pdev, 0); + mmio_flags = pci_resource_flags(pdev, 0); + mmio_len = pci_resource_len(pdev, 0); + + if (!mmio_start){ + printk(KERN_ERR PFX "mmio_start==0\n"); + goto out_err; + } + + if (!(mmio_flags & IORESOURCE_MEM)) { + printk(KERN_ERR PFX "region #0 not an MMIO resource, aborting\n"); + goto out_err; + } + if (mmio_len != SCU3_FPGA_MEM_SIZE) { + printk(KERN_ERR PFX "Invalid PCI mem region size(s), aborting %lx!=%x\n",mmio_len,SCU3_FPGA_MEM_SIZE); + goto out_err; + } + + initState|=4; //mem regions ok + + + + + {//dummy block + int k; + struct Tab{unsigned ofs,size;char**pBase;const char*name;}; + struct Tab sizes[]={ + { 0x000000, 0x1000, 0 ,"FAST-I"},// 10 fast in + { 0x100000, 0x1000, 0 ,"FAST-O"},// 4 fast out + { 0x200000, 0x4000, 0 ,"DPR-A"},// 0x4000 dpramA + { 0x300000, 0x1000, 0 ,"CAN-1"},// 0x80 can1 + { 0x400000, 0x1000, 0 ,"CAN-2"},// 0x80 can2 + //{ 0x500000, 0x1000, 0 },//16 UART1 + //{ 0x600000, 0x1000, 0 },//16 UART2 + { 0x700000, 0x1000, &addrBase_7 ,"PORT"},// 3 port 0,1,2 + //{ 0x800000, 0x1000, &addrBase_8 },// 8 sioA printf serial8250_register_port + //{ 0x900000, 0x1000, &addrBase_9 },// 8 sioB + { 0xa00000,0x10000, 0 ,"NVRAM"},//0x10000 nv-ram + { 0xb00000, 0x1000, 0 ,"TACHO"},// 0x10 tacho + { 0xc00000, 0x1000, &addrBase_c ,"INT"},// 6 interupt + { 0xd00000, 0x1000, &addrBase_d ,"REGS"},// 6 registers + { 0xe00000, 0x4000, 0 ,"DPR-B"},// 0x4000 dpramB + //{ 0xf00000, 0x1000, 0 } //16 UART3 + }; + + for( k=0; k < sizeof(sizes)/sizeof(sizes[0]); k++) + { + unsigned ofs = sizes[k].ofs ;//i<<20; + unsigned size = sizes[k].size; + resource_size_t addr = mmio_start + ofs; + char **pBase = sizes[k].pBase; //(unsigned*)sizes[k*3+2]; + struct uio_mem *uioMemSlot = &info->mem[k]; + BUG_ON(size == 0); + + //printk(KERN_INFO PFX "region %d %08x %06x\n",k,mem->addr,mem->size); + + if (!request_mem_region(addr, size,"scu3")) + { + printk(KERN_INFO PFX "pci_request_region %d not allowed \n",k); + goto out_err; + } + uioMemSlot->addr = addr; + uioMemSlot->size = size; + uioMemSlot->memtype = UIO_MEM_PHYS; + uioMemSlot->name = sizes[k].name; + + if(pBase){ + uioMemSlot->internal_addr = ioremap(uioMemSlot->addr,size); + *pBase = uioMemSlot->internal_addr; + if (!*pBase) + { + printk(KERN_ERR PFX "bad ioremap %d\n",k); + goto out_err; + } + } + } + } + SCU3_INT_FRG_REG = 0; //INT_OFF_MASK; //all ints off after + if (uio_register_device(&pdev->dev, info)) { + printk(KERN_ERR PFX "bad uio_register_device\n"); + goto out_err; + } + printk(KERN_INFO PFX "sio_init\n"); + initState|=8; //device registered + + + if(sio_init(0, mmio_start+0x500000, pci_irq,SCU3_INT_SER_1)==0) + initState|=0x10; //sio0 initialized + if(sio_init(1, mmio_start+0x600000, pci_irq,SCU3_INT_SER_2)==0) + initState|=0x20; //sio1 initialized + if(sio_init(2, mmio_start+0xf00000, pci_irq,SCU3_INT_SER_3)==0) + initState|=0x40; //sio2 initialized + + if(dmem_init(pdev)==0) + initState|=0x80; //dma memory + + ///home/husteret/build/SfcLeanOs-RT/trunk/Src/kernel/linux-2.6.22.1-rt9-lttng/include/linux/pci.h + // class_create_file(&pdev->bus->class_dev, &dev_attr_led1); + device_create_file(&pdev->dev, &dev_attr_scu3_led1); + device_create_file(&pdev->dev, &dev_attr_scu3_led2); + device_create_file(&pdev->dev, &dev_attr_scu3_led3); + device_create_file(&pdev->dev, &dev_attr_scu3_led4); + device_create_file(&pdev->dev, &dev_attr_scu3_leds); + //device_create_file(&dev->dev, &dev_attr_scu3_wdg); + device_create_file(&pdev->dev, &dev_attr_scu3_id); + initState|=0x100; + + printk(KERN_INFO PFX "successfully probed. adr=%p size=0x%lx irq=%ld =? devirq=%d\n" + ,(void*)info->mem[0].addr + ,mmio_len //info->mem[0].size + ,info->irq + ,pdev->irq + ); + return 0; + + out_err: + uio_scu3_remove(pdev);//uio_scu3_cleanup(pdev,info); + printk(KERN_ERR PFX "load error\n"); + return ret; +} + + +static void uio_scu3_remove(struct pci_dev *pdev) +{ + struct uio_info *info = pci_get_drvdata(pdev); + if(initState & 0x100) + { + device_remove_file(&pdev->dev, &dev_attr_scu3_led1); + device_remove_file(&pdev->dev, &dev_attr_scu3_led2); + device_remove_file(&pdev->dev, &dev_attr_scu3_led3); + device_remove_file(&pdev->dev, &dev_attr_scu3_led4); + device_remove_file(&pdev->dev, &dev_attr_scu3_leds); + //device_remove_file(&pdev->dev, &dev_attr_scu3_wdg); + device_remove_file(&pdev->dev, &dev_attr_scu3_id); + } + if(initState & 0x80) dmem_remove(pdev); + if(initState & 0x40) platform_device_unregister(uart_devices[2]); + if(initState & 0x20) platform_device_unregister(uart_devices[1]); + if(initState & 0x10) platform_device_unregister(uart_devices[0]); + + //if(initState & 0x10) serial8250_unregister_port(sioLine); + if(initState & 0x8) uio_unregister_device(info); + if(initState & 0x4) + { + int k; + for( k=0; k < MAX_UIO_MAPS; k++) + { + if(info->mem[k].addr) release_mem_region(info->mem[k].addr, + info->mem[k].size); + if(info->mem[k].internal_addr) iounmap(info->mem[k].internal_addr); + } + } + if(initState & 0x2) pci_disable_device(pdev); + if(initState & 0x1) pci_set_drvdata(pdev, NULL); + if(initState & 0x1) kfree(info); +} + + +static struct pci_device_id scu3_pci_tbl[] = {// + { + .vendor = PCI_VENDOR_ID_SCU3, + .device = SCU3_DEVICE_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + {0,} +}; +//MODULE_DEVICE_TABLE (pci, scu3_pci_tbl); +static struct pci_driver uio_scu3_driver = { + .name = DRV_NAME, + .id_table = scu3_pci_tbl, + .probe = uio_scu3_probe, + .remove = uio_scu3_remove, +}; + + + +/* + * Main initialization/remove routines + */ +extern void (*g_timer_int_callback)(void); + +static int __init uio_scu3_init_module(void) +{ + printk (KERN_INFO DRV_NAME "loading..\n"); + return pci_register_driver(&uio_scu3_driver); +} + +static void __exit uio_scu3_exit_module(void) +{ + pci_unregister_driver(&uio_scu3_driver); + printk(KERN_INFO PFX "successfully unloaded.\n"); +} + + +module_init(uio_scu3_init_module); +module_exit(uio_scu3_exit_module); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Husterer"); +MODULE_DESCRIPTION("UIO scu3 driver"); + +//for entry in modules.pcimap +MODULE_DEVICE_TABLE(pci, scu3_pci_tbl);