]> rtime.felk.cvut.cz Git - lincan.git/blobdiff - lincan/src/mpc5200.c
Merge branch 'master' into can-usb1
[lincan.git] / lincan / src / mpc5200.c
diff --git a/lincan/src/mpc5200.c b/lincan/src/mpc5200.c
new file mode 100644 (file)
index 0000000..4f67d24
--- /dev/null
@@ -0,0 +1,243 @@
+/**************************************************************************/
+/* File: mpc5200.c - Freescale MPC5200 MSCAN controller support           */
+/*                                                                        */
+/* 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>             */
+/* Copyright (C) 2007-2008 Martin Petera <peterm4@fel.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/mpc5200.h"
+#include "../include/mscan.h"
+#include "linux/of.h"
+#include "linux/of_platform.h"
+
+int mpc5200_init_device_node(struct canchip_t * chip, struct device_node * devnode);
+
+/* in time when release_io should take place, we dont know the
+ * base addresses that has to be free.
+ * This variable keeps those addresses
+ */
+unsigned long * chips_addr;
+
+
+int mpc5200_request_io(struct candevice_t *candev)
+{
+       int chipnr;
+       struct device_node * dn;
+       const char * of_devname = "can";
+
+       /* Workaround for OpenFormware */
+
+       /* Select device by chipnr:
+        *  first dev is @ 900
+        *  second dev is @ 980
+        * 
+        * There are only two devices on MPC5200 */
+
+       /* initialize internal address storage */
+       chips_addr = (unsigned long*)kmalloc(candev->nr_all_chips * sizeof(unsigned long), GFP_KERNEL);
+
+       /* DEBUGMSG("Looking for device node '%s'\n", of_devname); */
+
+       chipnr = 0;
+       for_each_node_by_name(dn, of_devname)
+       {
+               /* DEBUGMSG(" got OF node (%s)...\n", of_devname); */
+
+               if (!of_device_is_available(dn))
+               {
+                       DEBUGMSG("\tdevice not available\n");
+                       continue;
+               }
+
+               
+               if (mpc5200_init_device_node(candev->chip[chipnr], dn))
+                       return -ENODEV;
+
+               /* fill received address to internal storage */
+               chips_addr[chipnr] = (unsigned long)candev->chip[chipnr]->chip_base_addr;
+
+               /* original MIDAM Shark board use only one CAN chip - this handles similar cases */
+               if (++chipnr >= candev->nr_all_chips)
+                       break;
+       }
+
+
+       DEBUGMSG("Succesfully mapped %d can devices\n", chipnr);
+       return 0;
+}
+
+int mpc5200_release_io(struct candevice_t *candev)
+{
+       int i;
+
+       /*can_release_io_region(candev->io_addr, candev->nr_all_chips * IO_RANGE);*/
+
+       /* free all chips memorey space - using internal address storage */
+       for (i = 0; i < candev->nr_all_chips; i++)
+               iounmap((void*)chips_addr[i]);
+
+       kfree(chips_addr);
+       
+       return 0;
+}
+
+int mpc5200_reset(struct candevice_t *candev)
+{
+       int i; 
+       DEBUGMSG("Resetting MSCAN chips ...\n");
+
+       for (i = 0; i < candev->nr_all_chips; i++)
+       {
+           /* !!! Assuming this card has ONLY MSCAN chips !!! */
+           if (mscan_reset_chip(candev->chip[i]))
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+int mpc5200_init_hw_data(struct candevice_t *candev) 
+{
+       /* candev->res_addr = RESET_ADDR; */
+       candev->nr_82527_chips = NR_82527;
+       candev->nr_sja1000_chips = NR_SJA1000;
+       candev->nr_all_chips = NR_ALL;
+       /* candev->flags |= CANDEV_PROGRAMMABLE_IRQ; */
+
+       return 0;
+}
+
+/* special function for midam board */
+int mpc5200_midam_init_hw_data(struct candevice_t *candev)
+{
+       /* use same init routine */
+       mpc5200_init_hw_data(candev);
+
+       /* modify chip count */
+       candev->nr_all_chips = NR_ALL_MIDAM;
+
+       return 0;
+}
+
+int mpc5200_init_chip_data(struct candevice_t *candev, int chipnr)
+{
+       mscan_fill_chipspecops(candev->chip[chipnr]);
+
+       candev->chip[chipnr]->clock = MPC5200_CLK_FREQ;
+       candev->chip[chipnr]->hostdevice = candev;
+
+/*
+       candev->chip[chipnr]->chip_base_addr = can_ioport2ioptr(candev->io_addr) + chipnr * MPC5200_CAN_CHIP_OFFSET;  one chip with 2 interfaces
+       candev->chip[chipnr]->chip_irq = MPC5200_CAN_IRQ + chipnr;
+*/
+       DEBUGMSG("mpc5200_init_chip_data\n");
+
+       return 0;
+}
+
+int mpc5200_init_obj_data(struct canchip_t *chip, int objnr)
+{
+       /* we have only two chips with only one mailbox each */
+       chip->msgobj[objnr]->obj_base_addr = (can_ioptr_t) MSCAN_CTL0 + MPC5200_CAN_CHIP_OFFSET * objnr;
+
+       return 0;
+}
+
+int mpc5200_program_irq(struct candevice_t *candev)
+{
+       /* we  don't use programmable interrupt on MPC5200 */ 
+       return 0;
+}
+
+
+void mpc5200_write_register(unsigned data, can_ioptr_t address)
+{
+       /* address is an absolute address */
+       /* DEBUGMSG("write register\n"); */
+       writeb(data, address);          /* regs in PowerPC (5200) are one-byte length */
+}
+
+unsigned mpc5200_read_register(can_ioptr_t address)
+{
+       /* address is an absolute address */
+       /* DEBUGMSG("read register\n"); */
+       return readb(address);          /* regs in PowerPC (5200) are one-byte length */
+}
+
+int mpc5200_register(struct hwspecops_t *hwspecops)
+{
+       hwspecops->request_io = mpc5200_request_io;
+       hwspecops->release_io = mpc5200_release_io;
+       hwspecops->reset = mpc5200_reset;
+       hwspecops->init_hw_data = mpc5200_init_hw_data;
+       hwspecops->init_chip_data = mpc5200_init_chip_data;
+       hwspecops->init_obj_data = mpc5200_init_obj_data;
+       hwspecops->program_irq = NULL; /* mpc5200_program_irq; */
+       hwspecops->write_register = mpc5200_write_register;
+       hwspecops->read_register = mpc5200_read_register;
+       return 0;
+}
+
+int mpc5200_midam_register(struct hwspecops_t *hwspecops)
+{
+       /* use same register routine for MIDAM board */
+       mpc5200_register(hwspecops);
+
+       /* use specific init_hw_data */
+       hwspecops->init_hw_data = mpc5200_midam_init_hw_data;
+
+       return 0;
+}
+
+int mpc5200_init_device_node(struct canchip_t * chip, struct device_node * devnode)
+{
+       chip->chip_base_addr = of_iomap(devnode, 0);
+       if (!chip->chip_base_addr)
+       {
+               DEBUGMSG("\tcannot map IO - of_iomap\n");
+               return -ENODEV;
+       }
+
+       chip->chip_irq = irq_of_parse_and_map(devnode, 0);
+       if (!chip->chip_irq)
+       {
+               DEBUGMSG("\tcannot map IRQ\n");
+               iounmap(chip->chip_base_addr);
+               return -ENODEV;
+       }
+
+       DEBUGMSG("Bound to io-addr: 0x%08x IRQ: %d\n", (unsigned int)chip->chip_base_addr, chip->chip_irq);
+       
+       return 0;
+}