Actual driver code for directly mapped SJA1000 into PCI mem region 0. master
authorPavel Pisa <pisa@cmp.felk.cvut.cz>
Sun, 15 Sep 2013 19:07:03 +0000 (21:07 +0200)
committerPavel Pisa <pisa@cmp.felk.cvut.cz>
Sun, 15 Sep 2013 19:07:03 +0000 (21:07 +0200)
Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
lincan/src/pcisja1000mm.c [new file with mode: 0644]

diff --git a/lincan/src/pcisja1000mm.c b/lincan/src/pcisja1000mm.c
new file mode 100644 (file)
index 0000000..2f8a9ec
--- /dev/null
@@ -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 <http://dce.felk.cvut.cz>   */
+/* Copyright (C) 2002-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz>             */
+/* Funded by OCERA and FRESCOR IST projects                               */
+/* Based on CAN driver code by Arnaud Westenberg <arnaud@wanadoo.nl>      */
+/*                                                                        */
+/* 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;i<candev->nr_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_nr<candev->nr_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*/