[U-Boot] [RFC 3/5] CAN device driver for the SJA1000

Wolfgang Grandegger wg at grandegger.com
Sun Nov 1 12:33:35 CET 2009


From: Wolfgang Grandegger <wg at denx.de>

Signed-off-by: Wolfgang Grandegger <wg at denx.de>
---
 drivers/can/Makefile  |    3 +-
 drivers/can/sja1000.c |  223 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/sja1000.h     |  159 +++++++++++++++++++++++++++++++++++
 3 files changed, 384 insertions(+), 1 deletions(-)
 create mode 100644 drivers/can/sja1000.c
 create mode 100644 include/sja1000.h

diff --git a/drivers/can/Makefile b/drivers/can/Makefile
index 74d2ff5..e2b6bd6 100644
--- a/drivers/can/Makefile
+++ b/drivers/can/Makefile
@@ -25,7 +25,8 @@ include $(TOPDIR)/config.mk
 
 LIB 	:= $(obj)libcan.a
 
-COBJS-$(CONFIG_CAN)	+= can.o
+COBJS-$(CONFIG_CAN)		+= can.o
+COBJS-$(CONFIG_CAN_SJA1000)	+= sja1000.o
 
 COBJS	:= $(COBJS-y)
 SRCS 	:= $(COBJS:.o=.c)
