From: Wolfgang Grandegger <w...@denx.de>

Signed-off-by: Wolfgang Grandegger <w...@denx.de>
---
 drivers/can/Makefile |    1 +
 drivers/can/i82527.c |  366 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/i82527.h     |  201 +++++++++++++++++++++++++++
 3 files changed, 568 insertions(+), 0 deletions(-)
 create mode 100644 drivers/can/i82527.c
 create mode 100644 include/i82527.h

diff --git a/drivers/can/Makefile b/drivers/can/Makefile
index e2b6bd6..d550a45 100644
--- a/drivers/can/Makefile
+++ b/drivers/can/Makefile
@@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk
 LIB    := $(obj)libcan.a
 
 COBJS-$(CONFIG_CAN)            += can.o
+COBJS-$(CONFIG_CAN_I82527)     += i82527.o
 COBJS-$(CONFIG_CAN_SJA1000)    += sja1000.o
 
 COBJS  := $(COBJS-y)
diff --git a/drivers/can/i82527.c b/drivers/can/i82527.c
new file mode 100644
index 0000000..b3eacd6
--- /dev/null
+++ b/drivers/can/i82527.c
@@ -0,0 +1,366 @@
+/*
+ * (C) Copyright 2007-2009, Wolfgang Grandegger <w...@denx.de>
+ *
+ * Derived from OCAN driver:
+ *
+ * Copyright (C) 2002 Alessandro Rubini <rub...@linux.it>
+ * Copyright (C) 2002 System SpA <info.electron...@system-group.it>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/io.h>
+
+#include <can.h>
+#include <i82527.h>
+
+#define I82527_TX_OBJ  1
+#define I82527_RX_OBJ  15
+
+/*
+ * Basic functions to access registers
+ */
+#define i82527_read_reg(dev, reg)                      \
+       in_8 ((volatile u8 *)((dev)->base + (reg)))
+
+#define i82527_write_reg(dev, reg, value)                      \
+       out_8 ((volatile u8 *)((dev)->base + (reg)), value)
+
+/*
+ * Higher level functions
+ */
+static inline int i82527_read_msgcfg (struct can_dev *dev, int iobj)
+{
+       return i82527_read_reg (dev, (iobj * I82527_MSG_OFF) + I82527_MSG_CFG);
+}
+
+static inline void i82527_write_msgcfg (struct can_dev *dev, int iobj, int val)
+{
+       i82527_write_reg (dev, (iobj * I82527_MSG_OFF) + I82527_MSG_CFG, val);
+}
+
+static inline void i82527_write_std_mask (struct can_dev *dev, u16 val)
+{
+       /* errors are ignored, hmm... */
+       i82527_write_reg (dev, I82527_GMASK_STD, val >> 8);
+       i82527_write_reg (dev, I82527_GMASK_STD + 1, val & 0xff);
+}
+
+static inline void i82527_write_x_mask (struct can_dev *dev, u32 val)
+{
+       /* errors are ignored, hmm... */
+       i82527_write_reg (dev, I82527_GMASK_XTD, val >> 24);
+       i82527_write_reg (dev, I82527_GMASK_XTD + 1, (val >> 16) & 0xff);
+       i82527_write_reg (dev, I82527_GMASK_XTD + 2, (val >> 8) & 0xff);
+       i82527_write_reg (dev, I82527_GMASK_XTD + 3, (val >> 0) & 0xff);
+}
+
+static inline void i82527_write_15_mask (struct can_dev *dev, u32 val)
+{
+       /* errors are ignored, hmm... */
+       i82527_write_reg (dev, I82527_MSG15_MASK, val >> 24);
+       i82527_write_reg (dev, I82527_MSG15_MASK + 1, (val >> 16) & 0xff);
+       i82527_write_reg (dev, I82527_MSG15_MASK + 2, (val >> 8) & 0xff);
+       i82527_write_reg (dev, I82527_MSG15_MASK + 3, (val >> 0) & 0xff);
+}
+
+static inline void i82527_write_msgctrl (struct can_dev *dev, int iobj, u16 
val)
+{
+       /* FIXME: this is used little-endian, but doesn't need to be 16b */
+
+       /* errors are ignored, hmm... */
+       i82527_write_reg (dev, (iobj * I82527_MSG_OFF) +
+                         I82527_MSG_CTRL, val & 0xff);
+       i82527_write_reg (dev, (iobj * I82527_MSG_OFF) +
+                         I82527_MSG_CTRL + 1, val >> 8);
+}
+
+/* write a single byte of msgctrl, twice as fast as the function above */
+static inline void i82527_msgflag (struct can_dev *dev, int iobj, u16 act)
+{
+       if ((act & 0xff) == 0xff)
+               i82527_write_reg (dev, (iobj * I82527_MSG_OFF) +
+                                 I82527_MSG_CTRL + 1, act >> 8);
+       else
+               i82527_write_reg (dev, (iobj * I82527_MSG_OFF) +
+                                 I82527_MSG_CTRL, act & 0xff);
+}
+
+static inline u32 i82527_read_msgarb (struct can_dev *dev, int iobj)
+{
+       int port = (iobj * I82527_MSG_OFF) + I82527_MSG_ARBIT;
+
+       u32 ret = (i82527_read_reg (dev, port) << 24 |
+                  i82527_read_reg (dev, port + 1) << 16 |
+                  i82527_read_reg (dev, port + 2) << 8 |
+                  i82527_read_reg (dev, port + 3));
+       return ret;
+}
+
+static inline void i82527_write_msgarb (struct can_dev *dev, int iobj, u32 val)
+{
+       int port = (iobj * I82527_MSG_OFF) + I82527_MSG_ARBIT;
+       i82527_write_reg (dev, port, val >> 24);
+       i82527_write_reg (dev, port + 1, (val >> 16) & 0xff);
+       i82527_write_reg (dev, port + 2, (val >> 8) & 0xff);
+       i82527_write_reg (dev, port + 3, (val >> 0) & 0xff);
+}
+
+static inline void i82527_read_msgdata (struct can_dev *dev, int iobj,
+                                       int n, u8 * data)
+{
+       int i;
+       u8 reg = (iobj * I82527_MSG_OFF) + I82527_MSG_DATA;
+
+       for (i = 0; i < n; i++)
+               data[i] = i82527_read_reg (dev, reg++);
+}
+
+static inline void i82527_write_msgdata (struct can_dev *dev, int iobj,
+                                        int n, u8 * data)
+{
+       int i;
+       u8 reg = (iobj * I82527_MSG_OFF) + I82527_MSG_DATA;
+
+       if (!n)
+               return;
+       i82527_msgflag (dev, iobj, I82527_CPUUPD_S);    /* CPU updating */
+       for (i = 0; i < n && i < 8; i++)
+               i82527_write_reg (dev, reg++, data[i]);
+       i82527_msgflag (dev, iobj, I82527_CPUUPD_R);
+}
+
+static struct i82527_times i82527_baudrates[] = {
+       {1, 0, 0, 2, 0, 0, 3, 12, 1},   /* 125K */
+       {1, 0, 0, 2, 0, 0, 1, 12, 1},   /* 250K */
+       {1, 0, 0, 2, 0, 0, 0, 12, 1}    /* 500K */
+};
+
+static void i82527_baudrate (struct can_dev *dev, unsigned int ibaud)
+{
+       struct i82527_times *times;
+       int i;
+
+       i = sizeof (i82527_baudrates) / sizeof (struct i82527_times);
+       if (ibaud >= i)
+               ibaud = i - 1;
+       times = &i82527_baudrates[ibaud];
+
+       i = i82527_read_reg (dev, I82527_CPU_INT_REG) &
+           ~(I82527_DSC | I82527_DMC);
+       i |= times->t_dsc * I82527_DSC;
+       i |= times->t_dmc * I82527_DMC;
+       i82527_write_reg (dev, I82527_CPU_INT_REG, i);
+
+       i = (times->t_clkout_div << I82527_CLKDIV_SHIFT)
+           | (times->t_clkout_slew << I82527_SL_SHIFT);
+       i82527_write_reg (dev, I82527_CLKOUT_REG, i);
+
+       i = (times->t_sjw << I82527_SJW_SHIFT)
+           | (times->t_brp << I82527_BRP_SHIFT);
+       i82527_write_reg (dev, I82527_BITT0_REG, i);
+
+       i = (times->t_spl << I82527_SPL_SHIFT)
+           | (times->t_tseg1 << I82527_TSEG1_SHIFT)
+           | (times->t_tseg2 << I82527_TSEG2_SHIFT);
+       i82527_write_reg (dev, I82527_BITT1_REG, i);
+
+}
+
+int i82527_init (struct can_dev *dev, unsigned int ibaud)
+{
+       int i;
+
+       i82527_write_reg (dev, I82527_CTRL_REG, I82527_CCE | I82527_INIT);
+
+       i82527_write_reg (dev, I82527_CLKOUT_REG, 3 << I82527_SL_SHIFT);
+       i82527_write_reg (dev, I82527_STAT_REG, 0x00);
+       i82527_write_reg (dev, I82527_P1CONF, 0x00);
+       i82527_write_reg (dev, I82527_P2CONF, 0x00);
+
+       i82527_write_15_mask (dev, 0x0);
+       i82527_write_std_mask (dev, 0xffff);
+       i82527_write_x_mask (dev, 0xffffffff);
+
+       /* Turn off all message objects and clear message identifiers */
+       for (i = I82527_FIRST_OBJ; i < I82527_MSG_OBJS; i++) {
+               i82527_write_msgctrl (dev, i,
+                                     I82527_MSGVAL_R & I82527_INTPND_R &
+                                     I82527_RMTPND_R & I82527_TXRQST_R &
+                                     I82527_MSGLST_R & I82527_NEWDAT_R);
+               i82527_write_msgarb (dev, i, 0);
+       }
+
+       /* Configure Bus Configuration Register */
+       i82527_write_reg (dev, I82527_BUSCFG_REG,
+                         I82527_COBY | I82527_DCT1 | I82527_DCR1);
+
+       /*
+        * Preconfigure CPU Interface Register. The XTAL of the CAN1
+        * contoller is connected to CLKOUT of the CAN0 contoller.
+        */
+       i82527_write_reg (dev, I82527_CPU_INT_REG,
+                         (dev) ? I82527_MUX : (I82527_MUX | I82527_CEN));
+
+       i82527_baudrate (dev, ibaud);
+
+       /* Inizialization end */
+       i82527_write_reg (dev, I82527_STAT_REG, I82527_STAT_REG_DEFAULT);
+
+       /* Start device */
+       i82527_write_reg (dev, I82527_CTRL_REG, 0);
+
+       return 0;
+}
+
+void i82527_config_xmit (struct can_dev *dev, struct can_msg *msg,
+                        int iobj, int xmit)
+{
+       int cfg;
+
+       /* it looks like message 15 fires an interrupt when re-enabled */
+       if (iobj != 15)
+               i82527_msgflag (dev, iobj, I82527_MSGVAL_R); /* being updated */
+
+       /* set DLC and X flag */
+       cfg = (msg->dlc << I82527_DLC_SHIFT);
+       msg->id <<= I82527_ID_STD_SHIFT;
+
+       i82527_write_msgarb (dev, iobj, msg->id);
+
+       if (xmit) {
+               if (msg->dlc > 8)
+                       msg->dlc = 8;
+
+               cfg |= I82527_DIR_TX;
+
+               i82527_write_msgcfg (dev, iobj, cfg);
+               /* write data bytes */
+               i82527_write_msgdata (dev, iobj, msg->dlc, msg->data);
+
+               /* set MSGVAL; then clear CPUUpd and set newdat */
+               i82527_msgflag (dev, iobj, I82527_MSGVAL_S);
+
+               i82527_msgflag (dev, iobj, I82527_CPUUPD_R & I82527_NEWDAT_S &
+                               I82527_TXRQST_S);
+       } else {
+               i82527_write_msgcfg (dev, iobj, cfg);
+               i82527_msgflag (dev, iobj, I82527_MSGVAL_S);
+               i82527_msgflag (dev, iobj, I82527_RMTPND_R & I82527_MSGLST_R &
+                               I82527_NEWDAT_R);
+       }
+}
+
+int i82527_xmit (struct can_dev *dev, struct can_msg *msg)
+{
+       int i;
+
+       i82527_config_xmit (dev, msg, I82527_TX_OBJ, 1);
+
+       for (i = 0; i < CAN_XMIT_TIMEOUT_US; i += 10000) {
+               if (i82527_read_reg (dev, I82527_STAT_REG) & I82527_TXOK)
+                       return 0;
+               if (ctrlc ())
+                       break;
+               udelay (10000);
+       }
+
+       return -1;
+}
+
+int i82527_recv (struct can_dev *dev, struct can_msg *msg)
+{
+       int cfg, iobj = I82527_RX_OBJ;
+
+       /* Clear status registers (RXOK) */
+       i82527_write_reg (dev, I82527_STAT_REG, I82527_STAT_REG_DEFAULT);
+
+       msg->id = 0;
+       msg->dlc = 0;
+       i82527_config_xmit (dev, msg, iobj, 0);
+
+       while (!(i82527_read_reg (dev, I82527_STAT_REG) & I82527_RXOK)) {
+               if (ctrlc ())
+                       return -1;
+       }
+
+       if (iobj != 15)
+               i82527_msgflag (dev, iobj, I82527_NEWDAT_R);
+
+       msg->id = i82527_read_msgarb (dev, iobj) >> I82527_ID_STD_SHIFT;
+
+       cfg = i82527_read_msgcfg (dev, iobj);
+
+       msg->dlc = (cfg & I82527_DLC_MASK) >> I82527_DLC_SHIFT;
+
+       i82527_read_msgdata (dev, iobj, msg->dlc, msg->data);
+
+       i82527_msgflag (dev, iobj, I82527_INTPND_R);
+       if (iobj == 15)
+               i82527_msgflag (dev, iobj, I82527_NEWDAT_R & I82527_RMTPND_R);
+
+       return 0;
+}
+
+int i82527_status (struct can_dev *dev, int level)
+{
+       printf ("i82527 at %#lx", dev->base);
+       if (level > 0) {
+               int stat = i82527_read_reg (dev, I82527_STAT_REG) & 0xff;
+               printf (", status 0x%02x", stat);
+               if (stat & I82527_BOFF)
+                       puts (" busoff");
+               if (stat & I82527_WARN)
+                       puts (" warning");
+               if (stat & I82527_WAKE)
+                       puts (" wake");
+               if (stat & I82527_RXOK)
+                       puts (" rxok");
+               if (stat & I82527_TXOK)
+                       puts (" txok");
+               stat &= I82527_LEC_MASK;
+               if (stat > 0 && stat < 7)
+                       printf (" lec=%d", stat);
+       }
+
+       puts ("\n");
+       if (level > 1) {
+               int i;
+               for (i = 0; i < I82527_SIZE; i++) {
+                       if ((i % 0x10) == 0)
+                               printf ("\n%02x:", i);
+                       printf (" %02x", i82527_read_reg (dev, i));
+               }
+               puts ("\n");
+       }
+       return 0;
+}
+
+void i82527_register (struct can_dev *dev, unsigned long base)
+{
+       dev->name = "i82527";
+       dev->base = base;
+       dev->init = i82527_init;
+       dev->xmit = i82527_xmit;
+       dev->recv = i82527_recv;
+       dev->status = i82527_status;
+
+       can_register (dev);
+}
diff --git a/include/i82527.h b/include/i82527.h
new file mode 100644
index 0000000..8cd70e2
--- /dev/null
+++ b/include/i82527.h
@@ -0,0 +1,201 @@
+/*
+ * (C) Copyright 2007-2009, Wolfgang Grandegger <w...@denx.de>
+ *
+ * Derived from OCAN driver:
+ *
+ * Copyright (C) 2002 Alessandro Rubini <rub...@linux.it>
+ * Copyright (C) 2002 System SpA <info.electron...@system-group.it>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __I82527_H__
+#define __I82527_H__
+
+#include <can.h>
+
+#define I82527_MSG_OBJS             16 /* 0 is the whole device, 1..15 are 
objects */
+#define I82527_FIRST_OBJ      1
+
+#define I82527_ID_STD_SHIFT  21
+#define I82527_ID_XTD_SHIFT   3
+
+/*
+ * i82527 address map (referred from base address) & internal bits.
+ * See the 82527 data-sheet page 9 and following
+*/
+#define I82527_MAP_SIZE   0x100
+
+#define I82527_CTRL_REG                0x00
+   #define I82527_CCE             0x40
+   #define I82527_EIE             0x08
+   #define I82527_SIE             0x04
+   #define I82527_IE              0x02
+   #define I82527_INIT            0x01
+
+#define I82527_STAT_REG                0x01
+   #define I82527_BOFF            0x80
+   #define I82527_WARN            0x40
+   #define I82527_WAKE            0x20
+   #define I82527_RXOK            0x10
+   #define I82527_TXOK            0x08
+   #define I82527_LEC_MASK        0x07
+#define I82527_STAT_REG_DEFAULT    I82527_LEC_MASK
+
+#define I82527_CPU_INT_REG     0x02
+   #define I82527_RSTST                   0x80
+   #define I82527_DSC             0x40
+   #define I82527_DMC             0x20
+   #define I82527_PWD             0x10
+   #define I82527_SLEEP                   0x08
+   #define I82527_MUX             0x04
+   #define I82527_CEN             0x01
+
+/* Reserved                     0x03 */
+
+#define I82527_HI_SPEED_RD     0x04
+#define I82527_GMASK_STD       0x06
+#define I82527_GMASK_XTD       0x08
+#define I82527_MSG15_MASK      0x0c
+
+/* Message 1                   0x10 */
+#define I82527_MSG_OFF         0x10   /* No size definition here */
+   #define I82527_MSG_CTRL        0x00
+      #define I82527_MSGVAL_R    0xff7f   /* *********************** */
+      #define I82527_MSGVAL_S    0xffbf   /* WARNING!!!              */
+      #define I82527_TXIE_R      0xffdf   /* These masks must be     */
+      #define I82527_TXIE_S      0xffef   /* &-ed and *NOT* |-ed     */
+      #define I82527_RXIE_R      0xfff7   /*                         */
+      #define I82527_RXIE_S      0xfffb   /*                         */
+      #define I82527_INTPND_R    0xfffd   /*                         */
+      #define I82527_INTPND_S    0xfffe   /*                         */
+      #define I82527_RMTPND_R    0x7fff   /* WARNING!!!              */
+      #define I82527_RMTPND_S    0xbfff   /* These masks must be     */
+      #define I82527_TXRQST_R    0xdfff   /* &-ed and *NOT* |-ed     */
+      #define I82527_TXRQST_S    0xefff   /*                         */
+      #define I82527_MSGLST_R    0xf7ff   /*                         */
+      #define I82527_MSGLST_S    0xfbff   /*                         */
+      #define I82527_CPUUPD_R    0xf7ff   /* WARNING!!!              */
+      #define I82527_CPUUPD_S    0xfbff   /* These masks must be     */
+      #define I82527_NEWDAT_R    0xfdff   /* &-ed and *NOT* |-ed     */
+      #define I82527_NEWDAT_S    0xfeff   /* *********************** */
+
+   #define I82527_MSG_ARBIT    0x02
+   #define I82527_MSG_CFG      0x06
+      #define I82527_DLC_MASK     0xf0
+      #define I82527_DLC_SHIFT    4
+      #define I82527_DLC_MASK0    0x0f
+      #define I82527_DIR          0x08
+         #define I82527_DIR_TX       0x08
+         #define I82527_DIR_RX       0x00
+      #define I82527_XTD          0x04
+         #define I82527_XTD_XTD              0x04
+         #define I82527_XTD_STD              0x00
+   #define I82527_MSG_DATA     0x07   /* 8 bytes */
+#define I82527_CLKOUT_REG      0x1f
+   #define I82527_SL_MASK         0x30
+   #define I82527_SL_SHIFT        4
+   #define I82527_SL_MASK0        0x03
+   #define I82527_CLKDIV_MASK     0x0f
+   #define I82527_CLKDIV_SHIFT    0
+   #define I82527_CLKDIV_MASK0    0x0f
+
+/* Message 2                   0x20 */
+#define I82527_BUSCFG_REG      0x2f
+   #define I82527_COBY            0x40
+   #define I82527_POL             0x20
+   #define I82527_DCT1            0x08
+   #define I82527_DCR1            0x02
+   #define I82527_DCR0            0x01
+
+/* Message 3                   0x30 */
+#define I82527_BITT0_REG       0x3f
+   #define I82527_SJW_MASK         0xc0
+   #define I82527_SJW_SHIFT        6
+   #define I82527_SJW_MASK0        0x03
+   #define I82527_BRP_MASK         0x3f
+   #define I82527_BRP_SHIFT        0
+   #define I82527_BRP_MASK0        0x3f
+
+/* Message 4                   0x40 */
+#define I82527_BITT1_REG       0x4f
+   #define I82527_SPL_MASK         0x80
+   #define I82527_SPL_SHIFT        7
+   #define I82527_SPL_MASK0        0x01
+   #define I82527_TSEG2_MASK       0x70
+   #define I82527_TSEG2_SHIFT      4
+   #define I82527_TSEG2_MASK0      0x07
+   #define I82527_TSEG1_MASK       0x0f
+   #define I82527_TSEG1_SHIFT      0
+   #define I82527_TSEG1_MASK0      0x0f
+
+/* Message 5                   0x50 */
+#define I82527_INT_REG         0x5f
+
+/* Message 6                   0x60 */
+/* Reserved                     0x6f */
+
+/* Message 7                   0x70 */
+/* Reserved                     0x7f */
+
+/* Message 8                   0x80 */
+/* Reserved                     0x8f */
+
+/* Message 9                   0x90 */
+#define I82527_P1CONF          0x9f
+
+/* Message 10                  0xa0 */
+#define I82527_P2CONF          0xaf
+
+/* Message 11                  0xb0 */
+#define I82527_P1IN            0xbf
+
+/* Message 12                  0xc0 */
+#define I82527_P2IN            0xcf
+
+/* Message 13                  0xd0 */
+#define I82527_P1OUT           0xdf
+
+/* Message 14                  0xe0 */
+#define I82527_P2OUT           0xef
+
+/* Message 15                  0xf0 */
+#define I82527_SER_RST_ADD     0xff
+
+
+#define I82527_SIZE            0x100
+
+/*
+ * Bit timing
+ */
+struct i82527_times {
+       u8  t_dsc;
+       u8  t_dmc;
+       u8  t_clkout_div;
+       u8  t_clkout_slew;
+       u8  t_sjw;
+       u8  t_spl;
+       u8  t_brp;
+       u8  t_tseg1;
+       u8  t_tseg2;
+};
+
+void i82527_register (struct can_dev *dev, unsigned long base);
+
+#endif /*__I82527_H__*/
-- 
1.6.2.5

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to