/***************************************************************************** * hermes_cor_reset * * * * This is the first step in initializing the card's firmware and hardware: * * write HERMES_PCI_COR_MASK to the Configuration Option Register * *****************************************************************************/ int hermes_cor_reset (hermes_t *hw) { int k; u16_t reg; /* Assert the reset until the card notice */ hermes_write_reg (hw, HERMES_PCI_COR, HERMES_PCI_COR_MASK); milli_delay (HERMES_PCI_COR_ONT); /* Give time for the card to recover from this hard effort */ hermes_write_reg (hw, HERMES_PCI_COR, 0x0000); milli_delay (HERMES_PCI_COR_OFFT); /* The card is ready when it's no longer busy */ k = HERMES_PCI_COR_BUSYT; reg = hermes_read_reg (hw, HERMES_CMD); while (k && (reg & HERMES_CMD_BUSY)) { k--; milli_delay (1); reg = hermes_read_reg (hw, HERMES_CMD); } /* Did we timeout ? */ if (reg & HERMES_CMD_BUSY) { printf ("Busy timeout after resetting the COR\n"); return -1; } return (0); }
/* Set up a BAP to read a particular chunk of data from card's internal buffer. * * Returns: < 0 on internal failure (errno), 0 on success, >0 on error * from firmware * * Callable from any context */ static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) { int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; int k; u16 reg; /* Paranoia.. */ if ( (offset > HERMES_BAP_OFFSET_MAX) || (offset % 2) ) return -EINVAL; k = HERMES_BAP_BUSY_TIMEOUT; reg = hermes_read_reg(hw, oreg); while ((reg & HERMES_OFFSET_BUSY) && k) { k--; udelay(1); reg = hermes_read_reg(hw, oreg); } #ifdef HERMES_DEBUG_BUFFER hw->profile[HERMES_BAP_BUSY_TIMEOUT - k]++; if (k < HERMES_BAP_BUSY_TIMEOUT) { struct hermes_debug_entry *e = &hw->dbuf[(hw->dbufp++) % HERMES_DEBUG_BUFSIZE]; e->bap = bap; e->id = id; e->offset = offset; e->cycles = HERMES_BAP_BUSY_TIMEOUT - k; } #endif if (reg & HERMES_OFFSET_BUSY) return -ETIMEDOUT; /* Now we actually set up the transfer */ hermes_write_reg(hw, sreg, id); hermes_write_reg(hw, oreg, offset); /* Wait for the BAP to be ready */ k = HERMES_BAP_BUSY_TIMEOUT; reg = hermes_read_reg(hw, oreg); while ( (reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) { k--; udelay(1); reg = hermes_read_reg(hw, oreg); } if (reg & HERMES_OFFSET_BUSY) { return -ETIMEDOUT; } if (reg & HERMES_OFFSET_ERR) { return -EIO; } return 0; }
/* Set up a BAP to read a particular chunk of data from card's internal buffer. * * Returns: < 0 on internal failure (errno), 0 on success, >0 on error * from firmware * * Callable from any context */ static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) { int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; int k; u16 reg; /* Paranoia.. */ if ( (offset > HERMES_BAP_OFFSET_MAX) || (offset % 2) ) return -EINVAL; k = HERMES_BAP_BUSY_TIMEOUT; reg = hermes_read_reg(hw, oreg); while ((reg & HERMES_OFFSET_BUSY) && k) { k--; udelay(1); reg = hermes_read_reg(hw, oreg); } if (reg & HERMES_OFFSET_BUSY) return -ETIMEDOUT; /* Now we actually set up the transfer */ hermes_write_reg(hw, sreg, id); hermes_write_reg(hw, oreg, offset); /* Wait for the BAP to be ready */ k = HERMES_BAP_BUSY_TIMEOUT; reg = hermes_read_reg(hw, oreg); while ( (reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) { k--; udelay(1); reg = hermes_read_reg(hw, oreg); } if (reg != offset) { printk(KERN_ERR "hermes @ %p: BAP%d offset %s: " "reg=0x%x id=0x%x offset=0x%x\n", hw->iobase, bap, (reg & HERMES_OFFSET_BUSY) ? "timeout" : "error", reg, id, offset); if (reg & HERMES_OFFSET_BUSY) { return -ETIMEDOUT; } return -EIO; /* error or wrong offset */ } return 0; }
static int hermes_bap_seek(struct hermes *hw, int bap, u16 id, u16 offset) { int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; int k; u16 reg; if ((offset > HERMES_BAP_OFFSET_MAX) || (offset % 2)) return -EINVAL; k = HERMES_BAP_BUSY_TIMEOUT; reg = hermes_read_reg(hw, oreg); while ((reg & HERMES_OFFSET_BUSY) && k) { k--; udelay(1); reg = hermes_read_reg(hw, oreg); } if (reg & HERMES_OFFSET_BUSY) return -ETIMEDOUT; hermes_write_reg(hw, sreg, id); hermes_write_reg(hw, oreg, offset); k = HERMES_BAP_BUSY_TIMEOUT; reg = hermes_read_reg(hw, oreg); while ((reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) { k--; udelay(1); reg = hermes_read_reg(hw, oreg); } if (reg != offset) { printk(KERN_ERR "hermes @ %p: BAP%d offset %s: " "reg=0x%x id=0x%x offset=0x%x\n", hw->iobase, bap, (reg & HERMES_OFFSET_BUSY) ? "timeout" : "error", reg, id, offset); if (reg & HERMES_OFFSET_BUSY) return -ETIMEDOUT; return -EIO; } return 0; }
/* Set up a BAP to read a particular chunk of data from card's internal buffer. * * Returns: < 0 on internal failure (errno), 0 on success, >0 on error * from firmware * * Callable from any context */ int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) { int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; int k; u16 reg; /* Paranoia.. */ if ( (offset > HERMES_BAP_OFFSET_MAX) || (offset % 2) ) return -EINVAL; k = BAP_BUSY_TIMEOUT; reg = hermes_read_reg(hw, oreg); while ((reg & HERMES_OFFSET_BUSY) && k) { k--; udelay(1); reg = hermes_read_reg(hw, oreg); } if (reg & HERMES_OFFSET_BUSY) return -ETIMEDOUT; /* Now we actually set up the transfer */ hermes_write_reg(hw, sreg, id); hermes_write_reg(hw, oreg, offset); /* Wait for the BAP to be ready */ k = BAP_BUSY_TIMEOUT; reg = hermes_read_reg(hw, oreg); while ( (reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) { k--; udelay(1); reg = hermes_read_reg(hw, oreg); } if (reg & HERMES_OFFSET_BUSY) { DEBUG(1,"hermes_bap_seek: timeout\n"); return -ETIMEDOUT; } if (reg & HERMES_OFFSET_ERR) { DEBUG(1,"hermes_bap_seek: BAP error\n"); return -EIO; } return 0; }
/* Read a Length-Type-Value record from the card. * * If length is NULL, we ignore the length read from the card, and * read the entire buffer regardless. This is useful because some of * the configuration records appear to have incorrect lengths in * practice. * * Callable from user or bh context. */ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, u16 *length, void *buf) { int err = 0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; u16 rlength, rtype; unsigned nwords; if ( (bufsize < 0) || (bufsize % 2) ) return -EINVAL; err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL); if (err) return err; err = hermes_bap_seek(hw, bap, rid, 0); if (err) return err; rlength = hermes_read_reg(hw, dreg); if (! rlength) return -ENOENT; rtype = hermes_read_reg(hw, dreg); if (length) *length = rlength; if (rtype != rid) printk(KERN_WARNING "hermes @ %s0x%lx: " "hermes_read_ltv(): rid (0x%04x) does not match type (0x%04x)\n", IO_TYPE(hw), hw->iobase, rid, rtype); if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize) printk(KERN_WARNING "hermes @ %s0x%lx: " "Truncating LTV record from %d to %d bytes. " "(rid=0x%04x, len=0x%04x)\n", IO_TYPE(hw), hw->iobase, HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); nwords = min((unsigned)rlength - 1, bufsize / 2); hermes_read_words(hw, dreg, buf, nwords); return 0; }
/* Read a Length-Type-Value record from the card. * * If length is NULL, we ignore the length read from the card, and * read the entire buffer regardless. This is useful because some of * the configuration records appear to have incorrect lengths in * practice. * * Callable from user or bh context. */ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize, u16 *length, void *buf) { int err = 0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; u16 rlength, rtype; hermes_response_t resp; if (bufsize % 2) return -EINVAL; err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, &resp); if (err) goto out; err = hermes_bap_seek(hw, bap, rid, 0); if (err) goto out; rlength = hermes_read_reg(hw, dreg); rtype = hermes_read_reg(hw, dreg); if (length) *length = rlength; if (rtype != rid) printk(KERN_WARNING "hermes_read_ltv(): rid (0x%04x) does " "not match type (0x%04x)\n", rid, rtype); if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize) printk(KERN_WARNING "hermes @ 0x%x: Truncating LTV record from %d to %d bytes. " "(rid=0x%04x, len=0x%04x)\n", hw->iobase, HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); /* FIXME: we should read the min of the requested length and the actual record length */ hermes_read_words(hw, dreg, buf, bufsize / 2); out: return err; }
/***************************************************************************** * hermes_issue_cmd * * * * Issue a command to the chip. Waiting for it to complete is the caller's * * problem. The only thing we have to do first is to see whether we can * * actually write something in the CMD register: is it unbusy? * * Returns -EBUSY if the command register is busy, 0 on success. * *****************************************************************************/ static int hermes_issue_cmd (hermes_t * hw, u16_t cmd, u16_t param0) { int k = HERMES_CMD_BUSY_TIMEOUT; u16_t reg; /* First wait for the command register to unbusy */ reg = hermes_read_reg (hw, HERMES_CMD); while ((reg & HERMES_CMD_BUSY) && k) { k--; micro_delay (1); reg = hermes_read_reg (hw, HERMES_CMD); } /* it takes too long. Bailing out */ if (reg & HERMES_CMD_BUSY) { printf("Hermes: HERMES_CMD_BUSY timeout\n"); return -EBUSY; } /* write the values to the right registers */ hermes_write_reg (hw, HERMES_PARAM2, 0); hermes_write_reg (hw, HERMES_PARAM1, 0); hermes_write_reg (hw, HERMES_PARAM0, param0); hermes_write_reg (hw, HERMES_CMD, cmd); return 0; }
/***************************************************************************** * hermes_init * * * * Initialize the card * *****************************************************************************/ int hermes_init (hermes_t * hw) { u32_t status, reg, resp0; int err = 0; int k; /* We don't want to be interrupted while resetting the chipset. By * setting the control mask for hardware interrupt generation to 0, * we won't be disturbed*/ hw->inten = 0x0; hermes_write_reg (hw, HERMES_INTEN, 0); /* Acknowledge any pending events waiting for acknowledgement. We * assume there won't be any important to take care off */ hermes_write_reg (hw, HERMES_EVACK, 0xffff); /* Normally it's a "can't happen" for the command register to * be busy when we go to issue a command because we are * serializing all commands. However we want to have some * chance of resetting the card even if it gets into a stupid * state, so we actually wait to see if the command register * will unbusy itself here. */ k = HERMES_CMD_BUSY_TIMEOUT; reg = hermes_read_reg (hw, HERMES_CMD); while (k && (reg & HERMES_CMD_BUSY)) { if (reg == 0xffff) { /* Special case - the card has probably * been removed, so don't wait for the * timeout */ printf("Hermes: Card removed?\n"); return -ENODEV; } k--; micro_delay (1); reg = hermes_read_reg (hw, HERMES_CMD); } /* No need to explicitly handle the timeout - if we've timed * out hermes_issue_cmd() will probably return -EBUSY below. * But i check to be sure :-) */ if (reg & HERMES_CMD_BUSY) { printf("Hermes: Timeout waiting for the CMD_BUSY to unset\n"); return -EBUSY; } /* According to the documentation, EVSTAT may contain * obsolete event occurrence information. We have to acknowledge * it by writing EVACK. */ reg = hermes_read_reg (hw, HERMES_EVSTAT); hermes_write_reg (hw, HERMES_EVACK, reg); err = hermes_issue_cmd (hw, HERMES_CMD_INIT, 0); if (err){ printf("Hermes: errornr: 0x%x issueing HERMES_CMD_INIT\n", err); return err; } /* here we start waiting for the above command,CMD_INIT, to complete. * Completion is noticeable when the HERMES_EV_CMD bit in the * HERMES_EVSTAT register is set to 1 */ reg = hermes_read_reg (hw, HERMES_EVSTAT); k = HERMES_CMD_INIT_TIMEOUT; while ((!(reg & HERMES_EV_CMD)) && k) { k--; micro_delay (10); reg = hermes_read_reg (hw, HERMES_EVSTAT); } /* the software support register 0 (there are 3) is filled with a * magic number. With this one can test the availability of the card */ hermes_write_reg (hw, HERMES_SWSUPPORT0, HERMES_MAGIC); if (!hermes_present (hw)) { printf("Hermes: Card not present?: got mag. nr.0x%x\n", hermes_read_reg (hw, HERMES_SWSUPPORT0)); } if (!(reg & HERMES_EV_CMD)) { printf("hermes @ %x: Timeout waiting for card to reset\n", hw->iobase); return -ETIMEDOUT; } status = hermes_read_reg (hw, HERMES_STATUS); resp0 = hermes_read_reg (hw, HERMES_RESP0); /* after having issued the command above, the completion set a bit in * the EVSTAT register. This has to be acknowledged, as follows */ hermes_write_reg (hw, HERMES_EVACK, HERMES_EV_CMD); /* Was the status, the result of the issued command, ok? */ /* The expression below should be zero. Non-zero means an error */ if (status & HERMES_STATUS_RESULT) { printf("Hermes:Result of INIT_CMD wrong.error value: 0x%x\n", (status & HERMES_STATUS_RESULT) >> 8); err = -EIO; }
/***************************************************************************** * hermes_present * * * * Check whether we have access to the card. Does the SWSUPPORT0 contain the * * value we put in it earlier? * *****************************************************************************/ PRIVATE int hermes_present (hermes_t * hw) { int i = hermes_read_reg (hw, HERMES_SWSUPPORT0) == HERMES_MAGIC; if (!i) printf("Hermes: Error, card not present?\n"); return i; }