From: Pavel Pisa Date: Sun, 15 Sep 2013 19:07:03 +0000 (+0200) Subject: Actual driver code for directly mapped SJA1000 into PCI mem region 0. X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lincan.git/commitdiff_plain Actual driver code for directly mapped SJA1000 into PCI mem region 0. Signed-off-by: Pavel Pisa --- diff --git a/lincan/src/pcisja1000mm.c b/lincan/src/pcisja1000mm.c new file mode 100644 index 0000000..2f8a9ec --- /dev/null +++ b/lincan/src/pcisja1000mm.c @@ -0,0 +1,280 @@ +/**************************************************************************/ +/* File: pcisja1000mm.c - SJA1000 directly mapped into PCI mem region 0 */ +/* */ +/* LinCAN - (Not only) Linux CAN bus driver */ +/* Copyright (C) 2002-2009 DCE FEE CTU Prague */ +/* Copyright (C) 2002-2009 Pavel Pisa */ +/* Funded by OCERA and FRESCOR IST projects */ +/* Based on CAN driver code by Arnaud Westenberg */ +/* */ +/* LinCAN is free software; you can redistribute it and/or modify it */ +/* under terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2, or (at your option) any */ +/* later version. LinCAN is distributed in the hope that it will be */ +/* useful, but WITHOUT ANY WARRANTY; without even the implied warranty */ +/* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. You should have received a */ +/* copy of the GNU General Public License along with LinCAN; see file */ +/* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, */ +/* Cambridge, MA 02139, USA. */ +/* */ +/* To allow use of LinCAN in the compact embedded systems firmware */ +/* and RT-executives (RTEMS for example), main authors agree with next */ +/* special exception: */ +/* */ +/* Including LinCAN header files in a file, instantiating LinCAN generics */ +/* or templates, or linking other files with LinCAN objects to produce */ +/* an application image/executable, does not by itself cause the */ +/* resulting application image/executable to be covered by */ +/* the GNU General Public License. */ +/* This exception does not however invalidate any other reasons */ +/* why the executable file might be covered by the GNU Public License. */ +/* Publication of enhanced or derived LinCAN files is required although. */ +/**************************************************************************/ + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" +#include "../include/sja1000p.h" + +#ifdef CAN_ENABLE_PCI_SUPPORT + +#define DIRECT_PCISJA100_VENDOR 0x1b36 +#define DIRECT_PCISJA100_ID 0xbeef + +#define PCISJA1000MM_OCR_DEFAULT_STD 0xDA + +#define PCISJA1000MM_BYTES_PER_CIRCUIT 0x80 + +void pcisja1000mm_disconnect_irq(struct candevice_t *candev) +{ +} + +void pcisja1000mm_connect_irq(struct candevice_t *candev) +{ +} + +int pcisja1000mm_request_io(struct candevice_t *candev) +{ + unsigned long io_addr; + int i; + + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21)) + if(pci_request_region(candev->sysdevptr.pcidev, 0, "pcisja1000mm") != 0){ + CANMSG("Request of pcisja1000mm range failed\n"); + return -ENODEV; + } + #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + if(pci_request_regions(candev->sysdevptr.pcidev, "pcisja1000mm") != 0){ + CANMSG("Request of pcisja1000mm regions failed\n"); + return -ENODEV; + } + #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + + io_addr=pci_resource_start(candev->sysdevptr.pcidev,0);; + if (!(candev->dev_base_addr = ioremap(io_addr, + pci_resource_len(candev->sysdevptr.pcidev,0)))) { + CANMSG("Unable to access I/O memory at: 0x%lx\n", io_addr); + goto error_ioremap_io; + } + + candev->io_addr=io_addr; + + /* + * this is redundant with chip initialization, but remap address + * can change when resources are temporarily released + */ + for(i=0;inr_all_chips;i++) { + struct canchip_t *chip=candev->chip[i]; + if(!chip) continue; + chip->chip_base_addr = candev->dev_base_addr + + i * PCISJA1000MM_BYTES_PER_CIRCUIT; + if(!chip->msgobj[0]) continue; + chip->msgobj[0]->obj_base_addr=chip->chip_base_addr; + } + + pcisja1000mm_disconnect_irq(candev); + + return 0; + +error_ioremap_io: + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21)) + pci_release_region(candev->sysdevptr.pcidev, 0); + #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + pci_release_regions(candev->sysdevptr.pcidev); + #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + return -ENODEV; +} + +int pcisja1000mm_release_io(struct candevice_t *candev) +{ + pcisja1000mm_disconnect_irq(candev); + + iounmap(candev->dev_base_addr); + + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21)) + pci_release_region(candev->sysdevptr.pcidev, 0); + #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + pci_release_regions(candev->sysdevptr.pcidev); + #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + + return 0; +} + + +void pcisja1000mm_write_register(unsigned data, can_ioptr_t address) +{ + can_writeb(data,address); +} + +unsigned pcisja1000mm_read_register(can_ioptr_t address) +{ + return can_readb(address); +} + +int pcisja1000mm_reset(struct candevice_t *candev) +{ + int i=0,chip_nr; + struct canchip_t *chip; + unsigned cdr; + + DEBUGMSG("Resetting pcisja1000mm hardware ...\n"); + + pcisja1000mm_disconnect_irq(candev); + + for(chip_nr=0;chip_nrnr_all_chips;chip_nr++){ + if(!candev->chip[chip_nr]) continue; + chip=candev->chip[chip_nr]; + + pcisja1000mm_write_register(sjaMOD_RM, chip->chip_base_addr+SJAMOD); + udelay(1000); + + cdr=pcisja1000mm_read_register(chip->chip_base_addr+SJACDR); + pcisja1000mm_write_register(cdr|sjaCDR_PELICAN, chip->chip_base_addr+SJACDR); + + pcisja1000mm_write_register(0, chip->chip_base_addr+SJAIER); + + i=20; + pcisja1000mm_write_register(0, chip->chip_base_addr+SJAMOD); + while (pcisja1000mm_read_register(chip->chip_base_addr+SJAMOD)&sjaMOD_RM){ + if(!i--) return -ENODEV; + udelay(1000); + pcisja1000mm_write_register(0, chip->chip_base_addr+SJAMOD); + } + + cdr=pcisja1000mm_read_register(chip->chip_base_addr+SJACDR); + pcisja1000mm_write_register(cdr|sjaCDR_PELICAN, chip->chip_base_addr+SJACDR); + + pcisja1000mm_write_register(0, chip->chip_base_addr+SJAIER); + + pcisja1000mm_read_register(chip->chip_base_addr+SJAIR); + } + + + pcisja1000mm_connect_irq(candev); + + return 0; +} + +int pcisja1000mm_init_hw_data(struct candevice_t *candev) +{ + struct pci_dev *pcidev; + int i; + + pcidev = can_pci_get_next_untaken_device(DIRECT_PCISJA100_VENDOR, DIRECT_PCISJA100_ID); + if(pcidev == NULL) + return -ENODEV; + + if (pci_enable_device (pcidev)){ + printk(KERN_CRIT "Setup of PCISJA1000MM failed\n"); + can_pci_dev_put(pcidev); + return -EIO; + } + candev->sysdevptr.pcidev=pcidev; + + for(i=0;i<1;i++){ + + if(!(pci_resource_flags(pcidev,0)&IORESOURCE_MEM)){ + printk(KERN_ERR "PCISJA1000MM region %d is not memory\n",i); + goto error_ret; + } + } + candev->io_addr=pci_resource_start(pcidev,0); /*IO window for SJA1000 chips*/ + candev->dev_base_addr=can_ulong2ioptr(candev->io_addr); + candev->res_addr=0; + + /*candev->flags |= CANDEV_PROGRAMMABLE_IRQ;*/ + + candev->nr_82527_chips=0; + candev->nr_sja1000_chips=1; + candev->nr_all_chips=1; + + return 0; + +error_ret: + + printk(KERN_CRIT "Setup of PCISJA1000MM failed\n"); + pci_disable_device (pcidev); + can_pci_dev_put(pcidev); + return -EIO; +} + +void pcisja1000mm_done_hw_data(struct candevice_t *candev) +{ + struct pci_dev *pcidev = candev->sysdevptr.pcidev; + can_pci_dev_put(pcidev); +} + +int pcisja1000mm_init_chip_data(struct candevice_t *candev, int chipnr) +{ + + if(candev->sysdevptr.pcidev==NULL) + return -ENODEV; + + candev->chip[chipnr]->chip_irq=candev->sysdevptr.pcidev->irq; + + sja1000p_fill_chipspecops(candev->chip[chipnr]); + candev->chip[chipnr]->chip_base_addr= + can_ioport2ioptr(candev->io_addr+chipnr*PCISJA1000MM_BYTES_PER_CIRCUIT); + + candev->chip[chipnr]->flags = 0; + candev->chip[chipnr]->int_cpu_reg = 0; + candev->chip[chipnr]->int_clk_reg = 0; + candev->chip[chipnr]->int_bus_reg = 0; + candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF; + candev->chip[chipnr]->sja_ocr_reg = PCISJA1000MM_OCR_DEFAULT_STD; + candev->chip[chipnr]->clock = 16000000; + candev->chip[chipnr]->flags |= CHIP_IRQ_PCI; + + return 0; +} + +int pcisja1000mm_init_obj_data(struct canchip_t *chip, int objnr) +{ + chip->msgobj[objnr]->obj_base_addr=chip->chip_base_addr; + return 0; +} + +int pcisja1000mm_program_irq(struct candevice_t *candev) +{ + + return 0; +} + +int pcisja1000mm_register(struct hwspecops_t *hwspecops) +{ + hwspecops->request_io = pcisja1000mm_request_io; + hwspecops->release_io = pcisja1000mm_release_io; + hwspecops->reset = pcisja1000mm_reset; + hwspecops->init_hw_data = pcisja1000mm_init_hw_data; + hwspecops->done_hw_data = pcisja1000mm_done_hw_data; + hwspecops->init_chip_data = pcisja1000mm_init_chip_data; + hwspecops->init_obj_data = pcisja1000mm_init_obj_data; + hwspecops->write_register = pcisja1000mm_write_register; + hwspecops->read_register = pcisja1000mm_read_register; + hwspecops->program_irq = pcisja1000mm_program_irq; + return 0; +} + + +#endif /*CAN_ENABLE_PCI_SUPPORT*/