diff options
| author | wdenk <wdenk> | 2003-10-15 23:53:47 +0000 | 
|---|---|---|
| committer | wdenk <wdenk> | 2003-10-15 23:53:47 +0000 | 
| commit | 42d1f0394bef0624fc9664714d54bb137931d6a6 (patch) | |
| tree | 892a4130507484d25faf9a72e019cf88cfb3e3d9 /cpu/mpc85xx/i2c.c | |
| parent | 2d5b561e2bfdee8552a99b2cf93016cce2a74895 (diff) | |
| download | olio-uboot-2014.01-42d1f0394bef0624fc9664714d54bb137931d6a6.tar.xz olio-uboot-2014.01-42d1f0394bef0624fc9664714d54bb137931d6a6.zip | |
* Patches by Xianghua Xiao, 15 Oct 2003:LABEL_2003_10_16_0200
  - Added Motorola CPU 8540/8560 support (cpu/85xx)
  - Added Motorola MPC8540ADS board support (board/mpc8540ads)
  - Added Motorola MPC8560ADS board support (board/mpc8560ads)
* Minor code cleanup
Diffstat (limited to 'cpu/mpc85xx/i2c.c')
| -rw-r--r-- | cpu/mpc85xx/i2c.c | 288 | 
1 files changed, 288 insertions, 0 deletions
| diff --git a/cpu/mpc85xx/i2c.c b/cpu/mpc85xx/i2c.c new file mode 100644 index 000000000..ae08d1883 --- /dev/null +++ b/cpu/mpc85xx/i2c.c @@ -0,0 +1,288 @@ +/* + * (C) Copyright 2003,Motorola Inc. + * Xianghua Xiao <x.xiao@motorola.com> + * Adapted for Motorola 85xx chip. + * + * (C) Copyright 2003 + * Gleb Natapov <gnatapov@mrv.com> + * Some bits are taken from linux driver writen by adrian@humboldt.co.uk + * + * Hardware I2C driver for MPC107 PCI bridge. + * + * 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 <command.h> + +#define DEBUG + +#if defined(DEBUG) +#define DEB(x)      x +#else +#define DEB(x) +#endif + +#ifdef CONFIG_HARD_I2C +#include <i2c.h> + +#define TIMEOUT (CFG_HZ/4) + +#define I2C_Addr ((unsigned *)(CFG_CCSRBAR + 0x3000)) + +#define I2CADR  &I2C_Addr[0] +#define I2CFDR  &I2C_Addr[1] +#define I2CCCR  &I2C_Addr[2] +#define I2CCSR  &I2C_Addr[3] +#define I2CCDR  &I2C_Addr[4] +#define I2CDFSRR &I2C_Addr[5] + +#define I2C_READ  1 +#define I2C_WRITE 0 + +/* taken from linux include/asm-ppc/io.h */ +inline unsigned in_le32(volatile unsigned *addr) +{ +  unsigned ret; + +  __asm__ __volatile__("lwbrx %0,0,%1;\n" +		       "twi 0,%0,0;\n" +		       "isync" : "=r" (ret) : +		       "r" (addr), "m" (*addr)); +  return ret; +} + +inline void out_le32(volatile unsigned *addr, int val) +{ +  __asm__ __volatile__("stwbrx %1,0,%2; eieio" : "=m" (*addr) : +		       "r" (val), "r" (addr)); +} + +#define writel(val, addr) out_le32(addr, val) +#define readl(addr) in_le32(addr) + +void +i2c_init(int speed, int slaveadd) +{ +  /* stop I2C controller */ +  writel (0x0, I2CCCR); +  /* set clock */ +  writel (0x3f, I2CFDR); +  /* set default filter */ +  writel (0x10,I2CDFSRR); +  /* write slave address */ +  writel (slaveadd, I2CADR); +  /* clear status register */ +  writel (0x0, I2CCSR); +  /* start I2C controller */ +  writel (MPC85xx_I2CCR_MEN, I2CCCR); +} + +static __inline__ int +i2c_wait4bus (void) +{ +  ulong timeval = get_timer (0); + +  while (readl (I2CCSR) & MPC85xx_I2CSR_MBB) +    if (get_timer (timeval) > TIMEOUT) +      return -1; + +  return 0; +} + +static __inline__ int +i2c_wait (int write) +{ +  u32 csr; +  ulong timeval = get_timer (0); + +  do +    { +      csr  = readl (I2CCSR); + +      if (!(csr & MPC85xx_I2CSR_MIF)) +	continue; + +      writel (0x0, I2CCSR); + +      if (csr & MPC85xx_I2CSR_MAL) +	{ +	  DEB(printf ("i2c_wait: MAL\n")); +	  return -1; +	} + +      if (!(csr & MPC85xx_I2CSR_MCF)) +	{ +	  DEB(printf ("i2c_wait: unfinished\n")); +	  return -1; +	} + +      if (write == I2C_WRITE && (csr & MPC85xx_I2CSR_RXAK)) +	{ +	  DEB(printf ("i2c_wait: No RXACK\n")); +	  return -1; +	} + +      return 0; +    } while (get_timer (timeval) < TIMEOUT); + +  DEB(printf ("i2c_wait: timed out\n")); +  return -1; +} + +static __inline__ int +i2c_write_addr (u8 dev, u8 dir, int rsta) +{ +  writel (MPC85xx_I2CCR_MEN | MPC85xx_I2CCR_MSTA | MPC85xx_I2CCR_MTX | +	  (rsta?MPC85xx_I2CCR_RSTA:0), I2CCCR); + +  writel ((dev << 1) | dir, I2CCDR); + +  if (i2c_wait (I2C_WRITE) < 0) +    return 0; + +  return 1; +} + +static __inline__ int +__i2c_write (u8 *data, int length) +{ +  int i; + +  writel (MPC85xx_I2CCR_MEN | MPC85xx_I2CCR_MSTA | MPC85xx_I2CCR_MTX, I2CCCR); + +  for (i=0; i < length; i++) +    { +      writel (data[i], I2CCDR); + +      if (i2c_wait (I2C_WRITE) < 0) +	break; +    } + +  return i; +} + +static __inline__ int +__i2c_read (u8 *data, int length) +{ +  int i; + +  writel (MPC85xx_I2CCR_MEN | MPC85xx_I2CCR_MSTA | +	  ((length == 1) ? MPC85xx_I2CCR_TXAK : 0), I2CCCR); + +  /* dummy read */ +  readl (I2CCDR); + +  for (i=0; i < length; i++) +    { +      if (i2c_wait (I2C_READ) < 0) +	break; + +      /* Generate ack on last next to last byte */ +      if (i == length - 2) +	writel (MPC85xx_I2CCR_MEN | MPC85xx_I2CCR_MSTA | +		MPC85xx_I2CCR_TXAK, I2CCCR); + +      /* Generate stop on last byte */ +      if (i == length - 1) +	writel (MPC85xx_I2CCR_MEN | MPC85xx_I2CCR_TXAK, I2CCCR); + +      data[i] = readl (I2CCDR); +    } + +  return i; +} + +int +i2c_read (u8 dev, uint addr, int alen, u8 *data, int length) +{ +  int i = 0; +  u8 *a = (u8*)&addr; + +  if (i2c_wait4bus () < 0) +    goto exit; + +  if (i2c_write_addr (dev, I2C_WRITE, 0) == 0) +    goto exit; + +  if (__i2c_write (&a[4 - alen], alen) != alen) +    goto exit; + +  if (i2c_write_addr (dev, I2C_READ, 1) == 0) +    goto exit; + +  i = __i2c_read (data, length); + + exit: +  writel (MPC85xx_I2CCR_MEN, I2CCCR); + +  return !(i == length); +} + +int +i2c_write (u8 dev, uint addr, int alen, u8 *data, int length) +{ +  int i = 0; +  u8 *a = (u8*)&addr; + +  if (i2c_wait4bus () < 0) +    goto exit; + +  if (i2c_write_addr (dev, I2C_WRITE, 0) == 0) +    goto exit; + +  if (__i2c_write (&a[4 - alen], alen) != alen) +    goto exit; + +  i = __i2c_write (data, length); + + exit: +  writel (MPC85xx_I2CCR_MEN, I2CCCR); + +  return !(i == length); +} + +int i2c_probe (uchar chip) +{ +	int tmp; + +	/* +	 * Try to read the first location of the chip.  The underlying +	 * driver doesn't appear to support sending just the chip address +	 * and looking for an <ACK> back. +	 */ +	udelay(10000); +	return i2c_read (chip, 0, 1, (char *)&tmp, 1); +} + +uchar i2c_reg_read (uchar i2c_addr, uchar reg) +{ +	char buf[1]; + +	i2c_read (i2c_addr, reg, 1, buf, 1); + +	return (buf[0]); +} + +void i2c_reg_write (uchar i2c_addr, uchar reg, uchar val) +{ +	i2c_write (i2c_addr, reg, 1, &val, 1); +} + +#endif /* CONFIG_HARD_I2C */ |