Index: linux-3.12.33-rt47-i386/drivers/uio/Kconfig =================================================================== --- linux-3.12.33-rt47-i386.orig/drivers/uio/Kconfig 2015-09-15 10:45:22.214318717 +0200 +++ linux-3.12.33-rt47-i386/drivers/uio/Kconfig 2015-09-15 10:45:22.306319474 +0200 @@ -28,6 +28,14 @@ Driver for HDM SCU3 Board. If you don't know what to do here, say N. +config UIO_HDM_SCU3_MOD + tristate "Driver for HDM SCU3 Board with PCIexpress" + depends on UIO && PCI && SERIAL_UARTLITE + default n + help + Driver for HDM SCU3 Board. + If you don't know what to do here, say N. + config UIO_CIF tristate "generic Hilscher CIF Card driver" depends on PCI Index: linux-3.12.33-rt47-i386/drivers/uio/Makefile =================================================================== --- linux-3.12.33-rt47-i386.orig/drivers/uio/Makefile 2015-09-15 10:45:22.214318717 +0200 +++ linux-3.12.33-rt47-i386/drivers/uio/Makefile 2015-09-15 10:45:22.306319474 +0200 @@ -1,6 +1,7 @@ obj-$(CONFIG_UIO) += uio.o obj-$(CONFIG_UIO_HDM_SCU2) += uio_scu2.o obj-$(CONFIG_UIO_HDM_SCU3) += uio_scu3.o +obj-$(CONFIG_UIO_HDM_SCU3) += uio_scu3_mod.o obj-$(CONFIG_UIO_CIF) += uio_cif.o obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o obj-$(CONFIG_UIO_DMEM_GENIRQ) += uio_dmem_genirq.o Index: linux-3.12.33-rt47-i386/drivers/uio/uio_scu3_mod.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-3.12.33-rt47-i386/drivers/uio/uio_scu3_mod.c 2015-09-15 11:05:12.383997604 +0200 @@ -0,0 +1,445 @@ +/* + * uio_scu3.c + * + * Copyright(C) 2008, Dietrich Toews + * + * Userspace IO driver for HDM-Scu3 Hardwarecomponents + * + * Licensed under the GPLv2 only. + */ + +#define DRV_NAME_SYS "uio_scu3_pcie" +#define DRV_NAME "uio_scu3" +#define DRV_VERSION "2.2.0" +#define PFX DRV_NAME " PCIe: " + + +#include +#include +#include +#include +#include +#include + + +//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 0xbad4 +#define SCU3_DEVICE_ID 0x0008 +#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 ); + + + + +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; + +/* 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; +} + + +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; + } + 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, 0 ,"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( dmem_init( pdev ) == 0 ) initState |= 0x80; //dma memory + + 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_id ); + } + if( initState & 0x80 ) dmem_remove( pdev ); + + //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_SYS, + .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 PFX "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 v2" ); +MODULE_AUTHOR( "Dietrich Toews" ); +MODULE_DESCRIPTION( "UIO scu3 driver PCIe" ); + +//for entry in modules.pcimap +MODULE_DEVICE_TABLE( pci, scu3_pci_tbl );