/************************************************************* * * Copyright @ Motorola, 1999 * ************************************************************/ #include #ifdef CONFIG_HARD_I2C #include #include "i2c_export.h" #include "i2c.h" #undef I2CDBG0 #undef DEBUG /* Define a macro to use an optional application-layer print function, if * one was passed to the I2C library during initialization. If there was * no function pointer passed, this protects against calling it. Also define * the global variable that holds the passed pointer. */ #define TIMEOUT (CFG_HZ/4) #define PRINT if ( app_print ) app_print static int (*app_print) (char *, ...); /******************* Internal to I2C Driver *****************/ static unsigned int ByteToXmit = 0; static unsigned int XmitByte = 0; static unsigned char *XmitBuf = 0; static unsigned int XmitBufEmptyStop = 0; static unsigned int ByteToRcv = 0; static unsigned int RcvByte = 0; static unsigned char *RcvBuf = 0; static unsigned int RcvBufFulStop = 0; static unsigned int MasterRcvAddress = 0; /* Set by call to get_eumbbar during I2C_Initialize. * This could be globally available to the I2C library, but there is * an advantage to passing it as a parameter: it is already in a register * and doesn't have to be loaded from memory. Also, that is the way the * I2C library was already implemented and I don't want to change it without * a more detailed analysis. * It is being set as a global variable in I2C_Initialize to hide it from * the DINK application layer, because it is Kahlua-specific. I think that * get_eumbbar, load_runtime_reg, and store_runtime_reg should be defined in * a Kahlua-specific library dealing with the embedded utilities memory block. * Right now, get_eumbbar is defined in dink32/kahlua.s. The other two are * defined in dink32/drivers/i2c/i2c2.s. */ static unsigned int Global_eumbbar = 0; extern unsigned int load_runtime_reg (unsigned int eumbbar, unsigned int reg); extern unsigned int store_runtime_reg (unsigned int eumbbar, unsigned int reg, unsigned int val); /************************** API *****************/ /* Application Program Interface (API) are the calls provided by the I2C * library to upper layer applications (i.e., DINK) to access the Kahlua * I2C bus interface. The functions and values that are part of this API * are declared in i2c_export.h. */ /* Initialize I2C unit with the following: * driver's slave address * interrupt enabled * optional pointer to application layer print function * * These parameters may be added: * desired clock rate * digital filter frequency sampling rate * * This function must be called before I2C unit can be used. */ I2C_Status I2C_Initialize (unsigned char addr, I2C_INTERRUPT_MODE en_int, int (*p) (char *, ...)) { I2CStatus status; /* establish the pointer, if there is one, to the application's "printf" */ app_print = p; /* If this is the first call, get the embedded utilities memory block * base address. I'm not sure what to do about error handling here: * if a non-zero value is returned, accept it. */ if (Global_eumbbar == 0) Global_eumbbar = get_eumbbar (); if (Global_eumbbar == 0) { PRINT ("I2C_Initialize: can't find EUMBBAR\n"); return I2C_ERROR; } /* validate the I2C address */ if (addr & 0x80) { PRINT ("I2C_Initialize, I2C address invalid: %d 0x%x\n", (unsigned int) addr, (unsigned int) addr); return I2C_ERROR; } /* Call the internal I2C library function to perform work. * Accept the default frequency sampling rate (no way to set it currently, * via I2C_Init) and set the clock frequency to something reasonable. */ status = I2C_Init (Global_eumbbar, (unsigned char) 0x31, addr, en_int); if (status != I2CSUCCESS) { PRINT ("I2C_Initialize: error in initiation\n"); return I2C_ERROR; } /* all is well */ return I2C_SUCCESS; } /* Perform the given I2C transaction, only MASTER_XMIT and MASTER_RCV * are implemented. Both are only in polling mode. * * en_int controls interrupt/polling mode * act is the type of transaction * i2c_addr is the I2C address of the slave device * data_addr is the address of the data on the slave device * len is the length of data to send or receive * buffer is the address of the data buffer * stop = I2C_NO_STOP, don't signal STOP at end of transaction * I2C_STOP, signal STOP at end of transaction * retry is the timeout retry value, currently ignored * rsta = I2C_NO_RESTART, this is not continuation of existing transaction * I2C_RESTART, this is a continuation of existing transaction */ I2C_Status I2C_do_transaction ( I2C_INTERRUPT_MODE en_int, I2C_TRANSACTION_MODE act, unsigned char i2c_addr, unsigned char data_addr, int len, char *buffer, I2C_STOP_MODE stop, int retry, I2C_RESTART_MODE rsta) { I2C_Status status; unsigned char data_addr_buffer[1]; #if 1 /* This is a temporary work-around. The I2C library breaks the protocol * if it attempts to handle a data transmission in more than one * transaction, so the data address and the actual data bytes are put * into a single buffer before sending it to the library internal functions. * The problem is related to being able to restart a transaction without * sending the I2C device address or repeating the data address. It may take * a day or two to sort it all out, so I'll have to get back to it later. * Look at I2C_Start to see about using some status flags (I'm not sure that * "stop" and "rsta" are enough to reflect the states, maybe so; but the logic * in the library is insufficient) to control correct handling of the protocol. */ unsigned char dummy_buffer[257]; if (act == I2C_MASTER_XMIT) { int i; if (len > 256) return I2C_ERROR; for (i = 1; i <= len; i++) dummy_buffer[i] = buffer[i - 1]; dummy_buffer[0] = data_addr; status = I2C_do_buffer (en_int, act, i2c_addr, 1 + len, dummy_buffer, stop, retry, rsta); if (status != I2C_SUCCESS) { PRINT ("I2C_do_transaction: can't perform data transfer\n"); return I2C_ERROR; } return I2C_SUCCESS; } #endif /* end of temp work-around */ /* validate requested transaction type */ if ((act != I2C_MASTER_XMIT) && (act != I2C_MASTER_RCV)) { PRINT ("I2C_do_transaction, invalid transaction request: %d\n", act); return I2C_ERROR; } /* range check the I2C address */ if (i2c_addr & 0x80) { PRINT ("I2C_do_transaction, I2C address out of range: %d 0x%x\n", (unsigned int) i2c_addr, (unsigned int) i2c_addr); return I2C_ERROR; } else { data_addr_buffer[0] = data_addr; } /* * We first have to contact the slave device and transmit the * data address. Be careful about the STOP and restart stuff. * We don't want to signal STOP after sending the data * address, but this could be a continuation if the * application didn't release the bus after the previous * transaction, by not sending a STOP after it. */ status = I2C_do_buffer (en_int, I2C_MASTER_XMIT, i2c_addr, 1, data_addr_buffer, I2C_NO_STOP, retry, rsta); if (status != I2C_SUCCESS) { PRINT ("I2C_do_transaction: can't send data address for read\n"); return I2C_ERROR; } /* The data transfer will be a continuation. */ rsta = I2C_RESTART; /* now handle the user data */ status = I2C_do_buffer (en_int, act, i2c_addr, len, buffer, stop, retry, rsta); if (status != I2C_SUCCESS) { PRINT ("I2C_do_transaction: can't perform data transfer\n"); return I2C_ERROR; } /* all is well */ return I2C_SUCCESS; } /* This function performs the work for I2C_do_transaction. The work is * split into this function to enable I2C_do_transaction to first transmit * the data address to the I2C slave device without putting the data address * into the first byte of the buffer. * * en_int controls interrupt/polling mode * act is the type of transaction * i2c_addr is the I2C address of the slave device * len is the length of data to send or receive * buffer is the address of the data buffer * stop = I2C_NO_STOP, don't signal STOP at end of transaction * I2C_STOP, signal STOP at end of transaction * retry is the timeout retry value, currently ignored * rsta = I2C_NO_RESTART, this is not continuation of existing transaction * I2C_RESTART, this is a continuation of existing transaction */ static I2C_Status I2C_do_buffer (I2C_INTERRUPT_MODE en_int, I2C_TRANSACTION_MODE act, unsigned char i2c_addr, int len, unsigned char *buffer, I2C_STOP_MODE stop, int retry, I2C_RESTART_MODE rsta) { I2CStatus rval; unsigned int dev_stat; if (act == I2C_MASTER_RCV) { /* set up for master-receive transaction */ rval = I2C_get (Global_eumbbar, i2c_addr, buffer, len, stop, rsta); } else { /* set up for master-transmit transaction */ rval = I2C_put (Global_eumbbar, i2c_addr, buffer, len, stop, rsta); } /* validate the setup */ if (rval != I2CSUCCESS) { dev_stat = load_runtime_reg (Global_eumbbar, I2CSR); PRINT ("Error(I2C_do_buffer): control phase, code(0x%08x), status(0x%08x)\n", rval, dev_stat); I2C_Stop (Global_eumbbar); return I2C_ERROR; } if (en_int == 1) { /* this should not happen, no interrupt handling yet */ return I2C_SUCCESS; } /* this performs the polling action, when the transfer is completed, * the status returned from I2C_Timer_Event will be I2CBUFFFULL or * I2CBUFFEMPTY (rcv or xmit), I2CSUCCESS or I2CADDRESS indicates the * transaction is not yet complete, anything else is an error. */ while (rval == I2CSUCCESS || rval == I2CADDRESS) { int timeval = get_timer (0); /* poll the device until something happens */ do { rval = I2C_Timer_Event (Global_eumbbar, 0); } while (rval == I2CNOEVENT && get_timer (timeval) < TIMEOUT); /* check for error condition */ if (rval == I2CSUCCESS || rval == I2CBUFFFULL || rval == I2CBUFFEMPTY || rval == I2CADDRESS) { ; /* do nothing */ } else { /* report the error condition */ dev_stat = load_runtime_reg (Global_eumbbar, I2CSR); PRINT ("Error(I2C_do_buffer): code(0x%08x), status(0x%08x)\n", rval, dev_stat); return I2C_ERROR; } } /* all is well */ return I2C_SUCCESS; } /** * Note: * * In all following functions, * the caller shall pass the configured embedded utility memory * block base, EUMBBAR. **/ /*********************************************************** * function: I2C_put * * description: Send a buffer of data to the intended rcv_addr. * If stop_flag is set, after the whole buffer * is sent, generate a STOP signal provided that the * receiver doesn't signal the STOP in the middle. * I2C is the master performing transmitting. If * no STOP signal is generated at the end of current * transaction, the master can generate a START signal * to another slave addr. * * note: this is master xmit API *********************************************************/ static I2CStatus I2C_put (unsigned int eumbbar, unsigned char rcv_addr, /* receiver's address */ unsigned char *buffer_ptr, /* pointer of data to be sent */ unsigned int length, /* number of byte of in the buffer */ unsigned int stop_flag, /* 1 - signal STOP when buffer is empty * 0 - no STOP signal when buffer is empty */ unsigned int is_cnt) { /* 1 - this is a restart, don't check MBB * 0 - this is a new start, check MBB */ if (buffer_ptr == 0 || length == 0) { return I2CERROR; } #ifdef I2CDBG0 PRINT ("%s(%d): I2C_put\n", __FILE__, __LINE__); #endif XmitByte = 0; ByteToXmit = length; XmitBuf = buffer_ptr; XmitBufEmptyStop = stop_flag; RcvByte = 0; ByteToRcv = 0; RcvBuf = 0; /* we are the master, start transaction */ return I2C_Start (eumbbar, rcv_addr, XMIT, is_cnt); } /*********************************************************** * function: I2C_get * * description: * Receive a buffer of data from the desired sender_addr * If stop_flag is set, when the buffer is full and the * sender does not signal STOP, generate a STOP signal. * I2C is the master performing receiving. If no STOP signal * is generated, the master can generate a START signal * to another slave addr. * * note: this is master receive API **********************************************************/ static I2CStatus I2C_get (unsigned int eumbbar, unsigned char rcv_from, /* sender's address */ unsigned char *buffer_ptr, /* pointer of receiving buffer */ unsigned int length, /* length of the receiving buffer */ unsigned int stop_flag, /* 1 - signal STOP when buffer is full * 0 - no STOP signal when buffer is full */ unsigned int is_cnt) { /* 1 - this is a restart, don't check MBB * 0 - this is a new start, check MBB */ if (buffer_ptr == 0 || length == 0) { return I2CERROR; } #ifdef I2CDBG0 PRINT ("%s(%d): I2C_get\n", __FILE__, __LINE__); #endif RcvByte = 0; ByteToRcv = length; RcvBuf = buffer_ptr; RcvBufFulStop = stop_flag; XmitByte = 0; ByteToXmit = 0; XmitBuf = 0; /* we are the master, start the transaction */ return I2C_Start (eumbbar, rcv_from, RCV, is_cnt); } #if 0 /* turn off dead code */ /********************************************************* * function: I2C_write * * description: * Send a buffer of data to the requiring master. * If stop_flag is set, after the whole buffer is sent, * generate a STOP signal provided that the requiring * receiver doesn't signal the STOP in the middle. * I2C is the slave performing transmitting. * * Note: this is slave xmit API. * * due to the current Kahlua design, slave transmitter * shall not signal STOP since there is no way * for master to detect it, causing I2C bus hung. * * For the above reason, the stop_flag is always * set, i.e., 0. * * programmer shall use the timer on Kahlua to * control the interval of data byte at the * master side. *******************************************************/ static I2CStatus I2C_write (unsigned int eumbbar, unsigned char *buffer_ptr, /* pointer of data to be sent */ unsigned int length, /* number of byte of in the buffer */ unsigned int stop_flag) { /* 1 - signal STOP when buffer is empty * 0 - no STOP signal when buffer is empty */ if (buffer_ptr == 0 || length == 0) { return I2CERROR; } XmitByte = 0; ByteToXmit = length; XmitBuf = buffer_ptr; XmitBufEmptyStop = 0; /* in order to avoid bus hung, ignored the user's stop_flag */ RcvByte = 0; ByteToRcv = 0; RcvBuf = 0; /* we are the slave, just wait for being called, or pull */ /* I2C_Timer_Event( eumbbar ); */ } /****************************************************** * function: I2C_read * * description: * Receive a buffer of data from the sending master. * If stop_flag is set, when the buffer is full and the * sender does not signal STOP, generate a STOP signal. * I2C is the slave performing receiving. * * note: this is slave receive API ****************************************************/ static I2CStatus I2C_read (unsigned int eumbbar, unsigned char *buffer_ptr, /* pointer of receiving buffer */ unsigned int length, /* length of the receiving buffer */ unsigned int stop_flag) { /* 1 - signal STOP when buffer is full * 0 - no STOP signal when buffer is full */ if (buffer_ptr == 0 || length == 0) { return I2CERROR; } RcvByte = 0; ByteToRcv = length; RcvBuf = buffer_ptr; RcvBufFulStop = stop_flag; XmitByte = 0; ByteToXmit = 0; XmitBuf = 0; /* wait for master to call us, or poll */ /* I2C_Timer_Event( eumbbar ); */ } #endif /* turn off dead code */ /********************************************************* * function: I2c_Timer_Event * * description: * if interrupt is not used, this is the timer event handler. * After each fixed time interval, this function can be called * to check the I2C status and call appropriate function to * handle the status event. ********************************************************/ static I2CStatus I2C_Timer_Event (unsigned int eumbbar, I2CStatus (*handler) (unsigned int)) { I2C_STAT stat; #ifdef I2CDBG0 PRINT ("%s(%d): I2C_Timer_Event\n", __FILE__, __LINE__); #endif stat = I2C_Get_Stat (eumbbar); if (stat.mif == 1) { if (handler == 0) { return I2C_ISR (eumbbar); } else { return (*handler) (eumbbar); } } return I2CNOEVENT; } /****************** Device I/O function *****************/ /****************************************************** * function: I2C_Start * * description: Generate a START signal in the desired mode. * I2C is the master. * * Return I2CSUCCESS if no error. * * note: ****************************************************/ static I2CStatus I2C_Start (unsigned int eumbbar, unsigned char slave_addr, /* address of the receiver */ I2C_MODE mode, /* XMIT(1) - put (write) * RCV(0) - get (read) */ unsigned int is_cnt) { /* 1 - this is a restart, don't check MBB * 0 - this is a new start */ unsigned int tmp = 0; I2C_STAT stat; I2C_CTRL ctrl; #ifdef I2CDBG0 PRINT ("%s(%d): I2C_Start addr 0x%x mode %d cnt %d\n", __FILE__, __LINE__, slave_addr, mode, is_cnt); #endif ctrl = I2C_Get_Ctrl (eumbbar); /* first make sure I2C has been initialized */ if (ctrl.men == 0) { return I2CERROR; } /* next make sure bus is idle */ stat = I2C_Get_Stat (eumbbar); if (is_cnt == 0 && stat.mbb == 1) { /* sorry, we lost */ return I2CBUSBUSY; } else if (is_cnt == 1 && stat.mif == 1 && stat.mal == 0) { /* sorry, we lost the bus */ return I2CALOSS; } /* OK, I2C is enabled and we have the bus */ /* prepare to write the slave address */ ctrl.msta = 1; ctrl.mtx = 1; ctrl.txak = 0; ctrl.rsta = is_cnt; /* set the repeat start bit */ I2C_Set_Ctrl (eumbbar, ctrl); /* write the slave address and xmit/rcv mode bit */ tmp = load_runtime_reg (eumbbar, I2CDR); tmp = (tmp & 0xffffff00) | ((slave_addr & 0x007f) << 1) | (mode == XMIT ? 0x0 : 0x1); store_runtime_reg (eumbbar, I2CDR, tmp); if (mode == RCV) { MasterRcvAddress = 1; } else { MasterRcvAddress = 0; } #ifdef I2CDBG0 PRINT ("%s(%d): I2C_Start exit\n", __FILE__, __LINE__); #endif /* wait for the interrupt or poll */ return I2CSUCCESS; } /*********************************************************** * function: I2c_Stop * * description: Generate a STOP signal to terminate the master * transaction. * return I2CSUCCESS * **********************************************************/ static I2CStatus I2C_Stop (unsigned int eumbbar) { I2C_CTRL ctrl; #ifdef I2CDBG0 PRINT ("%s(%d): I2C_Stop enter\n", __FILE__, __LINE__); #endif ctrl = I2C_Get_Ctrl (eumbbar); ctrl.msta = 0; I2C_Set_Ctrl (eumbbar, ctrl); #ifdef I2CDBG0 PRINT ("%s(%d): I2C_Stop exit\n", __FILE__, __LINE__); #endif return I2CSUCCESS; } /**************************************************** * function: I2C_Master_Xmit * * description: Master sends one byte of data to * slave target * * return I2CSUCCESS if the byte transmitted. * Otherwise no-zero * * Note: condition must meet when this function is called: * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && I2CSR(RXAK) == 0 * I2CCR(MSTA) == 1 && I2CCR(MTX) == 1 * ***************************************************/ static I2CStatus I2C_Master_Xmit (unsigned int eumbbar) { unsigned int val; if (ByteToXmit > 0) { if (ByteToXmit == XmitByte) { /* all xmitted */ ByteToXmit = 0; if (XmitBufEmptyStop == 1) { I2C_Stop (eumbbar); } return I2CBUFFEMPTY; } #ifdef I2CDBG0 PRINT ("%s(%d): xmit 0x%02x\n", __FILE__, __LINE__, *(XmitBuf + XmitByte)); #endif val = *(XmitBuf + XmitByte); val &= 0x000000ff; store_runtime_reg (eumbbar, I2CDR, val); XmitByte++; return I2CSUCCESS; } return I2CBUFFEMPTY; } /*********************************************** * function: I2C_Master_Rcv * * description: master reads one byte data * from slave source * * return I2CSUCCESS if no error * * Note: condition must meet when this function is called: * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && * I2CCR(MSTA) == 1 && I2CCR(MTX) == 0 * ***********************************************/ static I2CStatus I2C_Master_Rcv (unsigned int eumbbar) { I2C_CTRL ctrl; unsigned int val; if (ByteToRcv > 0) { if (ByteToRcv - RcvByte == 2 && RcvBufFulStop == 1) { /* master requests more than or equal to 2 bytes * we are reading 2nd to last byte */ /* we need to set I2CCR(TXAK) to generate a STOP */ ctrl = I2C_Get_Ctrl (eumbbar); ctrl.txak = 1; I2C_Set_Ctrl (eumbbar, ctrl); /* Kahlua will automatically generate a STOP * next time a transaction happens */ /* note: the case of master requesting one byte is * handled in I2C_ISR */ } /* generat a STOP before reading the last byte */ if (RcvByte + 1 == ByteToRcv && RcvBufFulStop == 1) { I2C_Stop (eumbbar); } val = load_runtime_reg (eumbbar, I2CDR); *(RcvBuf + RcvByte) = val & 0xFF; #ifdef I2CDBG0 PRINT ("%s(%d): rcv 0x%02x\n", __FILE__, __LINE__, *(RcvBuf + RcvByte)); #endif RcvByte++; if (ByteToRcv == RcvByte) { ByteToRcv = 0; return I2CBUFFFULL; } return I2CSUCCESS; } return I2CBUFFFULL; } /**************************************************** * function: I2C_Slave_Xmit * * description: Slave sends one byte of data to * requesting destination * * return SUCCESS if the byte transmitted. Otherwise * No-zero * * Note: condition must meet when this function is called: * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && I2CSR(RXAK) = 0 * I2CCR(MSTA) == 0 && I2CCR(MTX) == 1 * ***************************************************/ static I2CStatus I2C_Slave_Xmit (unsigned int eumbbar) { unsigned int val; if (ByteToXmit > 0) { if (ByteToXmit == XmitByte) { /* no more data to send */ ByteToXmit = 0; /* * do not toggle I2CCR(MTX). Doing so will * cause bus-hung since current Kahlua design * does not give master a way to detect slave * stop. It is always a good idea for master * to use timer to prevent the long long * delays */ return I2CBUFFEMPTY; } #ifdef I2CDBG PRINT ("%s(%d): xmit 0x%02x\n", __FILE__, __LINE__, *(XmitBuf + XmitByte)); #endif val = *(XmitBuf + XmitByte); val &= 0x000000ff; store_runtime_reg (eumbbar, I2CDR, val); XmitByte++; return I2CSUCCESS; } return I2CBUFFEMPTY; } /*********************************************** * function: I2C_Slave_Rcv * * description: slave reads one byte data * from master source * * return I2CSUCCESS if no error otherwise non-zero * * Note: condition must meet when this function is called: * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && * I2CCR(MSTA) == 0 && I2CCR(MTX) = 0 * ***********************************************/ static I2CStatus I2C_Slave_Rcv (unsigned int eumbbar) { unsigned int val; I2C_CTRL ctrl; if (ByteToRcv > 0) { val = load_runtime_reg (eumbbar, I2CDR); *(RcvBuf + RcvByte) = val & 0xff; #ifdef I2CDBG PRINT ("%s(%d): rcv 0x%02x\n", __FILE__, __LINE__, *(RcvBuf + RcvByte)); #endif RcvByte++; if (ByteToRcv == RcvByte) { if (RcvBufFulStop == 1) { /* all done */ ctrl = I2C_Get_Ctrl (eumbbar); ctrl.txak = 1; I2C_Set_Ctrl (eumbbar, ctrl); } ByteToRcv = 0; return I2CBUFFFULL; } return I2CSUCCESS; } return I2CBUFFFULL; } /****************** Device Control Function *************/ /********************************************************* * function: I2C_Init * * description: Initialize I2C unit with desired frequency divider, * master's listening address, with interrupt enabled * or disabled. * * note: ********************************************************/ static I2CStatus I2C_Init (unsigned int eumbbar, unsigned char fdr, /* frequency divider */ unsigned char slave_addr, /* driver's address used for receiving */ unsigned int en_int) { /* 1 - enable I2C interrupt * 0 - disable I2C interrup */ I2C_CTRL ctrl; unsigned int tmp; #ifdef I2CDBG0 PRINT ("%s(%d): I2C_Init enter\n", __FILE__, __LINE__); #endif ctrl = I2C_Get_Ctrl (eumbbar); /* disable the I2C module before we change everything */ ctrl.men = 0; I2C_Set_Ctrl (eumbbar, ctrl); /* set the frequency diver */ tmp = load_runtime_reg (eumbbar, I2CFDR); tmp = (tmp & 0xffffffc0) | (fdr & 0x3f); store_runtime_reg (eumbbar, I2CFDR, tmp); /* Set our listening (slave) address */ tmp = load_runtime_reg (eumbbar, I2CADR); tmp = (tmp & 0xffffff01) | ((slave_addr & 0x7f) << 1); store_runtime_reg (eumbbar, I2CADR, tmp); /* enable I2C with desired interrupt setting */ ctrl.men = 1; ctrl.mien = en_int & 0x1; I2C_Set_Ctrl (eumbbar, ctrl); #ifdef I2CDBG0 PRINT ("%s(%d): I2C_Init exit\n", __FILE__, __LINE__); #endif return I2CSUCCESS; } /***************************************** * function I2c_Get_Stat * * description: Query I2C Status, i.e., read I2CSR * ****************************************/ static I2C_STAT I2C_Get_Stat (unsigned int eumbbar) { unsigned int temp; I2C_STAT stat; temp = load_runtime_reg (eumbbar, I2CSR); #ifdef I2CDBG0 PRINT ("%s(%d): get stat = 0x%08x\n", __FILE__, __LINE__, temp); #endif stat.rsrv0 = (temp & 0xffffff00) >> 8; stat.mcf = (temp & 0x00000080) >> 7; stat.maas = (temp & 0x00000040) >> 6; stat.mbb = (temp & 0x00000020) >> 5; stat.mal = (temp & 0x00000010) >> 4; stat.rsrv1 = (temp & 0x00000008) >> 3; stat.srw = (temp & 0x00000004) >> 2; stat.mif = (temp & 0x00000002) >> 1; stat.rxak = (temp & 0x00000001); return stat; } /********************************************* * function: I2c_Set_Ctrl * * description: Change I2C Control bits, * i.e., write to I2CCR * ********************************************/ static void I2C_Set_Ctrl (unsigned int eumbbar, I2C_CTRL ctrl) { /* new control value */ unsigned int temp = load_runtime_reg (eumbbar, I2CCR); temp &= 0xffffff03; temp |= ((ctrl.men & 0x1) << 7); temp |= ((ctrl.mien & 0x1) << 6); temp |= ((ctrl.msta & 0x1) << 5); temp |= ((ctrl.mtx & 0x1) << 4); temp |= ((ctrl.txak & 0x1) << 3); temp |= ((ctrl.rsta & 0x1) << 2); #ifdef I2CDBG0 PRINT ("%s(%d): set ctrl = 0x%08x\n", __FILE__, __LINE__, temp); #endif store_runtime_reg (eumbbar, I2CCR, temp); } /***************************************** * function: I2C_Get_Ctrl * * description: Query I2C Control bits, * i.e., read I2CCR *****************************************/ static I2C_CTRL I2C_Get_Ctrl (unsigned int eumbbar) { union { I2C_CTRL ctrl; unsigned int temp; } s; s.temp = load_runtime_reg (eumbbar, I2CCR); #ifdef I2CDBG0 PRINT ("%s(%d): get ctrl = 0x%08x\n", __FILE__, __LINE__, s.temp); #endif return s.ctrl; } /**************************************** * function: I2C_Slave_Addr * * description: Process slave address phase. * return I2CSUCCESS if no error * * note: Precondition for calling this function: * I2CSR(MIF) == 1 && * I2CSR(MAAS) == 1 ****************************************/ static I2CStatus I2C_Slave_Addr (unsigned int eumbbar) { I2C_STAT stat = I2C_Get_Stat (eumbbar); I2C_CTRL ctrl = I2C_Get_Ctrl (eumbbar); if (stat.srw == 1) { /* we are asked to xmit */ ctrl.mtx = 1; I2C_Set_Ctrl (eumbbar, ctrl); /* set MTX */ return I2C_Slave_Xmit (eumbbar); } /* we are asked to receive data */ ctrl.mtx = 0; I2C_Set_Ctrl (eumbbar, ctrl); (void) load_runtime_reg (eumbbar, I2CDR); /* do a fake read to start */ return I2CADDRESS; } /*********************************************** * function: I2C_ISR * * description: I2C Interrupt service routine * * note: Precondition: * I2CSR(MIF) == 1 **********************************************/ static I2CStatus I2C_ISR (unsigned int eumbbar) { I2C_STAT stat; I2C_CTRL ctrl; #ifdef I2CDBG0 PRINT ("%s(%d): I2C_ISR\n", __FILE__, __LINE__); #endif stat = I2C_Get_Stat (eumbbar); ctrl = I2C_Get_Ctrl (eumbbar); /* clear MIF */ stat.mif = 0; /* Now let see what kind of event this is */ if (stat.mcf == 1) { /* transfer compete */ /* clear the MIF bit */ I2C_Set_Stat (eumbbar, stat); if (ctrl.msta == 1) { /* master */ if (ctrl.mtx == 1) { /* check if this is the address phase for master receive */ if (MasterRcvAddress == 1) { /* Yes, it is the address phase of master receive */ ctrl.mtx = 0; /* now check how much we want to receive */ if (ByteToRcv == 1 && RcvBufFulStop == 1) { ctrl.txak = 1; } I2C_Set_Ctrl (eumbbar, ctrl); (void) load_runtime_reg (eumbbar, I2CDR); /* fake read first */ MasterRcvAddress = 0; return I2CADDRESS; } /* master xmit */ if (stat.rxak == 0) { /* slave has acknowledged */ return I2C_Master_Xmit (eumbbar); } /* slave has not acknowledged yet, generate a STOP */ if (XmitBufEmptyStop == 1) { ctrl.msta = 0; I2C_Set_Ctrl (eumbbar, ctrl); } return I2CSUCCESS; } /* master receive */ return I2C_Master_Rcv (eumbbar); } /* slave */ if (ctrl.mtx == 1) { /* slave xmit */ if (stat.rxak == 0) { /* master has acknowledged */ return I2C_Slave_Xmit (eumbbar); } /* master has not acknowledged, wait for STOP */ /* do nothing for preventing bus from hung */ return I2CSUCCESS; } /* slave rcv */ return I2C_Slave_Rcv (eumbbar); } else if (stat.maas == 1) { /* received a call from master */ /* clear the MIF bit */ I2C_Set_Stat (eumbbar, stat); /* master is calling us, process the address phase */ return I2C_Slave_Addr (eumbbar); } else { /* has to be arbitration lost */ stat.mal = 0; I2C_Set_Stat (eumbbar, stat); ctrl.msta = 0; /* return to receive mode */ I2C_Set_Ctrl (eumbbar, ctrl); } return I2CSUCCESS; } /****************************************************** * function: I2C_Set_Stat * * description: modify the I2CSR * *****************************************************/ static void I2C_Set_Stat (unsigned int eumbbar, I2C_STAT stat) { union { unsigned int val; I2C_STAT stat; } s_tmp; union { unsigned int val; I2C_STAT stat; } s; s.val = load_runtime_reg (eumbbar, I2CSR); s.val &= 0xffffff08; s_tmp.stat = stat; s.val |= (s_tmp.val & 0xf7); #ifdef I2CDBG0 PRINT ("%s(%d): set stat = 0x%08x\n", __FILE__, __LINE__, s.val); #endif store_runtime_reg (eumbbar, I2CSR, s.val); } /****************************************************** * The following are routines to glue the rest of * U-Boot to the Sandpoint I2C driver. *****************************************************/ void i2c_init (int speed, int slaveadd) { #ifdef DEBUG I2C_Initialize (0x7f, 0, (void *) printf); #else I2C_Initialize (0x7f, 0, 0); #endif } 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 back. */ udelay(10000); return i2c_read (chip, 0, 1, (char *)&tmp, 1); } int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len) { I2CStatus status; uchar xaddr[4]; if (alen > 0) { xaddr[0] = (addr >> 24) & 0xFF; xaddr[1] = (addr >> 16) & 0xFF; xaddr[2] = (addr >> 8) & 0xFF; xaddr[3] = addr & 0xFF; status = I2C_do_buffer (0, I2C_MASTER_XMIT, chip, alen, &xaddr[4 - alen], I2C_NO_STOP, 1, I2C_NO_RESTART); if (status != I2C_SUCCESS) { PRINT ("i2c_read: can't send data address for read\n"); return 1; } } /* The data transfer will be a continuation. */ status = I2C_do_buffer (0, I2C_MASTER_RCV, chip, len, buffer, I2C_STOP, 1, (alen > 0 ? I2C_RESTART : I2C_NO_RESTART)); if (status != I2C_SUCCESS) { PRINT ("i2c_read: can't perform data transfer\n"); return 1; } return 0; } int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len) { I2CStatus status; unsigned char dummy_buffer[I2C_RXTX_LEN + 2]; int i; dummy_buffer[0] = addr & 0xFF; if (alen == 2) dummy_buffer[1] = (addr >> 8) & 0xFF; for (i = 0; i < len; i++) dummy_buffer[i + alen] = buffer[i]; status = I2C_do_buffer (0, I2C_MASTER_XMIT, chip, alen + len, dummy_buffer, I2C_STOP, 1, I2C_NO_RESTART); #ifdef CFG_EEPROM_PAGE_WRITE_DELAY_MS udelay(CFG_EEPROM_PAGE_WRITE_DELAY_MS * 1000); #endif if (status != I2C_SUCCESS) { PRINT ("i2c_write: can't perform data transfer\n"); return 1; } return 0; } uchar i2c_reg_read (uchar i2c_addr, uchar reg) { char buf[1]; i2c_init (0, 0); i2c_read (i2c_addr, reg, 1, buf, 1); return (buf[0]); } void i2c_reg_write (uchar i2c_addr, uchar reg, uchar val) { i2c_init (0, 0); i2c_write (i2c_addr, reg, 1, &val, 1); } #endif /* CONFIG_HARD_I2C */