[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