-/* $OpenBSD: ufshci.c,v 1.9 2024/01/06 17:47:43 mglocker Exp $ */
+/* $OpenBSD: ufshci.c,v 1.10 2024/05/09 08:02:59 mglocker Exp $ */
/*
* Copyright (c) 2022 Marcus Glocker <mglocker@openbsd.org>
void ufshci_dmamem_free(struct ufshci_softc *,
struct ufshci_dmamem *);
int ufshci_init(struct ufshci_softc *);
-int ufshci_doorbell_get_free(struct ufshci_softc *);
int ufshci_doorbell_read(struct ufshci_softc *);
+void ufshci_doorbell_write(struct ufshci_softc *, int);
int ufshci_doorbell_poll(struct ufshci_softc *, int);
-void ufshci_doorbell_set(struct ufshci_softc *, int);
uint8_t ufshci_get_taskid(struct ufshci_softc *);
-int ufshci_utr_cmd_nop(struct ufshci_softc *);
+int ufshci_utr_cmd_nop(struct ufshci_softc *,
+ struct ufshci_ccb *, struct scsi_xfer *);
int ufshci_utr_cmd_lun(struct ufshci_softc *,
struct ufshci_ccb *, struct scsi_xfer *);
int ufshci_utr_cmd_inquiry(struct ufshci_softc *,
if (status & UFSHCI_REG_IS_UTRCS) {
DPRINTF("%s: UTRCS interrupt\n", __func__);
- ufshci_xfer_complete(sc);
-
/* Reset Interrupt Aggregation Counter and Timer. */
UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
UFSHCI_REG_UTRIACR_IAEN | UFSHCI_REG_UTRIACR_CTR);
+ ufshci_xfer_complete(sc);
+
handled = 1;
}
sc->sc_hcmid = UFSHCI_READ_4(sc, UFSHCI_REG_HCMID);
sc->sc_nutmrs = UFSHCI_REG_CAP_NUTMRS(sc->sc_cap) + 1;
sc->sc_rtt = UFSHCI_REG_CAP_RTT(sc->sc_cap) + 1;
- sc->sc_nutrs = UFSHCI_REG_CAP_NUTRS(sc->sc_cap) + 1;
+ //sc->sc_nutrs = UFSHCI_REG_CAP_NUTRS(sc->sc_cap) + 1;
+ /* XXX: Using more than one slot currently causes OCS errors */
+ sc->sc_nutrs = 1;
#if UFSHCI_DEBUG
printf("Capabilities (0x%08x):\n", sc->sc_cap);
}
int
-ufshci_doorbell_get_free(struct ufshci_softc *sc)
+ufshci_doorbell_read(struct ufshci_softc *sc)
{
- int slot;
uint32_t reg;
reg = UFSHCI_READ_4(sc, UFSHCI_REG_UTRLDBR);
- for (slot = 0; slot < sc->sc_nutrs; slot++) {
- if ((reg & (1 << slot)) == 0)
- return slot;
- }
-
- return -1;
+ return reg;
}
-int
-ufshci_doorbell_read(struct ufshci_softc *sc)
+void
+ufshci_doorbell_write(struct ufshci_softc *sc, int slot)
{
uint32_t reg;
- reg = UFSHCI_READ_4(sc, UFSHCI_REG_UTRLDBR);
+ reg = (1 << slot);
- return reg;
+ UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLDBR, reg);
}
int
return 0;
}
-void
-ufshci_doorbell_set(struct ufshci_softc *sc, int slot)
-{
- uint32_t reg;
-
- reg = (1 << slot);
-
- UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLDBR, reg);
-}
-
uint8_t
ufshci_get_taskid(struct ufshci_softc *sc)
{
}
int
-ufshci_utr_cmd_nop(struct ufshci_softc *sc)
+ufshci_utr_cmd_nop(struct ufshci_softc *sc, struct ufshci_ccb *ccb,
+ struct scsi_xfer *xs)
{
int slot, off, len;
uint64_t dva;
struct ufshci_ucd *ucd;
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
- slot = ufshci_doorbell_get_free(sc);
+ slot = ccb->ccb_slot;
utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
memset(utrd, 0, sizeof(*utrd));
DPRINTF("%s: slot=%d\n", __func__, slot);
utrd->dw0 |= UFSHCI_UTRD_DW0_DD_NO;
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */
- utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT;
+ utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG;
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */
utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV;
UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
- ufshci_doorbell_set(sc, slot);
+ ccb->ccb_status = CCB_STATUS_INPROGRESS;
+ ufshci_doorbell_write(sc, slot);
return 0;
}
bus_dmamap_t dmap = ccb->ccb_dmamap;
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
- slot = ufshci_doorbell_get_free(sc);
+ slot = ccb->ccb_slot;
utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
memset(utrd, 0, sizeof(*utrd));
DPRINTF("%s: slot=%d\n", __func__, slot);
UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
- ufshci_doorbell_set(sc, slot);
+ ccb->ccb_status = CCB_STATUS_INPROGRESS;
+ ufshci_doorbell_write(sc, slot);
return 0;
}
bus_dmamap_t dmap = ccb->ccb_dmamap;
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
- slot = ufshci_doorbell_get_free(sc);
+ slot = ccb->ccb_slot;
utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
memset(utrd, 0, sizeof(*utrd));
DPRINTF("%s: slot=%d\n", __func__, slot);
}
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
- ufshci_doorbell_set(sc, slot);
+ ccb->ccb_status = CCB_STATUS_INPROGRESS;
+ ufshci_doorbell_write(sc, slot);
return slot;
}
bus_dmamap_t dmap = ccb->ccb_dmamap;
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
- slot = ufshci_doorbell_get_free(sc);
+ slot = ccb->ccb_slot;
utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
memset(utrd, 0, sizeof(*utrd));
DPRINTF("%s: slot=%d\n", __func__, slot);
}
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
- ufshci_doorbell_set(sc, slot);
+ ccb->ccb_status = CCB_STATUS_INPROGRESS;
+ ufshci_doorbell_write(sc, slot);
return slot;
}
bus_dmamap_t dmap = ccb->ccb_dmamap;
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
- slot = ufshci_doorbell_get_free(sc);
+ slot = ccb->ccb_slot;
utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
memset(utrd, 0, sizeof(*utrd));
DPRINTF("%s: slot=%d\n", __func__, slot);
}
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
- ufshci_doorbell_set(sc, slot);
+ ccb->ccb_status = CCB_STATUS_INPROGRESS;
+ ufshci_doorbell_write(sc, slot);
return slot;
}
bus_dmamap_t dmap = ccb->ccb_dmamap;
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
- slot = ufshci_doorbell_get_free(sc);
+ slot = ccb->ccb_slot;
utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
memset(utrd, 0, sizeof(*utrd));
DPRINTF("%s: slot=%d\n", __func__, slot);
}
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
- ufshci_doorbell_set(sc, slot);
+ ccb->ccb_status = CCB_STATUS_INPROGRESS;
+ ufshci_doorbell_write(sc, slot);
return slot;
}
struct ufshci_ucd *ucd;
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
- slot = ufshci_doorbell_get_free(sc);
+ slot = ccb->ccb_slot;
utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
memset(utrd, 0, sizeof(*utrd));
DPRINTF("%s: slot=%d\n", __func__, slot);
}
/* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
- ufshci_doorbell_set(sc, slot);
+ ccb->ccb_status = CCB_STATUS_INPROGRESS;
+ ufshci_doorbell_write(sc, slot);
return slot;
}
uint32_t reg;
int i;
- reg = ufshci_doorbell_read(sc);
+ /* Wait for all commands to complete. */
+ while ((reg = ufshci_doorbell_read(sc))) {
+ DPRINTF("%s: doorbell reg=0x%x\n", __func__, reg);
+ if (reg == 0)
+ break;
+ }
for (i = 0; i < sc->sc_nutrs; i++) {
ccb = &sc->sc_ccbs[i];
- if (ccb->ccb_slot == -1)
- /* CCB isn't used. */
- continue;
-
- if (reg & (1 << ccb->ccb_slot))
- /* Transfer is still in progress. */
+ /* Skip unused CCBs. */
+ if (ccb->ccb_status != CCB_STATUS_INPROGRESS)
continue;
- /* Transfer has completed. */
if (ccb->ccb_done == NULL)
- panic("ccb_done not defined");
- ccb->ccb_done(sc, ccb);
+ panic("ccb done wasn't defined");
+
+ /* 7.2.3: Clear completion notification 3b) */
+ UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLCNR, (1U << i));
+
+ /* 7.2.3: Mark software slot for re-use 3c) */
+ ccb->ccb_status = CCB_STATUS_READY2FREE;
+
+ DPRINTF("slot %d completed\n", i);
+ }
+
+ /*
+ * Complete the CCB, which will re-schedule new transfers if any are
+ * pending.
+ */
+ for (i = 0; i < sc->sc_nutrs; i++) {
+ ccb = &sc->sc_ccbs[i];
+
+ /* 7.2.3: Process the transfer by higher OS layer 3a) */
+ if (ccb->ccb_status == CCB_STATUS_READY2FREE)
+ ccb->ccb_done(sc, ccb);
}
return 0;
goto free_maps;
ccb->ccb_cookie = NULL;
- ccb->ccb_slot = -1;
+ ccb->ccb_slot = i;
SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_list, ccb, ccb_entry);
}
ccb->ccb_done = ufshci_scsi_io_done;
/* Response length should be UPIU_SCSI_RSP_INQUIRY_SIZE. */
- ccb->ccb_slot = ufshci_utr_cmd_inquiry(sc, ccb, xs);
- if (ccb->ccb_slot == -1)
+ error = ufshci_utr_cmd_inquiry(sc, ccb, xs);
+ if (error == -1)
goto error2;
if (ISSET(xs->flags, SCSI_POLL)) {
error2:
bus_dmamap_unload(sc->sc_dmat, dmap);
ccb->ccb_cookie = NULL;
- ccb->ccb_slot = -1;
ccb->ccb_done = NULL;
error1:
xs->error = XS_DRIVER_STUFFUP;
ccb->ccb_done = ufshci_scsi_io_done;
/* Response length should be UPIU_SCSI_RSP_CAPACITY16_SIZE. */
- ccb->ccb_slot = ufshci_utr_cmd_capacity16(sc, ccb, xs);
- if (ccb->ccb_slot == -1)
+ error = ufshci_utr_cmd_capacity16(sc, ccb, xs);
+ if (error == -1)
goto error2;
if (ISSET(xs->flags, SCSI_POLL)) {
error2:
bus_dmamap_unload(sc->sc_dmat, dmap);
ccb->ccb_cookie = NULL;
- ccb->ccb_slot = -1;
ccb->ccb_done = NULL;
error1:
xs->error = XS_DRIVER_STUFFUP;
ccb->ccb_done = ufshci_scsi_io_done;
/* Response length should be UPIU_SCSI_RSP_CAPACITY_SIZE */
- ccb->ccb_slot = ufshci_utr_cmd_capacity(sc, ccb, xs);
- if (ccb->ccb_slot == -1)
+ error = ufshci_utr_cmd_capacity(sc, ccb, xs);
+ if (error == -1)
goto error2;
if (ISSET(xs->flags, SCSI_POLL)) {
error2:
bus_dmamap_unload(sc->sc_dmat, dmap);
ccb->ccb_cookie = NULL;
- ccb->ccb_slot = -1;
ccb->ccb_done = NULL;
error1:
xs->error = XS_DRIVER_STUFFUP;
struct ufshci_ccb *ccb = xs->io;
uint64_t lba;
uint32_t blocks;
+ int error;
/* lba = 0, blocks = 0: Synchronize all logical blocks. */
lba = 0; blocks = 0;
ccb->ccb_cookie = xs;
ccb->ccb_done = ufshci_scsi_done;
- ccb->ccb_slot = ufshci_utr_cmd_sync(sc, ccb, xs, (uint32_t)lba,
+ error = ufshci_utr_cmd_sync(sc, ccb, xs, (uint32_t)lba,
(uint16_t)blocks);
- if (ccb->ccb_slot == -1)
+ if (error == -1)
goto error;
if (ISSET(xs->flags, SCSI_POLL)) {
error:
ccb->ccb_cookie = NULL;
- ccb->ccb_slot = -1;
ccb->ccb_done = NULL;
xs->error = XS_DRIVER_STUFFUP;
ccb->ccb_done = ufshci_scsi_io_done;
if (dir == SCSI_DATA_IN)
- ccb->ccb_slot = ufshci_utr_cmd_io(sc, ccb, xs, SCSI_DATA_IN);
+ error = ufshci_utr_cmd_io(sc, ccb, xs, SCSI_DATA_IN);
else
- ccb->ccb_slot = ufshci_utr_cmd_io(sc, ccb, xs, SCSI_DATA_OUT);
-
- if (ccb->ccb_slot == -1)
+ error = ufshci_utr_cmd_io(sc, ccb, xs, SCSI_DATA_OUT);
+ if (error == -1)
goto error2;
if (ISSET(xs->flags, SCSI_POLL)) {
error2:
bus_dmamap_unload(sc->sc_dmat, dmap);
ccb->ccb_cookie = NULL;
- ccb->ccb_slot = -1;
ccb->ccb_done = NULL;
error1:
xs->error = XS_DRIVER_STUFFUP;
bus_dmamap_unload(sc->sc_dmat, dmap);
ccb->ccb_cookie = NULL;
- ccb->ccb_slot = -1;
+ ccb->ccb_status = CCB_STATUS_FREE;
ccb->ccb_done = NULL;
xs->error = XS_NOERROR;
struct scsi_xfer *xs = ccb->ccb_cookie;
ccb->ccb_cookie = NULL;
- ccb->ccb_slot = -1;
+ ccb->ccb_status = CCB_STATUS_FREE;
ccb->ccb_done = NULL;
xs->error = XS_NOERROR;