/* -*- linux-c -*- */ /* * Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Reg9050.h" #include "8253xctl.h" #include "ring.h" #include "8253x.h" #include "crc32dcl.h" #include "8253xmcs.h" #include "sp502.h" /* Just to guarantee that strings are null terminated */ #define MEMCPY(dest, src, cnt) \ { \ memcpy((dest), (src), (cnt)); \ (dest)[cnt] = 0; \ } static unsigned char sp502progbyte[] = { SP502_OFF, SP502_RS232, SP502_RS422, SP502_RS485, SP502_RS449, SP502_EIA530, SP502_V35 }; /* * The following routines are the multichannel server I2C serial EPROM routines. */ /* * Set the clock and the data lines of the SEP. */ static void mcs_sep_set(mcs_sep_t *msp, unsigned sdavalid, unsigned sda, unsigned sclvalid, unsigned scl) { #ifdef MAX #undef MAX #endif /* MAX */ #define MAX(x, y) ((x) > (y) ? (x) : (y)) unsigned char csr; unsigned int sleeptime; /* * Ensure sufficient clock */ sleeptime = 0; if (sclvalid) { if (msp->s_scl && !scl) { /* do we have a downgoing transition? */ sleeptime = MAX(1, sleeptime); } else if (!msp->s_scl && scl) { /* upgoing */ sleeptime = MAX(2, sleeptime); } msp->s_scl = scl; } if (sdavalid) { if ((msp->s_sda && !sda) || (!msp->s_sda && sda)) { sleeptime = MAX(1, sleeptime); } msp->s_sda = sda; } if (sleeptime > 0) { udelay(sleeptime); } /* * Construct the CSR byte. */ csr = 0; if (msp->s_sda) { csr |= CIMCMD_CIMCSR_SDA; } if (msp->s_scl) { csr |= CIMCMD_CIMCSR_SCL; } writeb((unsigned char) csr, msp->s_wrptr); } static void mcs_sep_start(mcs_sep_t *msp) { /* * Generate a START condition */ mcs_sep_set(msp, TRUE, TRUE, TRUE, TRUE); mcs_sep_set(msp, TRUE, FALSE, TRUE, TRUE); } static void mcs_sep_stop(mcs_sep_t *msp) { /* * Generate a STOP condition */ mcs_sep_set(msp, TRUE, FALSE, TRUE, TRUE); mcs_sep_set(msp, TRUE, TRUE, TRUE, TRUE); } /* * Send out a single byte. */ static void mcs_sep_byte(mcs_sep_t *msp, unsigned char val) { register int bitcount; /* Clock may be high ... lower the clock */ mcs_sep_set(msp, TRUE, FALSE, TRUE, FALSE); bitcount = 8; while (TRUE) { mcs_sep_set(msp, TRUE, (val & 0x80) != 0, TRUE, FALSE); mcs_sep_set(msp, TRUE, (val & 0x80) != 0, TRUE, TRUE); bitcount--; if (bitcount == 0) { break; } val <<= 1; } /* Clock is high ... lower the clock */ mcs_sep_set(msp, FALSE, FALSE, TRUE, FALSE); } /* * Wait for an acknowledge cycle. Expects the clock to be low. */ static unsigned mcs_sep_waitsep(mcs_sep_t *msp) { int loopcount; unsigned char cimcsr; /* Stop driving SDA */ mcs_sep_set(msp, TRUE, TRUE, FALSE, FALSE); /* Raise the clock */ mcs_sep_set(msp, FALSE, FALSE, TRUE, TRUE); loopcount = 1000; while (loopcount != 0) { cimcsr = readb(msp->s_rdptr); if ((cimcsr & CIMCMD_CIMCSR_SDA) == 0) { break; } loopcount--; } /* Lower the clock */ mcs_sep_set(msp, FALSE, FALSE, TRUE, FALSE); if (loopcount == 0) { return FALSE; } else { return TRUE; } } /* * Read the given CIM's SEP, starting at the given address, into * the given buffer, for the given length. * * Returns -1 if there was a failure, otherwise the byte count. */ static int mcs_sep_read(mcs_sep_t *msp, unsigned short addr, unsigned char *buf, unsigned int nbytes) { unsigned char cmdaddr, val, cimcsr; unsigned int bytecount, bitcount; mcs_sep_start(msp); /* * First, send out a dummy WRITE command with no data. */ cmdaddr = 0xa0 | (((addr >> 8) & 0x7) << 1) | 0x0; mcs_sep_byte(msp, cmdaddr); if (!mcs_sep_waitsep(msp)) { return -1; } /* * Now, send the reset of the address. */ mcs_sep_byte(msp, (unsigned char) addr); if (!mcs_sep_waitsep(msp)) { return -1; } /* * Now, restart with a read command. */ mcs_sep_start(msp); cmdaddr = 0xa0 | (((addr >> 8) & 0x7) << 1) | 0x1; mcs_sep_byte(msp, cmdaddr); if (!mcs_sep_waitsep(msp)) { return -1; } /* * Now, start reading the bytes. */ bytecount = 0; while (TRUE) { bitcount = 8; val = 0; while (TRUE) { mcs_sep_set(msp, TRUE, TRUE, TRUE, TRUE); cimcsr = readb(msp->s_rdptr); if ((cimcsr & CIMCMD_CIMCSR_SDA) != 0) { val |= 0x01; } mcs_sep_set(msp, FALSE, FALSE, TRUE, FALSE); bitcount--; if (bitcount == 0) { break; } val <<= 1; } *buf++ = val; bytecount++; nbytes--; if (nbytes == 0) { break; } /* * Send the acknowledge. */ mcs_sep_set(msp, FALSE, FALSE, TRUE, FALSE); mcs_sep_set(msp, TRUE, FALSE, TRUE, TRUE); mcs_sep_set(msp, FALSE, FALSE, TRUE, FALSE); } mcs_sep_stop(msp); return (int) bytecount; } unsigned int mcs_ciminit(SAB_BOARD *bptr, AURA_CIM *cim) { mcs_sep_t ms; ms.s_rdptr = (unsigned char *) (bptr->CIMCMD_REG + (CIMCMD_RDCIMCSR | (cim->ci_num << CIMCMD_CIMSHIFT))); ms.s_wrptr = (unsigned char *) (bptr->CIMCMD_REG + (CIMCMD_WRCIMCSR | (cim->ci_num << CIMCMD_CIMSHIFT))); ms.s_scl = ms.s_sda = FALSE; if (mcs_sep_read(&ms, (unsigned short) 0, &(cim->ci_sep[0]), sizeof(cim->ci_sep)) != sizeof(cim->ci_sep) || cim->ci_sep[MCS_SEP_MAGIC] != MCS_SEP_MAGICVAL) { if (cim->ci_sep[MCS_SEP_MAGIC] != MCS_SEP_MAGICVAL) { DEBUGPRINT((KERN_ALERT "auraXX20: invalid CIM %d serial EPROM on board %d", cim->ci_num, bptr->board_number)); } else { DEBUGPRINT((KERN_ALERT "auraXX20: error reading CIM %d serial EPROM on board %d", cim->ci_num, bptr->board_number)); } cim->ci_clkspeed = WANMCS_CLKSPEED; cim->ci_clkspdsrc = -1; cim->ci_spdgrd = 10; cim->ci_spdgrdsrc = -1; cim->ci_flags = 0; cim->ci_rev[0] = '\0'; cim->ci_sn[0] = '\0'; cim->ci_mfgdate[0] = '\0'; cim->ci_mfgloc[0] = '\0'; /* * Diddle the port setup registers to determine if this * CIM was built up for RS232 or SP502. */ writew((unsigned short) 0xffff, (unsigned short *) (bptr->CIMCMD_REG + (CIMCMD_WRSETUP | (cim->ci_num << CIMCMD_CIMSHIFT)))); #ifdef RICHARD_DELAY udelay(1); #endif /* RICHARD_DELAY */ if (readw((unsigned short *) (bptr->CIMCMD_REG + (CIMCMD_RDSETUP | (cim->ci_num << CIMCMD_CIMSHIFT)))) == 0xffff) { writew(0, (unsigned short *) (bptr->CIMCMD_REG + (CIMCMD_WRSETUP | (cim->ci_num << CIMCMD_CIMSHIFT)))); #ifdef RICHARD_DELAY udelay(1); #endif /* RICHARD_DELAY */ if (readw((unsigned short *) (bptr->CIMCMD_REG + (CIMCMD_RDSETUP | (cim->ci_num << CIMCMD_CIMSHIFT)))) == 0) { cim->ci_type = CIM_SP502; } else { cim->ci_type = CIM_RS232; } } else { cim->ci_type = CIM_RS232; } if (cim->ci_type == CIM_SP502) { cim->ci_flags |= CIM_SYNC; } } else { /* * Pick through the serial EPROM contents and derive * the values we need. */ MEMCPY(&(cim->ci_rev[0]), &(cim->ci_sep[MCS_SEP_REV]), MCS_SEP_REVLEN); MEMCPY(&(cim->ci_sn[0]), &(cim->ci_sep[MCS_SEP_SN]), MCS_SEP_SNLEN); MEMCPY(&(cim->ci_mfgdate[0]), &(cim->ci_sep[MCS_SEP_MFGDATE]), MCS_SEP_MFGDATELEN); MEMCPY(&(cim->ci_mfgloc[0]), &(cim->ci_sep[MCS_SEP_MFGLOC]), MCS_SEP_MFGLOCLEN); cim->ci_clkspeed = (unsigned long) cim->ci_sep[MCS_SEP_CLKSPD] | ((unsigned long) cim->ci_sep[MCS_SEP_CLKSPD + 1] << 8) | ((unsigned long) cim->ci_sep[MCS_SEP_CLKSPD + 2] << 16) | ((unsigned long) cim->ci_sep[MCS_SEP_CLKSPD + 3] << 24); cim->ci_clkspdsrc = SEPROM; cim->ci_spdgrd = (int) cim->ci_sep[MCS_SEP_SPDGRD]; cim->ci_spdgrdsrc = SEPROM; cim->ci_flags = (unsigned long) cim->ci_sep[MCS_SEP_FLAGS]; cim->ci_type = (int) cim->ci_sep[MCS_SEP_TYPE]; } /* * Possibly initialize the port setup registers. */ if (cim->ci_type == CIM_SP502) { unsigned short alloff; #ifdef DEBUG_VERBOSE unsigned short readback; #endif int offset; /* * Turn off all of the electrical interfaces. The * hardware *should* initialize to this state, but the * prototype, at least, does not. Note that this setting * is reflected in the SIF_OFF setting of l_interface in * mustard_lineinit, above. */ alloff = (unsigned short) SP502_OFF | ((unsigned short) SP502_OFF << 4) | ((unsigned short) SP502_OFF << 8) | ((unsigned short) SP502_OFF << 12); for (offset = 0; offset < 8; offset++) { #ifdef DEBUG_VERBOSE DEBUGPRINT((KERN_ALERT "cim %d setup reg #%d: writing 0x%x to 0x%x", cim->ci_num, offset, (unsigned) alloff, (CIMCMD_WRSETUP | (offset << 1) | (cim->ci_num << CIMCMD_CIMSHIFT)))); #endif /* DEBUG_VERBOSE */ writew((unsigned short) alloff, (unsigned short *) (bptr->CIMCMD_REG + (CIMCMD_WRSETUP | (offset << 1) | (cim->ci_num << CIMCMD_CIMSHIFT)))); #ifdef RICHARD_DELAY udelay(1); #endif /* RICHARD_DELAY */ #ifdef DEBUG_VERBOSE readback = readw((unsigned short *) (bptr->CIMCMD_REG + (CIMCMD_RDSETUP | (offset << 1) | (cim->ci_num << CIMCMD_CIMSHIFT)))); if (readback != alloff) { DEBUGPRINT((KERN_ALERT "cim %d setup reg #%d: readback (0x%x) should be 0x%x", cim->ci_num, offset, readback, alloff)); } #endif /* DEBUG_VERBOSE */ } } /* * Clear out the CIM CSR with the exception of the LED. */ writeb((unsigned char) 0, (unsigned char *) (bptr->CIMCMD_REG + (CIMCMD_WRCIMCSR | (cim->ci_num << CIMCMD_CIMSHIFT)))); return TRUE; } int wanmcs_reset(SAB_BOARD* bptr) /* note the board is the host card not the * individual extension boards */ { int counter; #if 0 /* from the ASE driver */ /* * Program the AMCC to deactivate the write FIFO. */ ASE_PUT32(cboard->b_bridgehandle, (aseuint32_t *) (cboard->b_bridge + AMCC_PTCR), ((aseuint32_t) (AMCC_PTMODE | AMCC_WRFIFODIS) << 24) | ((aseuint32_t) (AMCC_PTMODE | AMCC_WRFIFODIS) << 16) | ((aseuint32_t) (AMCC_PTMODE | AMCC_WRFIFODIS) << 8) | (aseuint32_t) (AMCC_PTMODE | AMCC_WRFIFODIS)); #endif /* 0 */ /* * First thing: do a reset of the local bus on the MIC * by diddling the Add-On Reset bit in the RCR. */ writel((unsigned int) AMCC_AORESET, (unsigned int *)(bptr->AMCC_REG + AMCC_RCR)); udelay(10); /* wait for 10 us. */ writel((unsigned int) 0, (unsigned int *)(bptr->AMCC_REG + AMCC_RCR)); udelay(10); /* wait for 10 us. */ /* * Now the PCI bridge is reset. Try to establish * a link through the Glink chipset. */ for (counter = 1000; counter != 0; counter--) { writeb(0, (unsigned char*) (bptr->MICCMD_REG + MICCMD_MICCSR)); udelay(5); if((readb((unsigned char*) (bptr->MICCMD_REG + MICCMD_MICCSR)) & MICCMD_MICCSR_GLE) == 0) { break; } } /* * Did we run out of time? */ if (counter == 0) { printk(KERN_ALERT "AMCC5920: board %p: GLink did not reset -- is the MEB on?", bptr); return FALSE; } /* * Now, hit the reset in the MEB. */ writeb(0, (unsigned int *) (bptr->CIMCMD_REG + CIMCMD_RESETENA)); udelay(5); writeb(0, (unsigned int *) (bptr->CIMCMD_REG + CIMCMD_RESETDIS)); /* * And we're done! */ return TRUE; } void aura_sp502_program(SAB_PORT *port, register unsigned int sigindex) { register unsigned char prognibble; SAB_BOARD *bptr; unsigned int cimnum; unsigned int chipno; unsigned int portno; unsigned int rdaddressreceiver; unsigned int rdaddresstransmitter; unsigned int wraddressreceiver; unsigned int wraddresstransmitter; unsigned short datareceiver; unsigned short datatransmitter; bptr = port->board; cimnum = port->chip->c_cim->ci_num; chipno = (port->chip->c_chipno & 1); /* chip number relative to EB not MCS */ portno = (port->portno + (8 * chipno)); /* portno on a per EB basis */ prognibble = (sp502progbyte[sigindex] & 0x0F); /* first 4 shorts contain receiver control bits */ rdaddressreceiver = (((unsigned int)bptr->CIMCMD_REG) + ((cimnum << CIMCMD_CIMSHIFT) | CIMCMD_RDSETUP | ((portno/4) << CIMCMD_CTRLSHIFT))); /* second 4 shorts contain transmitter control bits */ rdaddresstransmitter = (((unsigned int)bptr->CIMCMD_REG) + ((cimnum << CIMCMD_CIMSHIFT) | CIMCMD_RDSETUP | ((4+(portno/4)) << CIMCMD_CTRLSHIFT))); wraddressreceiver = (((unsigned int)bptr->CIMCMD_REG) + ((cimnum << CIMCMD_CIMSHIFT) | CIMCMD_WRSETUP | ((portno/4) << CIMCMD_CTRLSHIFT))); wraddresstransmitter = (((unsigned int)bptr->CIMCMD_REG) + ((cimnum << CIMCMD_CIMSHIFT) | CIMCMD_WRSETUP | ((4+(portno/4)) << CIMCMD_CTRLSHIFT))); /* read out the current receiver status */ datareceiver = readw((unsigned short*) rdaddressreceiver); /* clear out nibble that corresponds to current port */ datareceiver &= (unsigned short) ~(0x0F << ((3 - (portno % 4)) * 4)); /* or in new receiver control field */ datareceiver |= (prognibble << ((3 - (portno % 4)) * 4)); /* write back the short that corresponds to 4 ports */ writew(datareceiver, (unsigned short*) wraddressreceiver); /* just as above except that next 4 shorts correspond to transmitters */ datatransmitter = readw((unsigned short*) rdaddresstransmitter); datatransmitter &= (unsigned short) ~(0x0F << ((3 - (portno % 4)) * 4)); datatransmitter |= (prognibble << ((3 - (portno % 4)) * 4)); writew(datatransmitter, (unsigned short*) wraddresstransmitter); }