[U-Boot] [RFC 4/5] CAN device driver for the Intel 82527
Wolfgang Grandegger
wg at grandegger.com
Sun Nov 1 12:33:36 CET 2009
From: Wolfgang Grandegger <wg at denx.de>
Signed-off-by: Wolfgang Grandegger <wg at 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 <wg at denx.de>
+ *
+ * Derived from OCAN driver:
+ *
+ * Copyright (C) 2002 Alessandro Rubini <rubini at linux.it>
+ * Copyright (C) 2002 System SpA <info.electronics at 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 <wg at denx.de>
+ *
+ * Derived from OCAN driver:
+ *
+ * Copyright (C) 2002 Alessandro Rubini <rubini at linux.it>
+ * Copyright (C) 2002 System SpA <info.electronics at 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
More information about the U-Boot
mailing list