Handle dma mappings with more than one segment for rpi sdhci.
The driver inherently does dma in 512 byte chunks, but it's possible that such a buffer can span two physically discontiguous pages (such as when a userland program does IO on the raw /dev/mmcsdN devices). Now the driver can handle a buffer that's split across two pages. It could in theory handle any number of segments now, but as long as IO is being done in 512 byte blocks it will never need more than two.
This commit is contained in:
parent
740a7a7597
commit
244fe94f3b
@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$");
|
||||
#define BCM2835_DEFAULT_SDHCI_FREQ 50
|
||||
|
||||
#define BCM_SDHCI_BUFFER_SIZE 512
|
||||
#define NUM_DMA_SEGS 2
|
||||
|
||||
#ifdef DEBUG
|
||||
#define dprintf(fmt, args...) do { printf("%s(): ", __func__); \
|
||||
@ -97,10 +98,6 @@ TUNABLE_INT("hw.bcm2835.sdhci.min_freq", &bcm2835_sdhci_min_freq);
|
||||
TUNABLE_INT("hw.bcm2835.sdhci.hs", &bcm2835_sdhci_hs);
|
||||
TUNABLE_INT("hw.bcm2835.sdhci.pio_mode", &bcm2835_sdhci_pio_mode);
|
||||
|
||||
struct bcm_sdhci_dmamap_arg {
|
||||
bus_addr_t sc_dma_busaddr;
|
||||
};
|
||||
|
||||
struct bcm_sdhci_softc {
|
||||
device_t sc_dev;
|
||||
struct mtx sc_mtx;
|
||||
@ -125,9 +122,10 @@ struct bcm_sdhci_softc {
|
||||
bus_dmamap_t sc_dma_map;
|
||||
vm_paddr_t sc_sdhci_buffer_phys;
|
||||
uint32_t cmd_and_mode;
|
||||
bus_addr_t dmamap_seg_addrs[1];
|
||||
bus_size_t dmamap_seg_sizes[1];
|
||||
bus_addr_t dmamap_seg_addrs[NUM_DMA_SEGS];
|
||||
bus_size_t dmamap_seg_sizes[NUM_DMA_SEGS];
|
||||
int dmamap_seg_count;
|
||||
int dmamap_seg_index;
|
||||
int dmamap_status;
|
||||
};
|
||||
|
||||
@ -255,7 +253,7 @@ bcm_sdhci_attach(device_t dev)
|
||||
err = bus_dma_tag_create(bus_get_dma_tag(dev),
|
||||
1, 0, BUS_SPACE_MAXADDR_32BIT,
|
||||
BUS_SPACE_MAXADDR, NULL, NULL,
|
||||
BCM_SDHCI_BUFFER_SIZE, 1, BCM_SDHCI_BUFFER_SIZE,
|
||||
BCM_SDHCI_BUFFER_SIZE, NUM_DMA_SEGS, BCM_SDHCI_BUFFER_SIZE,
|
||||
BUS_DMA_ALLOCNOW, NULL, NULL,
|
||||
&sc->sc_dma_tag);
|
||||
|
||||
@ -427,19 +425,80 @@ bcm_sdhci_min_freq(device_t dev, struct sdhci_slot *slot)
|
||||
return bcm2835_sdhci_min_freq;
|
||||
}
|
||||
|
||||
static void
|
||||
bcm_sdhci_start_dma_seg(struct bcm_sdhci_softc *sc)
|
||||
{
|
||||
struct sdhci_slot *slot;
|
||||
vm_paddr_t pdst, psrc;
|
||||
int err, idx, len, sync_op;
|
||||
|
||||
slot = &sc->sc_slot;
|
||||
idx = sc->dmamap_seg_index++;
|
||||
len = sc->dmamap_seg_sizes[idx];
|
||||
slot->offset += len;
|
||||
|
||||
if (slot->curcmd->data->flags & MMC_DATA_READ) {
|
||||
bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
|
||||
BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
|
||||
bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
|
||||
BCM_DMA_INC_ADDR,
|
||||
(len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
|
||||
psrc = sc->sc_sdhci_buffer_phys;
|
||||
pdst = sc->dmamap_seg_addrs[idx];
|
||||
sync_op = BUS_DMASYNC_PREREAD;
|
||||
} else {
|
||||
bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
|
||||
BCM_DMA_INC_ADDR,
|
||||
(len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
|
||||
bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
|
||||
BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
|
||||
psrc = sc->dmamap_seg_addrs[idx];
|
||||
pdst = sc->sc_sdhci_buffer_phys;
|
||||
sync_op = BUS_DMASYNC_PREWRITE;
|
||||
}
|
||||
|
||||
/*
|
||||
* When starting a new DMA operation do the busdma sync operation, and
|
||||
* disable SDCHI data interrrupts because we'll be driven by DMA
|
||||
* interrupts (or SDHCI error interrupts) until the IO is done.
|
||||
*/
|
||||
if (idx == 0) {
|
||||
bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op);
|
||||
slot->intmask &= ~(SDHCI_INT_DATA_AVAIL |
|
||||
SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END);
|
||||
bcm_sdhci_write_4(sc->sc_dev, &sc->sc_slot, SDHCI_SIGNAL_ENABLE,
|
||||
slot->intmask);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the DMA transfer. Only programming errors (like failing to
|
||||
* allocate a channel) cause a non-zero return from bcm_dma_start().
|
||||
*/
|
||||
err = bcm_dma_start(sc->sc_dma_ch, psrc, pdst, len);
|
||||
KASSERT((err == 0), ("bcm2835_sdhci: failed DMA start"));
|
||||
}
|
||||
|
||||
static void
|
||||
bcm_sdhci_dma_intr(int ch, void *arg)
|
||||
{
|
||||
struct bcm_sdhci_softc *sc = (struct bcm_sdhci_softc *)arg;
|
||||
struct sdhci_slot *slot = &sc->sc_slot;
|
||||
uint32_t reg, mask;
|
||||
vm_paddr_t pdst, psrc;
|
||||
size_t len;
|
||||
int left, sync_op;
|
||||
|
||||
mtx_lock(&slot->mtx);
|
||||
|
||||
len = bcm_dma_length(sc->sc_dma_ch);
|
||||
/*
|
||||
* If there are more segments for the current dma, start the next one.
|
||||
* Otherwise unload the dma map and decide what to do next based on the
|
||||
* status of the sdhci controller and whether there's more data left.
|
||||
*/
|
||||
if (sc->dmamap_seg_index < sc->dmamap_seg_count) {
|
||||
bcm_sdhci_start_dma_seg(sc);
|
||||
mtx_unlock(&slot->mtx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot->curcmd->data->flags & MMC_DATA_READ) {
|
||||
sync_op = BUS_DMASYNC_POSTREAD;
|
||||
mask = SDHCI_INT_DATA_AVAIL;
|
||||
@ -450,8 +509,8 @@ bcm_sdhci_dma_intr(int ch, void *arg)
|
||||
bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op);
|
||||
bus_dmamap_unload(sc->sc_dma_tag, sc->sc_dma_map);
|
||||
|
||||
slot->offset += len;
|
||||
sc->sc_dma_inuse = 0;
|
||||
sc->dmamap_seg_count = 0;
|
||||
sc->dmamap_seg_index = 0;
|
||||
|
||||
left = min(BCM_SDHCI_BUFFER_SIZE,
|
||||
slot->curcmd->data->len - slot->offset);
|
||||
@ -475,7 +534,6 @@ bcm_sdhci_dma_intr(int ch, void *arg)
|
||||
else {
|
||||
/* already available? */
|
||||
if (reg & mask) {
|
||||
sc->sc_dma_inuse = 1;
|
||||
|
||||
/* ACK for DATA_AVAIL or SPACE_AVAIL */
|
||||
bcm_sdhci_write_4(slot->bus, slot,
|
||||
@ -489,24 +547,7 @@ bcm_sdhci_dma_intr(int ch, void *arg)
|
||||
slot->curcmd->error = MMC_ERR_NO_MEMORY;
|
||||
sdhci_finish_data(slot);
|
||||
} else {
|
||||
if (slot->curcmd->data->flags & MMC_DATA_READ) {
|
||||
psrc = sc->sc_sdhci_buffer_phys;
|
||||
pdst = sc->dmamap_seg_addrs[0];
|
||||
sync_op = BUS_DMASYNC_PREREAD;
|
||||
} else {
|
||||
psrc = sc->dmamap_seg_addrs[0];
|
||||
pdst = sc->sc_sdhci_buffer_phys;
|
||||
sync_op = BUS_DMASYNC_PREWRITE;
|
||||
}
|
||||
bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
|
||||
sync_op);
|
||||
if (bcm_dma_start(sc->sc_dma_ch, psrc, pdst,
|
||||
left)) {
|
||||
device_printf(sc->sc_dev,
|
||||
"failed DMA start\n");
|
||||
slot->curcmd->error = MMC_ERR_FAILED;
|
||||
sdhci_finish_data(slot);
|
||||
}
|
||||
bcm_sdhci_start_dma_seg(sc);
|
||||
}
|
||||
} else {
|
||||
/* wait for next data by INT */
|
||||
@ -528,7 +569,7 @@ bcm_sdhci_read_dma(device_t dev, struct sdhci_slot *slot)
|
||||
struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
|
||||
size_t left;
|
||||
|
||||
if (sc->sc_dma_inuse) {
|
||||
if (sc->dmamap_seg_count != 0) {
|
||||
device_printf(sc->sc_dev, "DMA in use\n");
|
||||
return;
|
||||
}
|
||||
@ -547,25 +588,8 @@ bcm_sdhci_read_dma(device_t dev, struct sdhci_slot *slot)
|
||||
return;
|
||||
}
|
||||
|
||||
bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
|
||||
BUS_DMASYNC_PREREAD);
|
||||
|
||||
bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
|
||||
BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
|
||||
bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
|
||||
BCM_DMA_INC_ADDR,
|
||||
(left & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
|
||||
|
||||
/* Disable INT */
|
||||
slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END);
|
||||
bcm_sdhci_write_4(dev, slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
|
||||
|
||||
sc->sc_dma_inuse = 1;
|
||||
|
||||
/* DMA start */
|
||||
if (bcm_dma_start(sc->sc_dma_ch, sc->sc_sdhci_buffer_phys,
|
||||
sc->dmamap_seg_addrs[0], left) != 0)
|
||||
device_printf(sc->sc_dev, "failed DMA start\n");
|
||||
bcm_sdhci_start_dma_seg(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -574,7 +598,7 @@ bcm_sdhci_write_dma(device_t dev, struct sdhci_slot *slot)
|
||||
struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
|
||||
size_t left;
|
||||
|
||||
if (sc->sc_dma_inuse) {
|
||||
if (sc->dmamap_seg_count != 0) {
|
||||
device_printf(sc->sc_dev, "DMA in use\n");
|
||||
return;
|
||||
}
|
||||
@ -593,25 +617,8 @@ bcm_sdhci_write_dma(device_t dev, struct sdhci_slot *slot)
|
||||
return;
|
||||
}
|
||||
|
||||
bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
|
||||
BCM_DMA_INC_ADDR,
|
||||
(left & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
|
||||
bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
|
||||
BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
|
||||
|
||||
bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
|
||||
BUS_DMASYNC_PREWRITE);
|
||||
|
||||
/* Disable INT */
|
||||
slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END);
|
||||
bcm_sdhci_write_4(dev, slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
|
||||
|
||||
sc->sc_dma_inuse = 1;
|
||||
|
||||
/* DMA start */
|
||||
if (bcm_dma_start(sc->sc_dma_ch, sc->dmamap_seg_addrs[0],
|
||||
sc->sc_sdhci_buffer_phys, left) != 0)
|
||||
device_printf(sc->sc_dev, "failed DMA start\n");
|
||||
bcm_sdhci_start_dma_seg(sc);
|
||||
}
|
||||
|
||||
static int
|
||||
|
Loading…
x
Reference in New Issue
Block a user