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