diff options
Diffstat (limited to 'cpu/mpc8220/i2cCore.c')
-rw-r--r-- | cpu/mpc8220/i2cCore.c | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/cpu/mpc8220/i2cCore.c b/cpu/mpc8220/i2cCore.c new file mode 100644 index 0000000..accf43c --- /dev/null +++ b/cpu/mpc8220/i2cCore.c @@ -0,0 +1,627 @@ +/* I2cCore.c - MPC8220 PPC I2C Library */ + +/* Copyright 2004 Freescale Semiconductor, Inc. */ + +/* +modification history +-------------------- +01c,29jun04,tcl 1.3 removed CR. Added two bytes offset support. +01b,19jan04,tcl 1.2 removed i2cMsDelay and sysDecGet. renamed i2cMsDelay + back to sysMsDelay +01a,19jan04,tcl 1.1 created and seperated from i2c.c +*/ + +/* +DESCRIPTION +This file contain I2C low level handling library functions +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <vxWorks.h> +#include <sysLib.h> +#include <iosLib.h> +#include <logLib.h> +#include <tickLib.h> + +/* BSP Includes */ +#include "config.h" +#include "mpc8220.h" +#include "i2cCore.h" + +#ifdef DEBUG_I2CCORE +int I2CCDbg = 0; +#endif + +#define ABS(x) ((x < 0)? -x : x) + +char *I2CERR[16] = { + "Transfer in Progress\n", /* 0 */ + "Transfer complete\n", + "Not Addressed\n", /* 2 */ + "Addressed as a slave\n", + "Bus is Idle\n", /* 4 */ + "Bus is busy\n", + "Arbitration Lost\n", /* 6 */ + "Arbitration on Track\n", + "Slave receive, master writing to slave\n", /* 8 */ + "Slave transmit, master reading from slave\n", + "Interrupt is pending\n", /* 10 */ + "Interrupt complete\n", + "Acknowledge received\n", /* 12 */ + "No acknowledge received\n", + "Unknown status\n", /* 14 */ + "\n" +}; + +/****************************************************************************** + * + * chk_status - Check I2C status bit + * + * RETURNS: OK, or ERROR if the bit encounter + * + */ + +STATUS chk_status (PSI2C pi2c, UINT8 sta_bit, UINT8 truefalse) +{ + int i, status = 0; + + for (i = 0; i < I2C_POLL_COUNT; i++) { + if ((pi2c->sr & sta_bit) == (truefalse ? sta_bit : 0)) + return (OK); + } + + I2CCDBG (L2, ("--- sr %x stabit %x truefalse %d\n", + pi2c->sr, sta_bit, truefalse, 0, 0, 0)); + + if (i == I2C_POLL_COUNT) { + switch (sta_bit) { + case I2C_STA_CF: + status = 0; + break; + case I2C_STA_AAS: + status = 2; + break; + case I2C_STA_BB: + status = 4; + break; + case I2C_STA_AL: + status = 6; + break; + case I2C_STA_SRW: + status = 8; + break; + case I2C_STA_IF: + status = 10; + break; + case I2C_STA_RXAK: + status = 12; + break; + default: + status = 14; + break; + } + + if (!truefalse) + status++; + + I2CCDBG (NO, ("--- status %d\n", status, 0, 0, 0, 0, 0)); + I2CCDBG (NO, (I2CERR[status], 0, 0, 0, 0, 0, 0)); + } + + return (ERROR); +} + +/****************************************************************************** + * + * I2C Enable - Enable the I2C Controller + * + */ +STATUS i2c_enable (SI2C * pi2c, PI2CSET pi2cSet) +{ + int fdr = pi2cSet->bit_rate; + UINT8 adr = pi2cSet->i2c_adr; + + I2CCDBG (L2, ("i2c_enable fdr %d adr %x\n", fdr, adr, 0, 0, 0, 0)); + + i2c_clear (pi2c); /* Clear FDR, ADR, SR and CR reg */ + + SetI2cFDR (pi2c, fdr); /* Frequency */ + pi2c->adr = adr; + + pi2c->cr = I2C_CTL_EN; /* Set Enable */ + + /* + The I2C bus should be in Idle state. If the bus is busy, + clear the STA bit in control register + */ + if (chk_status (pi2c, I2C_STA_BB, 0) != OK) { + if ((pi2c->cr & I2C_CTL_STA) == I2C_CTL_STA) + pi2c->cr &= ~I2C_CTL_STA; + + /* Check again if it is still busy, return error if found */ + if (chk_status (pi2c, I2C_STA_BB, 1) == OK) + return ERROR; + } + + return (OK); +} + +/****************************************************************************** + * + * I2C Disable - Disable the I2C Controller + * + */ +STATUS i2c_disable (PSI2C pi2c) +{ + i2c_clear (pi2c); + + pi2c->cr &= I2C_CTL_EN; /* Disable I2c */ + + if ((pi2c->cr & I2C_CTL_STA) == I2C_CTL_STA) + pi2c->cr &= ~I2C_CTL_STA; + + if (chk_status (pi2c, I2C_STA_BB, 0) != OK) + return ERROR; + + return (OK); +} + +/****************************************************************************** + * + * I2C Clear - Clear the I2C Controller + * + */ +STATUS i2c_clear (PSI2C pi2c) +{ + pi2c->adr = 0; + pi2c->fdr = 0; + pi2c->cr = 0; + pi2c->sr = 0; + + return (OK); +} + + +STATUS i2c_start (PSI2C pi2c, PI2CSET pi2cSet) +{ +#ifdef TWOBYTES + UINT16 ByteOffset = pi2cSet->str_adr; +#else + UINT8 ByteOffset = pi2cSet->str_adr; +#endif +#if 1 + UINT8 tmp = 0; +#endif + UINT8 Addr = pi2cSet->slv_adr; + + pi2c->cr |= I2C_CTL_STA; /* Generate start signal */ + + if (chk_status (pi2c, I2C_STA_BB, 1) != OK) + return ERROR; + + /* Write slave address */ + if (i2c_writebyte (pi2c, &Addr) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#ifdef TWOBYTES +# if 0 + /* Issue the offset to start */ + if (i2c_write2byte (pi2c, &ByteOffset) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#endif + tmp = (ByteOffset >> 8) & 0xff; + if (i2c_writebyte (pi2c, &tmp) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } + tmp = ByteOffset & 0xff; + if (i2c_writebyte (pi2c, &tmp) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#else + if (i2c_writebyte (pi2c, &ByteOffset) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#endif + + return (OK); +} + +STATUS i2c_stop (PSI2C pi2c) +{ + pi2c->cr &= ~I2C_CTL_STA; /* Generate stop signal */ + if (chk_status (pi2c, I2C_STA_BB, 0) != OK) + return ERROR; + + return (OK); +} + +/****************************************************************************** + * + * Read Len bytes to the location pointed to by *Data from the device + * with address Addr. + */ +int i2c_readblock (SI2C * pi2c, PI2CSET pi2cSet, UINT8 * Data) +{ + int i = 0; + UINT8 Tmp; + +/* UINT8 ByteOffset = pi2cSet->str_adr; not used? */ + UINT8 Addr = pi2cSet->slv_adr; + int Length = pi2cSet->xfer_size; + + I2CCDBG (L1, ("i2c_readblock addr %x data 0x%08x len %d offset %d\n", + Addr, (int) Data, Length, ByteOffset, 0, 0)); + + if (pi2c->sr & I2C_STA_AL) { /* Check if Arbitration lost */ + I2CCDBG (FN, ("Arbitration lost\n", 0, 0, 0, 0, 0, 0)); + pi2c->sr &= ~I2C_STA_AL; /* Clear Arbitration status bit */ + return ERROR; + } + + pi2c->cr |= I2C_CTL_TX; /* Enable the I2c for TX, Ack */ + + if (i2c_start (pi2c, pi2cSet) == ERROR) + return ERROR; + + pi2c->cr |= I2C_CTL_RSTA; /* Repeat Start */ + + Tmp = Addr | 1; + + if (i2c_writebyte (pi2c, &Tmp) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } + + if (((pi2c->sr & 0x07) == 0x07) || (pi2c->sr & 0x01)) + return ERROR; + + pi2c->cr &= ~I2C_CTL_TX; /* Set receive mode */ + + if (((pi2c->sr & 0x07) == 0x07) || (pi2c->sr & 0x01)) + return ERROR; + + /* Dummy Read */ + if (i2c_readbyte (pi2c, &Tmp, &i) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } + + i = 0; + while (Length) { + if (Length == 2) + pi2c->cr |= I2C_CTL_TXAK; + + if (Length == 1) + pi2c->cr &= ~I2C_CTL_STA; + + if (i2c_readbyte (pi2c, Data, &Length) != OK) { + return i2c_stop (pi2c); + } + i++; + Length--; + Data++; + } + + if (i2c_stop (pi2c) == ERROR) + return ERROR; + + return i; +} + +STATUS i2c_writeblock (SI2C * pi2c, PI2CSET pi2cSet, UINT8 * Data) +{ + int Length = pi2cSet->xfer_size; + +#ifdef TWOBYTES + UINT16 ByteOffset = pi2cSet->str_adr; +#else + UINT8 ByteOffset = pi2cSet->str_adr; +#endif + int j, k; + + I2CCDBG (L2, ("i2c_writeblock\n", 0, 0, 0, 0, 0, 0)); + + if (pi2c->sr & I2C_STA_AL) { + /* Check if arbitration lost */ + I2CCDBG (L2, ("Arbitration lost\n", 0, 0, 0, 0, 0, 0)); + pi2c->sr &= ~I2C_STA_AL; /* Clear the condition */ + return ERROR; + } + + pi2c->cr |= I2C_CTL_TX; /* Enable the I2c for TX, Ack */ + + /* Do the not even offset first */ + if ((ByteOffset % 8) != 0) { + int remain; + + if (Length > 8) { + remain = 8 - (ByteOffset % 8); + Length -= remain; + + pi2cSet->str_adr = ByteOffset; + + if (i2c_start (pi2c, pi2cSet) == ERROR) + return ERROR; + + for (j = ByteOffset; j < remain; j++) { + if (i2c_writebyte (pi2c, Data++) != OK) + return ERROR; + } + + if (i2c_stop (pi2c) == ERROR) + return ERROR; + + sysMsDelay (32); + + /* Update the new ByteOffset */ + ByteOffset += remain; + } + } + + for (j = ByteOffset, k = 0; j < (Length + ByteOffset); j++) { + if ((j % 8) == 0) { + pi2cSet->str_adr = j; + if (i2c_start (pi2c, pi2cSet) == ERROR) + return ERROR; + } + + k++; + + if (i2c_writebyte (pi2c, Data++) != OK) + return ERROR; + + if ((j == (Length - 1)) || ((k % 8) == 0)) { + if (i2c_stop (pi2c) == ERROR) + return ERROR; + + sysMsDelay (50); + } + + } + + return k; +} + +STATUS i2c_readbyte (SI2C * pi2c, UINT8 * readb, int *index) +{ + pi2c->sr &= ~I2C_STA_IF; /* Clear Interrupt Bit */ + *readb = pi2c->dr; /* Read a byte */ + + /* + Set I2C_CTRL_TXAK will cause Transfer pending and + set I2C_CTRL_STA will cause Interrupt pending + */ + if (*index != 2) { + if (chk_status (pi2c, I2C_STA_CF, 1) != OK) /* Transfer not complete? */ + return ERROR; + } + + if (*index != 1) { + if (chk_status (pi2c, I2C_STA_IF, 1) != OK) + return ERROR; + } + + return (OK); +} + + +STATUS i2c_writebyte (SI2C * pi2c, UINT8 * writeb) +{ + pi2c->sr &= ~I2C_STA_IF; /* Clear Interrupt */ + pi2c->dr = *writeb; /* Write a byte */ + + if (chk_status (pi2c, I2C_STA_CF, 1) != OK) /* Transfer not complete? */ + return ERROR; + + if (chk_status (pi2c, I2C_STA_IF, 1) != OK) + return ERROR; + + return OK; +} + +STATUS i2c_write2byte (SI2C * pi2c, UINT16 * writeb) +{ + UINT8 data; + + data = (UINT8) ((*writeb >> 8) & 0xff); + if (i2c_writebyte (pi2c, &data) != OK) + return ERROR; + data = (UINT8) (*writeb & 0xff); + if (i2c_writebyte (pi2c, &data) != OK) + return ERROR; + return OK; +} + +/* FDR table base on 33Mhz - more detail please refer to Odini2c_dividers.xls +FDR FDR scl sda scl2tap2 +510 432 tap tap tap tap scl_per sda_hold I2C Freq 0 1 2 3 4 5 +000 000 9 3 4 1 28 Clocks 9 Clocks 1190 KHz 0 0 0 0 0 0 +000 001 9 3 4 2 44 Clocks 11 Clocks 758 KHz 0 0 1 0 0 0 +000 010 9 3 6 4 80 Clocks 17 Clocks 417 KHz 0 0 0 1 0 0 +000 011 9 3 6 8 144 Clocks 25 Clocks 231 KHz 0 0 1 1 0 0 +000 100 9 3 14 16 288 Clocks 49 Clocks 116 KHz 0 0 0 0 1 0 +000 101 9 3 30 32 576 Clocks 97 Clocks 58 KHz 0 0 1 0 1 0 +000 110 9 3 62 64 1152 Clocks 193 Clocks 29 KHz 0 0 0 1 1 0 +000 111 9 3 126 128 2304 Clocks 385 Clocks 14 KHz 0 0 1 1 1 0 +001 000 10 3 4 1 30 Clocks 9 Clocks 1111 KHz1 0 0 0 0 0 +001 001 10 3 4 2 48 Clocks 11 Clocks 694 KHz 1 0 1 0 0 0 +001 010 10 3 6 4 88 Clocks 17 Clocks 379 KHz 1 0 0 1 0 0 +001 011 10 3 6 8 160 Clocks 25 Clocks 208 KHz 1 0 1 1 0 0 +001 100 10 3 14 16 320 Clocks 49 Clocks 104 KHz 1 0 0 0 1 0 +001 101 10 3 30 32 640 Clocks 97 Clocks 52 KHz 1 0 1 0 1 0 +001 110 10 3 62 64 1280 Clocks 193 Clocks 26 KHz 1 0 0 1 1 0 +001 111 10 3 126 128 2560 Clocks 385 Clocks 13 KHz 1 0 1 1 1 0 +010 000 12 4 4 1 34 Clocks 10 Clocks 980 KHz 0 1 0 0 0 0 +010 001 12 4 4 2 56 Clocks 13 Clocks 595 KHz 0 1 1 0 0 0 +010 010 12 4 6 4 104 Clocks 21 Clocks 321 KHz 0 1 0 1 0 0 +010 011 12 4 6 8 192 Clocks 33 Clocks 174 KHz 0 1 1 1 0 0 +010 100 12 4 14 16 384 Clocks 65 Clocks 87 KHz 0 1 0 0 1 0 +010 101 12 4 30 32 768 Clocks 129 Clocks 43 KHz 0 1 1 0 1 0 +010 110 12 4 62 64 1536 Clocks 257 Clocks 22 KHz 0 1 0 1 1 0 +010 111 12 4 126 128 3072 Clocks 513 Clocks 11 KHz 0 1 1 1 1 0 +011 000 15 4 4 1 40 Clocks 10 Clocks 833 KHz 1 1 0 0 0 0 +011 001 15 4 4 2 68 Clocks 13 Clocks 490 KHz 1 1 1 0 0 0 +011 010 15 4 6 4 128 Clocks 21 Clocks 260 KHz 1 1 0 1 0 0 +011 011 15 4 6 8 240 Clocks 33 Clocks 139 KHz 1 1 1 1 0 0 +011 100 15 4 14 16 480 Clocks 65 Clocks 69 KHz 1 1 0 0 1 0 +011 101 15 4 30 32 960 Clocks 129 Clocks 35 KHz 1 1 1 0 1 0 +011 110 15 4 62 64 1920 Clocks 257 Clocks 17 KHz 1 1 0 1 1 0 +011 111 15 4 126 128 3840 Clocks 513 Clocks 9 KHz 1 1 1 1 1 0 +100 000 5 1 4 1 20 Clocks 7 Clocks 1667 KHz 0 0 0 0 0 1 +100 001 5 1 4 2 28 Clocks 7 Clocks 1190 KHz 0 0 1 0 0 1 +100 010 5 1 6 4 48 Clocks 9 Clocks 694 KHz 0 0 0 1 0 1 +100 011 5 1 6 8 80 Clocks 9 Clocks 417 KHz 0 0 1 1 0 1 +100 100 5 1 14 16 160 Clocks 17 Clocks 208 KHz 0 0 0 0 1 1 +100 101 5 1 30 32 320 Clocks 33 Clocks 104 KHz 0 0 1 0 1 1 +100 110 5 1 62 64 640 Clocks 65 Clocks 52 KHz 0 0 0 1 1 1 +100 111 5 1 126 128 1280 Clocks 129 Clocks 26 KHz 0 0 1 1 1 1 +101 000 6 1 4 1 22 Clocks 7 Clocks 1515 KHz 1 0 0 0 0 1 +101 001 6 1 4 2 32 Clocks 7 Clocks 1042 KHz 1 0 1 0 0 1 +101 010 6 1 6 4 56 Clocks 9 Clocks 595 KHz 1 0 0 1 0 1 +101 011 6 1 6 8 96 Clocks 9 Clocks 347 KHz 1 0 1 1 0 1 +101 100 6 1 14 16 192 Clocks 17 Clocks 174 KHz 1 0 0 0 1 1 +101 101 6 1 30 32 384 Clocks 33 Clocks 87 KHz 1 0 1 0 1 1 +101 110 6 1 62 64 768 Clocks 65 Clocks 43 KHz 1 0 0 1 1 1 +101 111 6 1 126 128 1536 Clocks 129 Clocks 22 KHz 1 0 1 1 1 1 +110 000 7 2 4 1 24 Clocks 8 Clocks 1389 KHz 0 1 0 0 0 1 +110 001 7 2 4 2 36 Clocks 9 Clocks 926 KHz 0 1 1 0 0 1 +110 010 7 2 6 4 64 Clocks 13 Clocks 521 KHz 0 1 0 1 0 1 +110 011 7 2 6 8 112 Clocks 17 Clocks 298 KHz 0 1 1 1 0 1 +110 100 7 2 14 16 224 Clocks 33 Clocks 149 KHz 0 1 0 0 1 1 +110 101 7 2 30 32 448 Clocks 65 Clocks 74 KHz 0 1 1 0 1 1 +110 110 7 2 62 64 896 Clocks 129 Clocks 37 KHz 0 1 0 1 1 1 +110 111 7 2 126 128 1792 Clocks 257 Clocks 19 KHz 0 1 1 1 1 1 +111 000 8 2 4 1 26 Clocks 8 Clocks 1282 KHz 1 1 0 0 0 1 +111 001 8 2 4 2 40 Clocks 9 Clocks 833 KHz 1 1 1 0 0 1 +111 010 8 2 6 4 72 Clocks 13 Clocks 463 KHz 1 1 0 1 0 1 +111 011 8 2 6 8 128 Clocks 17 Clocks 260 KHz 1 1 1 1 0 1 +111 100 8 2 14 16 256 Clocks 33 Clocks 130 KHz 1 1 0 0 1 1 +111 101 8 2 30 32 512 Clocks 65 Clocks 65 KHz 1 1 1 0 1 1 +111 110 8 2 62 64 1024 Clocks 129 Clocks 33 KHz 1 1 0 1 1 1 +111 111 8 2 126 128 2048 Clocks 257 Clocks 16 KHz 1 1 1 1 1 1 +*/ +STATUS SetI2cFDR (PSI2C pi2cRegs, int bitrate) +{ +/* Constants */ + const UINT8 div_hold[8][3] = { {9, 3}, {10, 3}, + {12, 4}, {15, 4}, + {5, 1}, {6, 1}, + {7, 2}, {8, 2} + }; + + const UINT8 scl_tap[8][2] = { {4, 1}, {4, 2}, + {6, 4}, {6, 8}, + {14, 16}, {30, 32}, + {62, 64}, {126, 128} + }; + + UINT8 mfdr_bits; + + int i = 0; + int j = 0; + + int Diff, min; + int WhichFreq, iRec, jRec; + int SCL_Period; + int SCL_Hold; + int I2C_Freq; + + I2CCDBG (L2, ("Entering getBitRate: bitrate %d pi2cRegs 0x%08x\n", + bitrate, (int) pi2cRegs, 0, 0, 0, 0)); + + if (bitrate < 0) { + I2CCDBG (NO, ("Invalid bitrate\n", 0, 0, 0, 0, 0, 0)); + return ERROR; + } + + /* Initialize */ + mfdr_bits = 0; + min = 0x7fffffff; + WhichFreq = iRec = jRec = 0; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + /* SCL Period = 2 * (scl2tap + [(SCL_Tap - 1) * tap2tap] + 2) + * SCL Hold = scl2tap + ((SDA_Tap - 1) * tap2tap) + 3 + * Bit Rate (I2C Freq) = System Freq / SCL Period + */ + SCL_Period = + 2 * (scl_tap[i][0] + + ((div_hold[j][0] - 1) * scl_tap[i][1]) + + 2); + + /* Now get the I2C Freq */ + I2C_Freq = DEV_CLOCK_FREQ / SCL_Period; + + /* Take equal or slower */ + if (I2C_Freq > bitrate) + continue; + + /* Take the differences */ + Diff = I2C_Freq - bitrate; + + Diff = ABS (Diff); + + /* Find the closer value */ + if (Diff < min) { + min = Diff; + WhichFreq = I2C_Freq; + iRec = i; + jRec = j; + } + + I2CCDBG (L2, + ("--- (%d,%d) I2C_Freq %d minDiff %d min %d\n", + i, j, I2C_Freq, Diff, min, 0)); + } + } + + SCL_Period = + 2 * (scl_tap[iRec][0] + + ((div_hold[jRec][0] - 1) * scl_tap[iRec][1]) + 2); + + I2CCDBG (L2, ("\nmin %d WhichFreq %d iRec %d jRec %d\n", + min, WhichFreq, iRec, jRec, 0, 0)); + I2CCDBG (L2, ("--- scl2tap %d SCL_Tap %d tap2tap %d\n", + scl_tap[iRec][0], div_hold[jRec][0], scl_tap[iRec][1], + 0, 0, 0)); + + /* This may no require */ + SCL_Hold = + scl_tap[iRec][0] + + ((div_hold[jRec][1] - 1) * scl_tap[iRec][1]) + 3; + I2CCDBG (L2, + ("--- SCL_Period %d SCL_Hold %d\n", SCL_Period, SCL_Hold, 0, + 0, 0, 0)); + + I2CCDBG (L2, ("--- mfdr_bits %x\n", mfdr_bits, 0, 0, 0, 0, 0)); + + /* FDR 4,3,2 */ + if ((iRec & 1) == 1) + mfdr_bits |= 0x04; /* FDR 2 */ + if ((iRec & 2) == 2) + mfdr_bits |= 0x08; /* FDR 3 */ + if ((iRec & 4) == 4) + mfdr_bits |= 0x10; /* FDR 4 */ + /* FDR 5,1,0 */ + if ((jRec & 1) == 1) + mfdr_bits |= 0x01; /* FDR 0 */ + if ((jRec & 2) == 2) + mfdr_bits |= 0x02; /* FDR 1 */ + if ((jRec & 4) == 4) + mfdr_bits |= 0x20; /* FDR 5 */ + + I2CCDBG (L2, ("--- mfdr_bits %x\n", mfdr_bits, 0, 0, 0, 0, 0)); + + pi2cRegs->fdr = mfdr_bits; + + return OK; +} |