📄 i2c1.c
字号:
/************************************************************* * * Copyright @ Motorola, 1999 * ************************************************************/#include <common.h>#ifdef CONFIG_HARD_I2C#include <i2c.h>#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_printstatic 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -