diff options
author | Claas Ziemke <ziemke@irs.uni-stuttgart.de> | 2012-08-22 14:39:02 +0200 |
---|---|---|
committer | Joel Sherrill <joel.sherrill@oarcorp.com> | 2014-11-03 14:19:47 -0600 |
commit | 7a669866eea8b66b87b22015b793b5ea4ff950c1 (patch) | |
tree | f7faffb2cd20b45894ddabbc60f1a0e5ee96403d /c/src/lib/libbsp/arm/beagle/misc/i2c.c | |
parent | Add some generic ARM am335x and omap definitions (diff) | |
download | rtems-7a669866eea8b66b87b22015b793b5ea4ff950c1.tar.bz2 |
Added BeagleBoard BSP
Coding done in course of GSoC2012.
Commit edited to be brought up-to-date with mainline by
Ben Gras <beng@shrike-systems.com>.
Diffstat (limited to 'c/src/lib/libbsp/arm/beagle/misc/i2c.c')
-rw-r--r-- | c/src/lib/libbsp/arm/beagle/misc/i2c.c | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/arm/beagle/misc/i2c.c b/c/src/lib/libbsp/arm/beagle/misc/i2c.c new file mode 100644 index 0000000000..322c501d3c --- /dev/null +++ b/c/src/lib/libbsp/arm/beagle/misc/i2c.c @@ -0,0 +1,450 @@ +/** + * @file + * + * @ingroup beagle_i2c + * + * @brief I2C support implementation. + */ + +/* + * Copyright (c) 2012 Claas Ziemke. All rights reserved. + * + * Claas Ziemke + * Kernerstrasse 11 + * 70182 Stuttgart + * Germany + * <claas.ziemke@gmx.net> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#include <rtems.h> + +#include <bsp.h> +#include <bsp/i2c.h> + +static struct i2c *i2c_base = (struct i2c *)I2C_DEFAULT_BASE; + +static unsigned short wait_for_pin( void ) { + + unsigned short status; + int timeout = I2C_TIMEOUT; + + do { + udelay( 1000 ); + status = readw( &i2c_base->stat ); + } while( !( status & + ( I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY | + I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK | + I2C_STAT_AL ) ) && timeout-- ); + + if( timeout <= 0 ) { + printk( "timed out in wait_for_pin: I2C_STAT = %x\n", + readw( &i2c_base->stat ) ); + writew( 0xFFFF, &i2c_base->stat ); /* clear current interrupts...*/ + status = 0; + } + + return status; +} + +static void wait_for_bb( void ) { + + int timeout = I2C_TIMEOUT; + unsigned short status; + + writew( 0xFFFF, &i2c_base->stat ); /* clear current interrupts...*/ + while( ( status = readw( &i2c_base->stat ) & I2C_STAT_BB ) && timeout-- ) { + writew( status, &i2c_base->stat ); + udelay( 1000 ); + } + + if( timeout <= 0 ) { + printk( "timed out in wait_for_bb: I2C_STAT = %x\n", + readw( &i2c_base->stat ) ); + } + writew( 0xFFFF, &i2c_base->stat ); /* clear delayed stuff*/ +} + +static void flush_fifo( void ) { + + unsigned short status; + + /* note: if you try and read data when its not there or ready + * you get a bus error + */ + while( 1 ) { + status = readw( &i2c_base->stat ); + if( status == I2C_STAT_RRDY ) { + readw( &i2c_base->data ); + writew( I2C_STAT_RRDY, &i2c_base->stat ); + udelay( 1000 ); + } else { + break; + } + } +} + +void i2c_init( int speed, int slaveadd ) { + + int psc, fsscll, fssclh; + int hsscll = 0, hssclh = 0; + unsigned int scll, sclh; + int timeout = I2C_TIMEOUT; + + // Only handle standard, fast and high speeds + if( ( speed != OMAP_I2C_STANDARD ) && + ( speed != OMAP_I2C_FAST_MODE ) && + ( speed != OMAP_I2C_HIGH_SPEED ) ) { + printk( "Error : I2C unsupported speed %d\n", speed ); + return; + } + + psc = I2C_IP_CLK / I2C_INTERNAL_SAMPLING_CLK; + psc -= 1; + if( psc < I2C_PSC_MIN ) { + printk( "Error : I2C unsupported prescalar %d\n", psc ); + return; + } + + if( speed == OMAP_I2C_HIGH_SPEED ) { + // High speed + + // For first phase of HS mode + fsscll = fssclh = I2C_INTERNAL_SAMPLING_CLK / ( 2 * OMAP_I2C_FAST_MODE ); + + fsscll -= I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM; + fssclh -= I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM; + if( ( ( fsscll < 0 ) || ( fssclh < 0 ) ) || ( ( fsscll > 255 ) || + ( fssclh > 255 ) ) ) { + printk( "Error : I2C initializing first phase clock\n" ); + return; + } + + // For second phase of HS mode + hsscll = hssclh = I2C_INTERNAL_SAMPLING_CLK / ( 2 * speed ); + + hsscll -= I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM; + hssclh -= I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM; + if( ( ( fsscll < 0 ) || ( fssclh < 0 ) ) || ( ( fsscll > 255 ) || + ( fssclh > 255 ) ) ) { + printk( "Error : I2C initializing second phase clock\n" ); + return; + } + + scll = ( unsigned int ) hsscll << 8 | ( unsigned int ) fsscll; + sclh = ( unsigned int ) hssclh << 8 | ( unsigned int ) fssclh; + + } else { + // Standard and fast speed + fsscll = fssclh = I2C_INTERNAL_SAMPLING_CLK / ( 2 * speed ); + + fsscll -= I2C_FASTSPEED_SCLL_TRIM; + fssclh -= I2C_FASTSPEED_SCLH_TRIM; + if( ( ( fsscll < 0 ) || ( fssclh < 0 ) ) || ( ( fsscll > 255 ) || + ( fssclh > 255 ) ) ) { + printk( "Error : I2C initializing clock\n" ); + return; + } + + scll = ( unsigned int ) fsscll; + sclh = ( unsigned int ) fssclh; + } + + if( readw( &i2c_base->con ) & I2C_CON_EN ) { + writew( 0, &i2c_base->con ); + udelay( 50000 ); + } + + writew( 0x2, &i2c_base->sysc ); /* for ES2 after soft reset */ + udelay( 1000 ); + + writew( I2C_CON_EN, &i2c_base->con ); + while( !( readw( &i2c_base->syss ) & I2C_SYSS_RDONE ) && timeout-- ) { + if (timeout <= 0) { + printk( "ERROR: Timeout in soft-reset\n" ); + return; + } + udelay( 1000 ); + } + + writew( 0, &i2c_base->con ); + writew( psc, &i2c_base->psc ); + writew( scll, &i2c_base->scll ); + writew( sclh, &i2c_base->sclh ); + + /* own address */ + writew( slaveadd, &i2c_base->oa ); + writew( I2C_CON_EN, &i2c_base->con ); + + /* have to enable intrrupts or OMAP i2c module doesn't work */ + writew( I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | I2C_IE_NACK_IE | + I2C_IE_AL_IE, &i2c_base->ie ); + udelay( 1000 ); + flush_fifo(); + writew( 0xFFFF, &i2c_base->stat ); + writew( 0, &i2c_base->cnt ); + + //if( gd->flags & GD_FLG_RELOC ) bus_initialized[ current_bus ] = 1; +} + +static int i2c_read_byte( + unsigned char devaddr, + unsigned char regoffset, + unsigned char *value +) +{ + int i2c_error = 0; + unsigned short status; + + /* wait until bus not busy */ + wait_for_bb(); + + /* one byte only */ + writew(1, &i2c_base->cnt); + /* set slave address */ + writew(devaddr, &i2c_base->sa); + /* no stop bit needed here */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | + I2C_CON_TRX, &i2c_base->con); + + /* send register offset */ + while (1) { + status = wait_for_pin(); + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + goto read_exit; + } + if (status & I2C_STAT_XRDY) { + /* Important: have to use byte access */ + writeb(regoffset, &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + + /* set slave address */ + writew(devaddr, &i2c_base->sa); + /* read one byte from slave */ + writew(1, &i2c_base->cnt); + /* need stop bit here */ + writew(I2C_CON_EN | I2C_CON_MST | + I2C_CON_STT | I2C_CON_STP, + &i2c_base->con); + + /* receive data */ + while (1) { + status = wait_for_pin(); + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + goto read_exit; + } + if (status & I2C_STAT_RRDY) { + *value = readw(&i2c_base->data); + writew(I2C_STAT_RRDY, &i2c_base->stat); + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + +read_exit: + flush_fifo(); + writew(0xFFFF, &i2c_base->stat); + writew(0, &i2c_base->cnt); + return i2c_error; +} + +int i2c_write( + unsigned char chip, + unsigned int addr, + int alen, + unsigned char *buffer, + int len +) +{ + int i; + unsigned short status; + int i2c_error = 0; + + if (alen > 1) { + printk("I2C write: addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > 256) { + printk("I2C write: address 0x%x + 0x%x out of range\n", + addr, len); + return 1; + } + + /* wait until bus not busy */ + wait_for_bb(); + + /* start address phase - will write regoffset + len bytes data */ + writew(alen + len, &i2c_base->cnt); + /* set slave address */ + writew(chip, &i2c_base->sa); + /* stop bit needed here */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); + + /* Send address byte */ + status = wait_for_pin(); + + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + printk("error waiting for i2c address ACK (status=0x%x)\n", + status); + goto write_exit; + } + + if (status & I2C_STAT_XRDY) { + writeb(addr & 0xFF, &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } else { + i2c_error = 1; + printk("i2c bus not ready for transmit (status=0x%x)\n", + status); + goto write_exit; + } + + /* address phase is over, now write data */ + for (i = 0; i < len; i++) { + status = wait_for_pin(); + + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + printk("i2c error waiting for data ACK (status=0x%x)\n", + status); + goto write_exit; + } + + if (status & I2C_STAT_XRDY) { + writeb(buffer[i], &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } else { + i2c_error = 1; + printk("i2c bus not ready for Tx (i=%d)\n", i); + goto write_exit; + } + } + +write_exit: + flush_fifo(); + writew(0xFFFF, &i2c_base->stat); + return i2c_error; +} + +int i2c_read( + unsigned char chip, + uint addr, + int alen, + unsigned char *buffer, + int len +) +{ + int i; + + if (alen > 1) { + printk("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > 256) { + printk("I2C read: address out of range\n"); + return 1; + } + + for (i = 0; i < len; i++) { + if (i2c_read_byte(chip, addr + i, &buffer[i])) { + printk("I2C read: I/O error\n"); + i2c_init( CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE ); + return 1; + } + } + + return 0; +} + +/* Write (fill) memory + * + * Syntax: + * i2c mw {i2c_chip} {addr}{.0, .1, .2} {data} [{count}] + */ +static int imw ( unsigned char chip, unsigned long addr, unsigned char byte ) +{ + + unsigned int alen; + int count; + + alen = 1; + count = 1; + + while (count-- > 0) { + if (i2c_write(chip, addr++, alen, &byte, 1) != 0) + printk("Error writing the chip.\n"); + /* + * Wait for the write to complete. The write can take + * up to 10mSec (we allow a little more time). + */ + } + + return (0); +} + +/* + * Syntax: + * i2c md {i2c_chip} {addr}{.0, .1, .2} {len} + */ +static int imd( unsigned char chip, unsigned int addr, unsigned int length ) +{ + int j, nbytes, linebytes; + + unsigned int alen = 0; + if (alen > 3) return 0; + + /* + * Print the lines. + * + * We buffer all read data, so we can make sure data is read only + * once. + */ + nbytes = length; + do { + unsigned char linebuf[DISP_LINE_LEN]; + unsigned char *cp; + + linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; + + if (i2c_read(chip, addr, alen, linebuf, linebytes) != 0) + printk ("Error reading the chip.\n"); + else { + printk("%04x:", addr); + cp = linebuf; + for (j=0; j<linebytes; j++) { + printk(" %02x", *cp++); + addr++; + } + printk (" "); + cp = linebuf; + for (j=0; j<linebytes; j++) { + if ((*cp < 0x20) || (*cp > 0x7e)) + printk ("."); + else + printk("%c", *cp); + cp++; + } + printk ("\n"); + } + nbytes -= linebytes; + } while (nbytes > 0); + + return 0; +} |