diff --git a/drivers/can/sja1000.c b/drivers/can/sja1000.c
new file mode 100644
index 0000000..b75f01c
--- /dev/null
+++ b/drivers/can/sja1000.c
@@ -0,0 +1,223 @@
+/*
+ * (C) Copyright 2007-2009, Wolfgang Grandegger <wg at denx.de>
+ *
+ * Derived from Xenomai's RT-Socket-CAN driver for SJA1000:
+ *
+ * Copyright (C) 2005,2006 Sebastian Smolorz
+ *                        <Sebastian.Smolorz at stud.uni-hannover.de>
+ *
+ * Copyright (C) 2005, Sascha Hauer, Pengutronix
+ *
+ * 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 <sja1000.h>
+
+#define SJA1000_OCR	(SJA_OCR_MODE_NORMAL | SJA_OCR_TX0_PUSHPULL)
+#define SJA1000_CDR	SJA_CDR_CAN_MODE
+
+/*
+ * Basic functions to access registers
+ */
+#define sja1000_read_reg(dev, reg)			\
+	in_8 ((volatile u8 *)((dev)->base + (reg)))
+
+#define sja1000_write_reg(dev, reg, value)			\
+	out_8 ((volatile u8 *)((dev)->base + (reg)), value)
+
+/*
+ * Baudrate table
+ */
+
+static u16 sja1000_btr0btr1[] = {
+	0x031c,			/* 125K */
+	0x011c,			/* 250K */
+	0x001c,			/* 500K */
+};
+
+int sja1000_init (struct can_dev *dev, unsigned int ibaud)
+{
+	int i, wait = 1000;
+	u16 btr0btr1;
+
+	/* Disable the controller's interrupts */
+	sja1000_write_reg (dev, SJA_IER, 0x00);
+
+	/* Set reset mode bit */
+	sja1000_write_reg (dev, SJA_MOD, SJA_MOD_RM);
+
+	/* Read reset mode bit, multiple tests */
+	do {
+		udelay (100);
+		if (sja1000_read_reg (dev, SJA_MOD) & SJA_MOD_RM)
+			break;
+	} while (--wait);
+
+	sja1000_write_reg (dev, SJA_CDR, SJA1000_CDR);
+	sja1000_write_reg (dev, SJA_OCR, SJA1000_OCR);
+
+	sja1000_write_reg (dev, SJA_AMR0, 0xFF);
+	sja1000_write_reg (dev, SJA_AMR1, 0xFF);
+	sja1000_write_reg (dev, SJA_AMR2, 0xFF);
+	sja1000_write_reg (dev, SJA_AMR3, 0xFF);
+
+	sja1000_write_reg (dev, SJA_RXERR, 0);
+	sja1000_write_reg (dev, SJA_TXERR, 0);
+
+	i = sizeof (sja1000_btr0btr1) / sizeof (u16);
+	if (ibaud >= i)
+		ibaud = i - 1;
+	btr0btr1 = sja1000_btr0btr1[ibaud];
+	sja1000_write_reg (dev, SJA_BTR0, (btr0btr1 >> 8) & 0xff);
+	sja1000_write_reg (dev, SJA_BTR1, (btr0btr1 & 0xff));
+
+	/* Clear error code capture (i.e. read it) */
+	sja1000_read_reg (dev, SJA_ECC);
+
+	/* Clear reset mode bit in SJA1000 */
+	sja1000_write_reg (dev, SJA_MOD, 0);
+
+	return 0;
+}
+
+int sja1000_xmit (struct can_dev *dev, struct can_msg *msg)
+{
+	int i;
+	u8 fir;
+
+	if (msg->dlc > 8)
+		msg->dlc = 8;
+	fir = msg->dlc;
+
+	sja1000_write_reg (dev, SJA_ID1, msg->id >> 3);
+	sja1000_write_reg (dev, SJA_ID2, msg->id << 5);
+	for (i = 0; i < msg->dlc; i++)
+		sja1000_write_reg (dev, SJA_DATA_SFF (i), msg->data[i]);
+
+	/* Write frame information register */
+	sja1000_write_reg (dev, SJA_FIR, fir);
+
+	/* Push the 'send' button */
+	sja1000_write_reg (dev, SJA_CMR, SJA_CMR_TR);
+
+	/* Wait some time */
+	for (i = 0; i < CAN_XMIT_TIMEOUT_US; i += 10000) {
+		if (sja1000_read_reg (dev, SJA_SR) & SJA_SR_TCS)
+			return 0;
+		if (ctrlc ())
+			break;
+		udelay (10000);
+	}
+
+	return -1;
+}
+
+int sja1000_recv (struct can_dev *dev, struct can_msg *msg)
+{
+	int i;
+	u8 fir;
+
+	while (!(sja1000_read_reg (dev, SJA_SR) & SJA_SR_RBS)) {
+		if (ctrlc ())
+			return -1;
+	}
+
+	/* Read out frame information register */
+	fir = sja1000_read_reg (dev, SJA_FIR);
+
+	/* Extract data length code */
+	msg->dlc = fir & SJA_FIR_DLC_MASK;
+
+	/* If DLC exceeds 8 bytes adjust it to 8 (for the payload size) */
+	if (msg->dlc > 8)
+		msg->dlc = 8;
+
+	if (fir & SJA_FIR_EFF) {
+		printf ("Extended CAN messages not supported\n");
+		return -1;
+	} else {
+		msg->id = sja1000_read_reg (dev, SJA_ID1) << 3;
+		msg->id |= sja1000_read_reg (dev, SJA_ID2) >> 5;
+
+		if (!(fir & SJA_FIR_RTR)) {
+			for (i = 0; i < msg->dlc; i++)
+				msg->data[i] =
+				    sja1000_read_reg (dev, SJA_DATA_SFF (i));
+		}
+	}
+	if (fir & SJA_FIR_RTR)
+		msg->id |= CAN_RTR_FLAG;
+
+	/* Release Receive Buffer */
+	sja1000_write_reg (dev, SJA_CMR, SJA_CMR_RRB);
+
+	return 0;
+}
+
+int sja1000_status (struct can_dev *dev, int level)
+{
+	printf ("SJA1000 at %#x", dev->base);
+	if (level > 0) {
+		int stat = sja1000_read_reg (dev, SJA_SR) & 0xff;
+		printf (", status 0x%02x", stat);
+		if (stat & SJA_SR_BS)
+			puts (" busoff");
+		if (stat & SJA_SR_ES)
+			puts (" error");
+		if (stat & SJA_SR_TS)
+			puts (" txing");
+		if (stat & SJA_SR_RS)
+			puts (" rxing");
+		if (stat & SJA_SR_TCS)
+			puts (" txdone");
+		if (stat & SJA_SR_TBS)
+			puts (" txfree");
+		if (stat & SJA_SR_DOS)
+			puts (" overrun");
+		if (stat & SJA_SR_RBS)
+			puts (" rxfull");
+	}
+	puts ("\n");
+	if (level > 1) {
+		int i;
+		for (i = 0; i < SJA1000_SIZE; i++) {
+			if ((i % 0x10) == 0)
+				printf ("\n%02x:", i);
+			printf (" %02x", sja1000_read_reg (dev, i));
+		}
+		puts ("\n");
+	}
+	return 0;
+}
+
+void sja1000_register (struct can_dev *dev, unsigned long base)
+{
+	dev->name = "sja1000";
+	dev->base = base;
+	dev->init = sja1000_init;
+	dev->xmit = sja1000_xmit;
+	dev->recv = sja1000_recv;
+	dev->status = sja1000_status;
+
+	can_register (dev);
+}
diff --git a/include/sja1000.h b/include/sja1000.h
new file mode 100644
index 0000000..56b43bf
--- /dev/null
+++ b/include/sja1000.h
@@ -0,0 +1,159 @@
+/*
+ * (C) Copyright 2007-2009, Wolfgang Grandegger <wg at denx.de>
+ *
+ * Derived from Xenomai's RT-Socket-CAN driver for SJA1000:
+ *
+ * Copyright (C) 2005,2006 Sebastian Smolorz
+ *                        <Sebastian.Smolorz at stud.uni-hannover.de>
+ *
+ * Copyright (C) 2005, Sascha Hauer, Pengutronix
+ *
+ * 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 __SJA1000_H__
+#define __SJA1000_H__
+
+#include <can.h>
+
+/* PeliCAN mode address map */
+
+/* reset and operating mode */
+#define SJA_MOD          0       /* Mode register                   */
+#define SJA_CMR          1       /* Command register                */
+#define SJA_SR           2       /* Status register                 */
+#define SJA_IR           3       /* Interrupt register              */
+#define SJA_IER          4       /* Interrupt enable register       */
+#define SJA_BTR0         6       /* Bus timing register 0           */
+#define SJA_BTR1         7       /* Bus timing register 1           */
+#define SJA_OCR          8       /* Output control register         */
+#define SJA_ALC         11       /* Arbitration lost capture        */
+#define SJA_ECC         12       /* Error code capture register     */
+#define SJA_RXERR       14       /* Receive error counter           */
+#define SJA_TXERR       15       /* Transmit error counter          */
+#define SJA_CDR         31       /* Clock divider register          */
+
+/* reset mode */
+#define SJA_ACR0        16       /* Acceptance code register 0      */
+#define SJA_ACR1        17       /* Acceptance code register 1      */
+#define SJA_ACR2        18       /* Acceptance code register 2      */
+#define SJA_ACR3        19       /* Acceptance code register 3      */
+#define SJA_AMR0        20       /* Acceptance mask register 0      */
+#define SJA_AMR1        21       /* Acceptance mask register 1      */
+#define SJA_AMR2        22       /* Acceptance mask register 2      */
+#define SJA_AMR3        23       /* Acceptance mask register 3      */
+
+/* operating mode */
+#define SJA_FIR         16       /* Frame information register      */
+#define SJA_ID1         17       /* Identifier 1                    */
+#define SJA_ID2         18       /* Identifier 2                    */
+#define SJA_ID3         19       /* Identifier 3 (EFF only)         */
+#define SJA_ID4         20       /* Identifier 4 (EFF only)         */
+
+#define SJA_DATA_SFF(x) (19 + (x)) /* Data registers in case of standard
+                                    * frame format; 0 <= x <= 7 */
+#define SJA_DATA_EFF(x) (21 + (x)) /* Data registers in case of extended
+                                    * frame format; 0 <= x <= 7 */
+/* Mode register */
+#define SJA_MOD_RM           (1<<0) /* Reset Mode                          */
+#define SJA_MOD_LOM          (1<<1) /* Listen Only Mode                    */
+#define SJA_MOD_STM          (1<<2) /* Self Test Mode                      */
+#define SJA_MOD_AFM          (1<<3) /* Acceptance Filter Mode              */
+#define SJA_MOD_SM           (1<<4) /* Sleep Mode                          */
+
+/* Command register */
+#define SJA_CMR_TR           (1<<0) /* Transmission request                */
+#define SJA_CMR_AT           (1<<1) /* Abort Transmission                  */
+#define SJA_CMR_RRB          (1<<2) /* Release Receive Buffer              */
+#define SJA_CMR_CDO          (1<<3) /* Clear Data Overrun                  */
+#define SJA_CMR_SRR          (1<<4) /* Self reception request              */
+
+/* Status register */
+#define SJA_SR_RBS           (1<<0) /* Receive Buffer Status               */
+#define SJA_SR_DOS           (1<<1) /* Data Overrun Status                 */
+#define SJA_SR_TBS           (1<<2) /* Transmit Buffer Status              */
+#define SJA_SR_TCS           (1<<3) /* Transmission Complete Status        */
+#define SJA_SR_RS            (1<<4) /* Receive Status                      */
+#define SJA_SR_TS            (1<<5) /* Transmit Status                     */
+#define SJA_SR_ES            (1<<6) /* Error Status                        */
+#define SJA_SR_BS            (1<<7) /* Bus Status                          */
+
+/* Interrupt register */
+#define SJA_IR_RI            (1<<0) /* Receive Interrupt                   */
+#define SJA_IR_TI            (1<<1) /* Transmit Interrupt                  */
+#define SJA_IR_EI            (1<<2) /* Error Warning Interrupt             */
+#define SJA_IR_DOI           (1<<3) /* Data Overrun Interrupt              */
+#define SJA_IR_WUI           (1<<4) /* Wake-Up Interrupt                   */
+#define SJA_IR_EPI           (1<<5) /* Error Passive Interrupt             */
+#define SJA_IR_ALI           (1<<6) /* Arbitration Lost Interrupt          */
+#define SJA_IR_BEI           (1<<7) /* Bus Error Interrupt                 */
+
+/* Interrupt enable register */
+#define SJA_IER_RIE          (1<<0) /* Receive Interrupt Enable            */
+#define SJA_IER_TIE          (1<<1) /* Transmit Interrupt Enable           */
+#define SJA_IER_EIE          (1<<2) /* Error Warning Interrupt Enable      */
+#define SJA_IER_DOIE         (1<<3) /* Data Overrun Interrupt Enable       */
+#define SJA_IER_WUIE         (1<<4) /* Wake-Up Interrupt Enable            */
+#define SJA_IER_EPIE         (1<<5) /* Error Passive Interrupt Enable      */
+#define SJA_IER_ALIE         (1<<6) /* Arbitration Lost Interrupt Enable   */
+#define SJA_IER_BEIE         (1<<7) /* Bus Error Interrupt Enable          */
+
+/* Output control register */
+#define SJA_OCR_MODE_BIPHASE 0
+#define SJA_OCR_MODE_TEST    1
+#define SJA_OCR_MODE_NORMAL  2
+#define SJA_OCR_MODE_CLOCK   3
+#define SJA_OCR_TX0_INVERT   (1<<2)
+#define SJA_OCR_TX0_PULLDOWN (1<<3)
+#define SJA_OCR_TX0_PULLUP   (2<<3)
+#define SJA_OCR_TX0_PUSHPULL (3<<3)
+#define SJA_OCR_TX1_INVERT   (1<<5)
+#define SJA_OCR_TX1_PULLDOWN (1<<6)
+#define SJA_OCR_TX1_PULLUP   (2<<6)
+#define SJA_OCR_TX1_PUSHPULL (3<<6)
+
+/* Error code capture register */
+
+/*
+ * The segmentation field gives information about the location of
+ * errors on the bus
+ */
+#define SJA_ECC_SEG_MASK     31     /* Segmentation field mask             */
+#define SJA_ECC_DIR          (1<<5) /* Transfer direction                  */
+#define SJA_ECC_ERR_BIT      (0<<6)
+#define SJA_ECC_ERR_FORM     (1<<6)
+#define SJA_ECC_ERR_STUFF    (2<<6)
+#define SJA_ECC_ERR_MASK     (3<<6) /* Error code mask                     */
+
+/* Frame information register */
+#define SJA_FIR_DLC_MASK     15     /* Data length code mask               */
+#define SJA_FIR_RTR          (1<<6) /* Remote transmission request         */
+#define SJA_FIR_EFF          (1<<7) /* Extended frame format               */
+
+/* Clock divider register */
+#define SJA_CDR_CLK_OFF      (1<<3) /* Clock off (CLKOUT pin)              */
+#define SJA_CDR_CBP          (1<<6) /* CAN input comparator bypass         */
+#define SJA_CDR_CAN_MODE     (1<<7) /* CAN mode: 1 = PeliCAN               */
+
+#define SJA1000_SIZE	     0x80
+
+void sja1000_register (struct can_dev *dev, unsigned long base);
+
+#endif  /* __SJA1000_H__ */
+
-- 
1.6.2.5



More information about the U-Boot mailing list