summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/pci/arc.c158
1 files changed, 154 insertions, 4 deletions
diff --git a/sys/dev/pci/arc.c b/sys/dev/pci/arc.c
index 006097319d5..72397160a4b 100644
--- a/sys/dev/pci/arc.c
+++ b/sys/dev/pci/arc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: arc.c,v 1.20 2006/08/15 04:26:58 dlg Exp $ */
+/* $OpenBSD: arc.c,v 1.21 2006/08/17 12:16:35 dlg Exp $ */
/*
* Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
@@ -22,6 +22,8 @@
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
+#include <sys/proc.h>
+#include <sys/rwlock.h>
#include <machine/bus.h>
@@ -37,6 +39,7 @@
#ifdef ARC_DEBUG
#define ARC_D_INIT (1<<0)
#define ARC_D_RW (1<<1)
+#define ARC_D_DB (1<<2)
int arcdebug = 0;
@@ -112,10 +115,9 @@ static const struct pci_matchid arc_devices[] = {
#define ARC_REG_MSGBUF_LEN 1024
#define ARC_REG_IOC_WBUF_LEN 0x0e00
#define ARC_REG_IOC_WBUF 0x0e04
-#define ARC_REG_IOC_WBUF_MAXLEN 124
#define ARC_REG_IOC_RBUF_LEN 0x0f00
#define ARC_REG_IOC_RBUF 0x0f04
-#define ARC_REG_IOC_RBUF_MAXLEN 124
+#define ARC_REG_IOC_RWBUF_MAXLEN 124 /* for both RBUF and WBUF */
struct arc_msg_firmware_info {
u_int32_t signature;
@@ -212,6 +214,9 @@ struct arc_softc {
struct arc_dmamem *sc_requests;
struct arc_ccb *sc_ccbs;
struct arc_ccb_list sc_ccb_free;
+
+ struct rwlock sc_lock;
+ volatile int sc_talking;
};
#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
@@ -295,6 +300,12 @@ int arc_map_pci_resources(struct arc_softc *,
struct pci_attach_args *);
int arc_query_firmware(struct arc_softc *);
+/* these lock access to the read and write regions. */
+void arc_lock(struct arc_softc *);
+void arc_unlock(struct arc_softc *);
+void arc_wait(struct arc_softc *);
+int arc_msgbuf(struct arc_softc *, void *, size_t,
+ void *, size_t);
int
arc_match(struct device *parent, void *match, void *aux)
@@ -309,6 +320,9 @@ arc_attach(struct device *parent, struct device *self, void *aux)
struct arc_softc *sc = (struct arc_softc *)self;
struct pci_attach_args *pa = aux;
+ sc->sc_talking = 0;
+ rw_init(&sc->sc_lock, "arcmsg");
+
if (arc_map_pci_resources(sc, pa) != 0) {
/* error message printed by arc_map_pci_resources */
return;
@@ -334,7 +348,8 @@ arc_attach(struct device *parent, struct device *self, void *aux)
config_found(self, &sc->sc_link, scsiprint);
/* XXX enable interrupts */
- arc_write(sc, ARC_REG_INTRMASK, ~ARC_REG_INTRMASK_POSTQUEUE);
+ arc_write(sc, ARC_REG_INTRMASK,
+ ~(ARC_REG_INTRMASK_POSTQUEUE|ARC_REG_INTRSTAT_DOORBELL));
return;
}
@@ -367,6 +382,22 @@ arc_intr(void *arg)
return (0);
arc_write(sc, ARC_REG_INTRSTAT, intrstat);
+ if (intrstat & ARC_REG_INTRSTAT_DOORBELL) {
+ if (sc->sc_talking) {
+ /* if an ioctl is talking, wake it up */
+ arc_write(sc, ARC_REG_INTRMASK,
+ ~ARC_REG_INTRMASK_POSTQUEUE);
+ wakeup(sc);
+ } else {
+ /* otherwise drop it */
+ reg = arc_read(sc, ARC_REG_OUTB_DOORBELL);
+ arc_write(sc, ARC_REG_OUTB_DOORBELL, reg);
+ if (reg & ARC_REG_OUTB_DOORBELL_WRITE_OK)
+ arc_write(sc, ARC_REG_INB_DOORBELL,
+ ARC_REG_INB_DOORBELL_READ_OK);
+ }
+ }
+
while ((reg = arc_pop(sc)) != 0xffffffff) {
cmd = (struct arc_io_cmd *)(kva +
((reg << ARC_REG_REPLY_QUEUE_ADDR_SHIFT) -
@@ -709,6 +740,125 @@ arc_query_firmware(struct arc_softc *sc)
return (0);
}
+int
+arc_msgbuf(struct arc_softc *sc, void *wptr, size_t wlen, void *rptr,
+ size_t rlen)
+{
+ u_int8_t rwbuf[ARC_REG_IOC_RWBUF_MAXLEN];
+ u_int8_t *wbuf = wptr, *rbuf = rptr;
+ int wdone = 0, rdone = 0, i;
+ u_int32_t reg, rwlen;
+
+ DNPRINTF(ARC_D_DB, "%s: arc_msgbuf wlen: %d rlen: %d\n", DEVNAME(sc),
+ wlen, rlen);
+
+ if (arc_read(sc, ARC_REG_OUTB_DOORBELL) != 0)
+ return (EBUSY);
+
+ reg = ARC_REG_OUTB_DOORBELL_READ_OK;
+
+ do {
+ if ((reg & ARC_REG_OUTB_DOORBELL_READ_OK) && wdone < wlen) {
+ bzero(rwbuf, sizeof(rwbuf));
+ rwlen = (wlen - wdone) % sizeof(rwbuf);
+ bcopy(&wbuf[wdone], rwbuf, rwlen);
+ wdone += rwlen;
+
+#ifdef ARC_DEBUG
+ if (arcdebug & ARC_D_DB) {
+ printf("%s: write:", DEVNAME(sc));
+ for (i = 0; i < rwlen; i++)
+ printf(" 0x%02x", rwbuf[i]);
+ printf("\n");
+ }
+#endif
+
+ /* copy the chunk to the hw */
+ arc_write(sc, ARC_REG_IOC_WBUF_LEN, rwlen);
+ arc_write_region(sc, ARC_REG_IOC_WBUF, rwbuf,
+ sizeof(rwbuf));
+
+ /* say we have a buffer for the hw */
+ arc_write(sc, ARC_REG_INB_DOORBELL,
+ ARC_REG_INB_DOORBELL_WRITE_OK);
+ }
+
+ while ((reg = arc_read(sc, ARC_REG_OUTB_DOORBELL)) == 0)
+ arc_wait(sc);
+ arc_write(sc, ARC_REG_OUTB_DOORBELL, reg);
+
+ DNPRINTF(ARC_D_DB, "%s: reg: 0x%08x\n", DEVNAME(sc), reg);
+
+ if ((reg & ARC_REG_OUTB_DOORBELL_WRITE_OK) && rdone < rlen) {
+ rwlen = arc_read(sc, ARC_REG_IOC_RBUF_LEN);
+ arc_read_region(sc, ARC_REG_IOC_RBUF, rwbuf,
+ sizeof(rwbuf));
+
+ arc_write(sc, ARC_REG_INB_DOORBELL,
+ ARC_REG_INB_DOORBELL_READ_OK);
+
+#ifdef ARC_DEBUG
+ printf("%s: len: %d+%d=%d/%d\n", DEVNAME(sc),
+ rwlen, rdone, rwlen + rdone, rlen);
+ if (arcdebug & ARC_D_DB) {
+ printf("%s: read:", DEVNAME(sc));
+ for (i = 0; i < rwlen; i++)
+ printf(" 0x%02x", rwbuf[i]);
+ printf("\n");
+ }
+#endif
+
+ if ((rdone + rwlen) > rlen)
+ return (EIO);
+
+ bcopy(rwbuf, &rbuf[rdone], rwlen);
+ rdone += rwlen;
+
+ }
+
+ } while (rdone != rlen);
+
+ return (0);
+}
+
+void
+arc_lock(struct arc_softc *sc)
+{
+ int s;
+
+ rw_enter_write(&sc->sc_lock);
+ s = splbio();
+ arc_write(sc, ARC_REG_INTRMASK, ~ARC_REG_INTRMASK_POSTQUEUE);
+ sc->sc_talking = 1;
+ splx(s);
+}
+
+void
+arc_unlock(struct arc_softc *sc)
+{
+ int s;
+
+ s = splbio();
+ sc->sc_talking = 0;
+ arc_write(sc, ARC_REG_INTRMASK,
+ ~(ARC_REG_INTRMASK_POSTQUEUE|ARC_REG_INTRSTAT_DOORBELL));
+ splx(s);
+ rw_exit_write(&sc->sc_lock);
+}
+
+void
+arc_wait(struct arc_softc *sc)
+{
+ int s;
+
+ s = splbio();
+ arc_write(sc, ARC_REG_INTRMASK,
+ ~(ARC_REG_INTRMASK_POSTQUEUE|ARC_REG_INTRSTAT_DOORBELL));
+ if (tsleep(sc, PWAIT, "arcdb", hz) == EWOULDBLOCK)
+ arc_write(sc, ARC_REG_INTRMASK, ~ARC_REG_INTRMASK_POSTQUEUE);
+ splx(s);
+}
+
u_int32_t
arc_read(struct arc_softc *sc, bus_size_t r)
{