/* * UIO can CAN L2 PCI * * (C) Armin Steinhoff * (C) 2007 Hans J. Koch * Original code (C) 2005 Benedikt Spranger * * Licensed under GPL version 2 only. * */ #include #include #include #include #include #define DEBUG 1 #define PCI_VENDORID 0x110A #define PCI_DEVICEID 0x2104 #define INT_QUEUE_SIZE 64 static unsigned char IntIx, * IntQ; static void __iomem *ISR; static void __iomem *ICR; static irqreturn_t CAN_handler(int irq, struct uio_info *dev_info) { // check PITA ICR ... if(*((unsigned long *)ICR) & 0x02) // our interrupt ? { IntQ[IntIx] = *((unsigned char *)ISR); IntIx = (IntIx + 1) & 0xF ; // modulo 16 *((unsigned long *)ICR) = 0x02; // confirm interrupt return(IRQ_HANDLED); } else return(IRQ_NONE); } static int __devinit ems_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct uio_info *info; int err; info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); if (!info) return -ENOMEM; // if (pci_enable_device(dev)) goto out_free; err = pci_enable_device(dev); if (err) { dev_err(&dev->dev, "%s: pci_enable_device failed: %d\n", __func__, err); return err; } if (pci_request_regions(dev, "uio_ems")) goto out_disable; info->mem[0].addr = pci_resource_start(dev, 0); if (!info->mem[0].addr) goto out_release; info->mem[0].size = pci_resource_len(dev, 0); info->mem[0].memtype = UIO_MEM_PHYS; info->mem[0].internal_addr = ioremap(info->mem[0].addr,info->mem[0].size); if (!info->mem[0].internal_addr) goto out_release; // disable interrupt at PITA level *( (unsigned long *)( info->mem[0].internal_addr )) &= ~0x20000; // reset Bit 17 info->mem[1].addr = pci_resource_start(dev, 1); if (!info->mem[1].addr) goto out_release; info->mem[1].size = pci_resource_len(dev, 1); info->mem[1].memtype = UIO_MEM_PHYS; info->mem[1].internal_addr = ioremap(info->mem[1].addr,info->mem[1].size); if (!info->mem[1].internal_addr) goto out_release; // interrupt queue info->mem[2].addr = (unsigned long)kmalloc(64, GFP_KERNEL); IntQ = (unsigned char * )info->mem[2].addr; if (!info->mem[2].addr) goto out_unmap1; memset(IntQ, 0x00, 16); IntIx = 0; info->mem[2].memtype = UIO_MEM_LOGICAL; info->mem[2].size = 64; ISR = info->mem[1].internal_addr + 12 + 0x400; // interrupt status channel 1 ICR = info->mem[0].internal_addr; *((unsigned long *)ICR) = 0x02; // confirm interrupt info->name = "uio_ems"; info->version = "0.0.1"; info->irq = dev->irq; info->irq_flags |= IRQF_SHARED; info->handler = CAN_handler; if (uio_register_device(&dev->dev, info)) goto out_kfree; pci_set_drvdata(dev, info); printk("can_pci_probe end\n"); return 0; out_kfree: kfree((void *)info->mem[2].addr); out_unmap1: iounmap(info->mem[1].internal_addr); iounmap(info->mem[0].internal_addr); out_release: pci_release_regions(dev); out_disable: pci_disable_device(dev); kfree (info); printk("CAN_PCI: -ENODEV\n"); return -ENODEV; } static void ems_pci_remove(struct pci_dev *dev) { struct uio_info *info = pci_get_drvdata(dev); uio_unregister_device(info); pci_release_regions(dev); pci_disable_device(dev); pci_set_drvdata(dev, NULL); iounmap(info->mem[0].internal_addr); iounmap(info->mem[1].internal_addr); kfree((void *)info->mem[2].addr); kfree (info); } static struct pci_device_id ems_pci_ids[] __devinitdata = { { PCI_VENDORID, PCI_DEVICEID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0, } }; static struct pci_driver ems_pci_driver = { .name = "uio_ems", .id_table = ems_pci_ids, .probe = ems_pci_probe, .remove = ems_pci_remove, }; static int __init ems_init_module(void) { int ret; ret = pci_register_driver(&ems_pci_driver); return ret; } static void __exit ems_exit_module(void) { pci_unregister_driver(&ems_pci_driver); } module_init(ems_init_module); module_exit(ems_exit_module); MODULE_DEVICE_TABLE(pci, ems_pci_ids); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("A. Steinhoff");