patch-2.3.20 linux/arch/ppc/mbxboot/iic.c

Next file: linux/arch/ppc/mbxboot/m8xx_tty.c
Previous file: linux/arch/ppc/mbxboot/head.S
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.19/linux/arch/ppc/mbxboot/iic.c linux/arch/ppc/mbxboot/iic.c
@@ -0,0 +1,250 @@
+
+/* 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 "../8xx_io/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);
+u_char	aschex_to_byte(u_char *cp);
+
+static	int	iic_init_done;
+
+static void
+iic_init()
+{
+	volatile iic_t *iip;
+	volatile i2c8xx_t *i2c;
+	volatile cbd_t	*tbdf, *rbdf;
+	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));
+
+	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	dpaddr, 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 = 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 = 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;
+}
+
+/* Because I didn't find anything that would do this.......
+*/
+u_char
+aschex_to_byte(u_char *cp)
+{
+	u_char	byte, c;
+
+	c = *cp++;
+
+	if ((c >= 'A') && (c <= 'F')) {
+		c -= 'A';
+		c += 10;
+	}
+	else if ((c >= 'a') && (c <= 'f')) {
+		c -= 'a';
+		c += 10;
+	}
+	else {
+		c -= '0';
+	}
+	
+	byte = c * 16;
+
+	c = *cp;
+
+	if ((c >= 'A') && (c <= 'F')) {
+		c -= 'A';
+		c += 10;
+	}
+	else if ((c >= 'a') && (c <= 'f')) {
+		c -= 'a';
+		c += 10;
+	}
+	else {
+		c -= '0';
+	}
+	
+	byte += c;
+
+	return(byte);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)