123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- /*
- * (C) Copyright 2006 Freescale Semiconductor, Inc.
- *
- * (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
- *
- * Change log:
- *
- * 20050101: Eran Liberty (liberty@freescale.com)
- * Initial file creating (porting from 85XX & 8260)
- * 20060601: Dave Liu (daveliu@freescale.com)
- * Unified variable names for mpc83xx
- */
- #include <common.h>
- #include <command.h>
- #include <asm/io.h>
- #ifdef CONFIG_HARD_I2C
- #include <i2c.h>
- #include <asm/i2c.h>
- DECLARE_GLOBAL_DATA_PTR;
- /* Three I2C bus speeds are supported here (50kHz, 100kHz
- * and 400kHz). It should be easy to add more. Note that
- * the maximum bus speed for I2C bus 1 is CSB/3, while I2C
- * bus 2 can go as high as CSB.
- * Typical values for CSB are 266MHz and 200MHz. */
- /* 50kH 100kHz 400kHz */
- static const uchar speed_map_266[][3] =
- {{0x2e, 0x2a, 0x20}, /* base 88MHz */
- {0x34, 0x30, 0x28}}; /* base 266 MHz */
- static const uchar speed_map_200[][3] =
- {{0x2c, 0x28, 0x20}, /* base 66 MHz */
- {0x33, 0x2f, 0x26}}; /* base 200 MHz */
- /* Initialize the bus pointer to whatever one the SPD EEPROM is on.
- * Default is bus 1. This is necessary because the DDR initialization
- * runs from ROM, and we can't switch buses because we can't modify
- * the i2c_dev variable. Everything gets straightened out once i2c_init
- * is called from RAM. */
- #if defined CFG_SPD_BUS_NUM
- static i2c_t *i2c_dev = CFG_SPD_BUS_NUM;
- #else
- static i2c_t *i2c_dev = I2C_1;
- #endif
- static uchar busNum = I2C_BUS_1 ;
- static int bus_speed[2] = {0, 0};
- static int set_speed(int speed)
- {
- uchar value;
- const uchar *spdPtr;
- /* Global data contains maximum I2C bus 1 speed, which is CSB/3 */
- if(gd->i2c_clk == 88000000)
- {
- spdPtr = speed_map_266[busNum];
- }
- else if(gd->i2c_clk == 66000000)
- {
- spdPtr = speed_map_200[busNum];
- }
- else
- {
- printf("Max I2C bus speed %d not supported\n", gd->i2c_clk);
- return -1;
- }
- switch(speed)
- {
- case 50000:
- value = *(spdPtr + 0);
- break;
- case 100000:
- value = *(spdPtr + 1);
- break;
- case 400000:
- value = *(spdPtr + 2);
- break;
- default:
- printf("I2C bus speed %d not supported\n", speed);
- return -2;
- }
- /* set clock */
- writeb(value, &i2c_dev->fdr);
- bus_speed[busNum] = speed;
- return 0;
- }
- static void _i2c_init(int speed, int slaveadd)
- {
- /* stop I2C controller */
- writeb(0x00 , &i2c_dev->cr);
- /* set clock */
- writeb(speed, &i2c_dev->fdr);
- /* set default filter */
- writeb(0x10,&i2c_dev->dfsrr);
- /* write slave address */
- writeb(slaveadd, &i2c_dev->adr);
- /* clear status register */
- writeb(0x00, &i2c_dev->sr);
- /* start I2C controller */
- writeb(I2C_CR_MEN, &i2c_dev->cr);
- }
- void i2c_init(int speed, int slaveadd)
- {
- /* Set both interfaces to the same speed and slave address */
- /* Note: This function gets called twice - before and after
- * relocation to RAM. The first time it's called, we are unable
- * to change buses, so whichever one 'i2c_dev' was initialized to
- * gets set twice. When run from RAM both buses get set properly */
- i2c_set_bus_num(I2C_BUS_1);
- _i2c_init(speed, slaveadd);
- #ifdef CFG_I2C2_OFFSET
- i2c_set_bus_num(I2C_BUS_2);
- _i2c_init(speed, slaveadd);
- i2c_set_bus_num(I2C_BUS_1);
- #endif /* CFG_I2C2_OFFSET */
- }
- static __inline__ int
- i2c_wait4bus (void)
- {
- ulong timeval = get_timer (0);
- while (readb(&i2c_dev->sr) & I2C_SR_MBB) {
- if (get_timer (timeval) > I2C_TIMEOUT) {
- return -1;
- }
- }
- return 0;
- }
- static __inline__ int
- i2c_wait (int write)
- {
- u32 csr;
- ulong timeval = get_timer(0);
- do {
- csr = readb(&i2c_dev->sr);
- if (!(csr & I2C_SR_MIF))
- continue;
- writeb(0x0, &i2c_dev->sr);
- if (csr & I2C_SR_MAL) {
- debug("i2c_wait: MAL\n");
- return -1;
- }
- if (!(csr & I2C_SR_MCF)) {
- debug("i2c_wait: unfinished\n");
- return -1;
- }
- if (write == I2C_WRITE && (csr & I2C_SR_RXAK)) {
- debug("i2c_wait: No RXACK\n");
- return -1;
- }
- return 0;
- } while (get_timer (timeval) < I2C_TIMEOUT);
- debug("i2c_wait: timed out\n");
- return -1;
- }
- static __inline__ int
- i2c_write_addr (u8 dev, u8 dir, int rsta)
- {
- writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX |
- (rsta?I2C_CR_RSTA:0),
- &i2c_dev->cr);
- writeb((dev << 1) | dir, &i2c_dev->dr);
- if (i2c_wait (I2C_WRITE) < 0)
- return 0;
- return 1;
- }
- static __inline__ int
- __i2c_write (u8 *data, int length)
- {
- int i;
- writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX,
- &i2c_dev->cr);
- for (i=0; i < length; i++) {
- writeb(data[i], &i2c_dev->dr);
- if (i2c_wait (I2C_WRITE) < 0)
- break;
- }
- return i;
- }
- static __inline__ int
- __i2c_read (u8 *data, int length)
- {
- int i;
- writeb(I2C_CR_MEN | I2C_CR_MSTA |
- ((length == 1) ? I2C_CR_TXAK : 0),
- &i2c_dev->cr);
- /* dummy read */
- readb(&i2c_dev->dr);
- 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)
- writeb(I2C_CR_MEN | I2C_CR_MSTA |
- I2C_CR_TXAK,
- &i2c_dev->cr);
- /* Generate stop on last byte */
- if (i == length - 1)
- writeb(I2C_CR_MEN | I2C_CR_TXAK, &i2c_dev->cr);
- data[i] = readb(&i2c_dev->dr);
- }
- 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:
- writeb(I2C_CR_MEN, &i2c_dev->cr);
- 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:
- writeb(I2C_CR_MEN, &i2c_dev->cr);
- 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, (uchar *)&tmp, 1);
- }
- uchar i2c_reg_read (uchar i2c_addr, uchar reg)
- {
- uchar 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);
- }
- int i2c_set_bus_num(uchar bus)
- {
- if(bus == I2C_BUS_1)
- {
- i2c_dev = I2C_1;
- }
- #ifdef CFG_I2C2_OFFSET
- else if(bus == I2C_BUS_2)
- {
- i2c_dev = I2C_2;
- }
- #endif /* CFG_I2C2_OFFSET */
- else
- {
- return -1;
- }
- busNum = bus;
- return 0;
- }
- int i2c_set_bus_speed(int speed)
- {
- return set_speed(speed);
- }
- uchar i2c_get_bus_num(void)
- {
- return busNum;
- }
- int i2c_get_bus_speed(void)
- {
- return bus_speed[busNum];
- }
- #endif /* CONFIG_HARD_I2C */
|