OpenCloudOS-Kernel/arch/ppc/boot/simple/iic.c

215 lines
4.9 KiB
C

/* Minimal support functions to read configuration from IIC EEPROMS
* on MPC8xx boards. Originally written for RPGC RPX-Lite.
* Dan Malek (dmalek@jlc.net).
*/
#include <linux/types.h>
#include <asm/uaccess.h>
#include <asm/mpc8xx.h>
#include <asm/commproc.h>
/* IIC functions.
* These are just the basic master read/write operations so we can
* examine serial EEPROM.
*/
void iic_read(uint devaddr, u_char *buf, uint offset, uint count);
static int iic_init_done;
static void
iic_init(void)
{
volatile iic_t *iip;
volatile i2c8xx_t *i2c;
volatile cpm8xx_t *cp;
volatile immap_t *immap;
uint dpaddr;
immap = (immap_t *)IMAP_ADDR;
cp = (cpm8xx_t *)&(immap->im_cpm);
/* Reset the CPM. This is necessary on the 860 processors
* that may have started the SCC1 ethernet without relocating
* the IIC.
* This also stops the Ethernet in case we were loaded by a
* BOOTP rom monitor.
*/
cp->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG);
/* Wait for it.
*/
while (cp->cp_cpcr & (CPM_CR_RST | CPM_CR_FLG));
/* Remove any microcode patches. We will install our own
* later.
*/
cp->cp_cpmcr1 = 0;
cp->cp_cpmcr2 = 0;
cp->cp_cpmcr3 = 0;
cp->cp_cpmcr4 = 0;
cp->cp_rccr = 0;
iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
i2c = (i2c8xx_t *)&(immap->im_i2c);
/* Initialize Port B IIC pins.
*/
cp->cp_pbpar |= 0x00000030;
cp->cp_pbdir |= 0x00000030;
cp->cp_pbodr |= 0x00000030;
/* Initialize the parameter ram.
*/
/* Allocate space for a two transmit and one receive buffer
* descriptor in the DP ram.
* For now, this address seems OK, but it may have to
* change with newer versions of the firmware.
*/
dpaddr = 0x0840;
/* Set up the IIC parameters in the parameter ram.
*/
iip->iic_tbase = dpaddr;
iip->iic_rbase = dpaddr + (2 * sizeof(cbd_t));
iip->iic_tfcr = SMC_EB;
iip->iic_rfcr = SMC_EB;
/* This should really be done by the reader/writer.
*/
iip->iic_mrblr = 128;
/* Initialize Tx/Rx parameters.
*/
cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
while (cp->cp_cpcr & CPM_CR_FLG);
/* Select an arbitrary address. Just make sure it is unique.
*/
i2c->i2c_i2add = 0x34;
/* Make clock run maximum slow.
*/
i2c->i2c_i2brg = 7;
/* Disable interrupts.
*/
i2c->i2c_i2cmr = 0;
i2c->i2c_i2cer = 0xff;
/* Enable SDMA.
*/
immap->im_siu_conf.sc_sdcr = 1;
iic_init_done = 1;
}
/* Read from IIC.
* Caller provides device address, memory buffer, and byte count.
*/
static u_char iitemp[32];
void
iic_read(uint devaddr, u_char *buf, uint offset, uint count)
{
volatile iic_t *iip;
volatile i2c8xx_t *i2c;
volatile cbd_t *tbdf, *rbdf;
volatile cpm8xx_t *cp;
volatile immap_t *immap;
u_char *tb;
uint temp;
/* If the interface has not been initialized, do that now.
*/
if (!iic_init_done)
iic_init();
immap = (immap_t *)IMAP_ADDR;
cp = (cpm8xx_t *)&(immap->im_cpm);
iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
i2c = (i2c8xx_t *)&(immap->im_i2c);
tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];
/* Send a "dummy write" operation. This is a write request with
* only the offset sent, followed by another start condition.
* This will ensure we start reading from the first location
* of the EEPROM.
*/
tb = iitemp;
tb = (u_char *)(((uint)tb + 15) & ~15);
tbdf->cbd_bufaddr = (int)tb;
*tb = devaddr & 0xfe; /* Device address */
*(tb+1) = offset; /* Offset */
tbdf->cbd_datlen = 2; /* Length */
tbdf->cbd_sc =
BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START;
i2c->i2c_i2mod = 1; /* Enable */
i2c->i2c_i2cer = 0xff;
i2c->i2c_i2com = 0x81; /* Start master */
/* Wait for IIC transfer.
*/
#if 0
while ((i2c->i2c_i2cer & 3) == 0);
if (tbdf->cbd_sc & BD_SC_READY)
printf("IIC ra complete but tbuf ready\n");
#else
temp = 10000000;
while ((tbdf->cbd_sc & BD_SC_READY) && (temp != 0))
temp--;
#if 0
/* We can't do this...there is no serial port yet!
*/
if (temp == 0) {
printf("Timeout reading EEPROM\n");
return;
}
#endif
#endif
/* Chip errata, clear enable.
*/
i2c->i2c_i2mod = 0;
/* To read, we need an empty buffer of the proper length.
* All that is used is the first byte for address, the remainder
* is just used for timing (and doesn't really have to exist).
*/
tbdf->cbd_bufaddr = (int)tb;
*tb = devaddr | 1; /* Device address */
rbdf->cbd_bufaddr = (uint)buf; /* Desination buffer */
tbdf->cbd_datlen = rbdf->cbd_datlen = count + 1; /* Length */
tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START;
rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;
/* Chip bug, set enable here.
*/
i2c->i2c_i2mod = 1; /* Enable */
i2c->i2c_i2cer = 0xff;
i2c->i2c_i2com = 0x81; /* Start master */
/* Wait for IIC transfer.
*/
#if 0
while ((i2c->i2c_i2cer & 1) == 0);
if (rbdf->cbd_sc & BD_SC_EMPTY)
printf("IIC read complete but rbuf empty\n");
#else
temp = 10000000;
while ((tbdf->cbd_sc & BD_SC_READY) && (temp != 0))
temp--;
#endif
/* Chip errata, clear enable.
*/
i2c->i2c_i2mod = 0;
}