int tpm_getburst(struct tpm_softc *sc) { int burst, to, rv; to = tpm_tmotohz(TPM_BURST_TMO); burst = 0; while (burst == 0 && to--) { /* * Burst count has to be read from bits 8 to 23 without * touching any other bits, eg. the actual status bits 0 * to 7. */ burst = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS + 1); burst |= bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS + 2) << 8; #ifdef TPM_DEBUG printf("tpm_getburst: read %d\n", burst); #endif if (burst) return burst; rv = tsleep(sc, PRIBIO | PCATCH, "tpm_getburst", 1); if (rv && rv != EWOULDBLOCK) { return 0; } } return 0; }
int tpm_request_locality(struct tpm_softc *sc, int l) { uint32_t r; int to, rv; if (l != 0) return EINVAL; if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) == (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) return 0; bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS, TPM_ACCESS_REQUEST_USE); to = tpm_tmotohz(TPM_ACCESS_TMO); while ((r = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) != (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY) && to--) { rv = tsleep(sc->sc_init, PRIBIO | PCATCH, "tpm_locality", 1); if (rv && rv != EWOULDBLOCK) { #ifdef TPM_DEBUG aprint_debug_dev(sc->sc_dev, "%s: interrupted %d\n", __func__, rv); #endif return rv; } } if ((r & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) != (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) { #ifdef TPM_DEBUG char buf[128]; snprintb(buf, sizeof(buf), TPM_ACCESS_BITS, r); aprint_debug_dev(sc->sc_dev, "%s: access %s\n", __func__, buf); #endif return EBUSY; } return 0; }
int tpm_request_locality(struct tpm_softc *sc, int l) { u_int32_t r; int to, rv; if (l != 0) return EINVAL; if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) == (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) return 0; bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS, TPM_ACCESS_REQUEST_USE); to = tpm_tmotohz(TPM_ACCESS_TMO); while ((r = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) != (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY) && to--) { rv = tsleep(sc->sc_init, PRIBIO | PCATCH, "tpm_locality", 1); if (rv && rv != EWOULDBLOCK) { #ifdef TPM_DEBUG printf("tpm_request_locality: interrupted %d\n", rv); #endif return rv; } } if ((r & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) != (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) { #ifdef TPM_DEBUG printf("tpm_request_locality: access %b\n", r, TPM_ACCESS_BITS); #endif return EBUSY; } return 0; }
/* * Wait on given status bits, uses interrupts where possible, otherwise polls. */ int tpm_waitfor(struct tpm_softc *sc, u_int8_t b0, int tmo, void *c) { u_int8_t b; int re, to, rv; #ifdef TPM_DEBUG printf("tpm_waitfor: b0 %b\n", b0, TPM_STS_BITS); #endif /* * If possible, use interrupts, otherwise poll. * * We use interrupts for TPM_STS_VALID and TPM_STS_DATA_AVAIL (if * the tpm chips supports them) as waiting for those can take * really long. The other TPM_STS* are not needed very often * so we do not support them. */ if (sc->sc_vector != IRQUNK) { b = b0; /* * Wait for data ready. This interrupt only occures * when both TPM_STS_VALID and TPM_STS_DATA_AVAIL are asserted. * Thus we don't have to bother with TPM_STS_VALID * separately and can just return. * * This only holds for interrupts! When using polling * both flags have to be waited for, see below. */ if ((b & TPM_STS_DATA_AVAIL) && (sc->sc_capabilities & TPM_INTF_DATA_AVAIL_INT)) return tpm_waitfor_int(sc, b, tmo, c, TPM_DATA_AVAIL_INT); /* Wait for status valid bit. */ if ((b & TPM_STS_VALID) && (sc->sc_capabilities & TPM_INTF_STS_VALID_INT)) { rv = tpm_waitfor_int(sc, b, tmo, c, TPM_STS_VALID_INT); if (rv != 0) return rv; else b = b0 & ~TPM_STS_VALID; } /* * When all flags are taken care of, return. Otherwise * use polling for eg. TPM_STS_CMD_READY. */ if (b == 0) return 0; } re = 3; restart: /* * If requested wait for TPM_STS_VALID before dealing with * any other flag. Eg. when both TPM_STS_DATA_AVAIL and TPM_STS_VALID * are requested, wait for the latter first. */ b = b0; if (b0 & TPM_STS_VALID) b = TPM_STS_VALID; to = tpm_tmotohz(tmo); again: if ((rv = tpm_waitfor_poll(sc, b, to, c)) != 0) return rv; if ((b & sc->sc_stat) == TPM_STS_VALID) { /* Now wait for other flags. */ b = b0 & ~TPM_STS_VALID; to++; goto again; } if ((sc->sc_stat & b) != b) { #ifdef TPM_DEBUG printf("tpm_waitfor: timeout: stat=%b b=%b\n", sc->sc_stat, TPM_STS_BITS, b, TPM_STS_BITS); #endif if (re-- && (b0 & TPM_STS_VALID)) { bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, TPM_STS_RESP_RETRY); goto restart; } return EIO; } return 0; }
/* Wait for given status bits using interrupts. */ int tpm_waitfor_int(struct tpm_softc *sc, u_int8_t mask, int tmo, void *c, int inttype) { int rv, to; /* Poll and return when condition is already met. */ sc->sc_stat = tpm_status(sc); if ((sc->sc_stat & mask) == mask) return 0; /* * Enable interrupt on tpm chip. Note that interrupts on our * level (SPL_TTY) are disabled (see tpm{read,write} et al) and * will not be delivered to the cpu until we call tsleep(9) below. */ bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) | inttype); bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) | TPM_GLOBAL_INT_ENABLE); /* * Poll once more to remedy the race between previous polling * and enabling interrupts on the tpm chip. */ sc->sc_stat = tpm_status(sc); if ((sc->sc_stat & mask) == mask) { rv = 0; goto out; } to = tpm_tmotohz(tmo); #ifdef TPM_DEBUG printf("tpm_waitfor_int: sleeping for %d ticks on %p\n", to, c); #endif /* * tsleep(9) enables interrupts on the cpu and returns after * wake up with interrupts disabled again. Note that interrupts * generated by the tpm chip while being at SPL_TTY are not lost * but held and delivered as soon as the cpu goes below SPL_TTY. */ rv = tsleep(c, PRIBIO | PCATCH, "tpm_intr", to); sc->sc_stat = tpm_status(sc); #ifdef TPM_DEBUG printf("tpm_waitfor_int: woke up with rv %d stat %b\n", rv, sc->sc_stat, TPM_STS_BITS); #endif if ((sc->sc_stat & mask) == mask) rv = 0; /* Disable interrupts on tpm chip again. */ out: bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) & ~TPM_GLOBAL_INT_ENABLE); bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) & ~inttype); return rv; }