Contribution from Petri Laakso:
- DMA driver stub code replaced with working code. - Add support to multi block DMA in ssp driver.
This commit is contained in:
parent
882558bd95
commit
aca15765bd
@ -1,4 +1,4 @@
|
|||||||
/* $Id: imx23_apbdma.c,v 1.2 2012/12/16 19:40:00 jkunz Exp $ */
|
/* $Id: imx23_apbdma.c,v 1.3 2013/03/03 10:33:56 jkunz Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
||||||
@ -30,53 +30,25 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
#include <sys/types.h>
|
||||||
#include <sys/bus.h>
|
#include <sys/bus.h>
|
||||||
#include <sys/device.h>
|
#include <sys/device.h>
|
||||||
#include <sys/errno.h>
|
#include <sys/errno.h>
|
||||||
|
#include <sys/mutex.h>
|
||||||
#include <sys/kmem.h>
|
#include <sys/kmem.h>
|
||||||
#include <sys/queue.h>
|
|
||||||
#include <sys/systm.h>
|
#include <sys/systm.h>
|
||||||
|
|
||||||
|
#include <arm/imx/imx23_apbdma.h>
|
||||||
#include <arm/imx/imx23_apbdmareg.h>
|
#include <arm/imx/imx23_apbdmareg.h>
|
||||||
|
#include <arm/imx/imx23_apbdmavar.h>
|
||||||
#include <arm/imx/imx23_apbhdmareg.h>
|
#include <arm/imx/imx23_apbhdmareg.h>
|
||||||
#include <arm/imx/imx23_apbxdmareg.h>
|
#include <arm/imx/imx23_apbxdmareg.h>
|
||||||
#include <arm/imx/imx23_apbdma.h>
|
|
||||||
#include <arm/imx/imx23var.h>
|
#include <arm/imx/imx23var.h>
|
||||||
|
|
||||||
static int apbdma_match(device_t, cfdata_t, void *);
|
static int apbdma_match(device_t, cfdata_t, void *);
|
||||||
static void apbdma_attach(device_t, device_t, void *);
|
static void apbdma_attach(device_t, device_t, void *);
|
||||||
static int apbdma_activate(device_t, enum devact);
|
static int apbdma_activate(device_t, enum devact);
|
||||||
|
|
||||||
#define APBDMA_SOFT_RST_LOOP 455 /* At least 1 us ... */
|
|
||||||
#define DMACTRL_RD(sc, reg) \
|
|
||||||
bus_space_read_4(sc->sc_iot, sc->sc_hdl, (reg))
|
|
||||||
#define DMACTRL_WR(sc, reg, val) \
|
|
||||||
bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val))
|
|
||||||
|
|
||||||
struct apbdma_softc {
|
|
||||||
device_t sc_dev;
|
|
||||||
bus_space_tag_t sc_iot;
|
|
||||||
bus_space_handle_t sc_hdl;
|
|
||||||
bus_dma_tag_t sc_dmat;
|
|
||||||
bus_dmamap_t sc_dmamp;
|
|
||||||
struct imx23_dma_channel *sc_channel;
|
|
||||||
int n_channel;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct imx23_dma_cmd {
|
|
||||||
uint32_t next_cmd;
|
|
||||||
uint32_t cmd;
|
|
||||||
uint32_t buffer;
|
|
||||||
uint32_t pio[CMDPIOWORDS_MAX];
|
|
||||||
SIMPLEQ_ENTRY(imx23_dma_cmd) entries;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct imx23_dma_channel {
|
|
||||||
SIMPLEQ_HEAD(simplehead, imx23_dma_cmd) head;
|
|
||||||
struct simplehead *headp;
|
|
||||||
struct apbdma_softc *sc;
|
|
||||||
};
|
|
||||||
|
|
||||||
CFATTACH_DECL3_NEW(apbdma,
|
CFATTACH_DECL3_NEW(apbdma,
|
||||||
sizeof(struct apbdma_softc),
|
sizeof(struct apbdma_softc),
|
||||||
apbdma_match,
|
apbdma_match,
|
||||||
@ -88,6 +60,14 @@ CFATTACH_DECL3_NEW(apbdma,
|
|||||||
0);
|
0);
|
||||||
|
|
||||||
static void apbdma_reset(struct apbdma_softc *);
|
static void apbdma_reset(struct apbdma_softc *);
|
||||||
|
static void apbdma_init(struct apbdma_softc *);
|
||||||
|
|
||||||
|
#define DMA_RD(sc, reg) \
|
||||||
|
bus_space_read_4(sc->sc_iot, sc->sc_ioh, (reg))
|
||||||
|
#define DMA_WR(sc, reg, val) \
|
||||||
|
bus_space_write_4(sc->sc_iot, sc->sc_ioh, (reg), (val))
|
||||||
|
|
||||||
|
#define APBDMA_SOFT_RST_LOOP 455 /* At least 1 us ... */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
apbdma_match(device_t parent, cfdata_t match, void *aux)
|
apbdma_match(device_t parent, cfdata_t match, void *aux)
|
||||||
@ -108,67 +88,44 @@ apbdma_attach(device_t parent, device_t self, void *aux)
|
|||||||
{
|
{
|
||||||
struct apb_attach_args *aa = aux;
|
struct apb_attach_args *aa = aux;
|
||||||
struct apbdma_softc *sc = device_private(self);
|
struct apbdma_softc *sc = device_private(self);
|
||||||
//struct apb_softc *scp = device_private(parent);
|
struct apb_softc *sc_parent = device_private(parent);
|
||||||
|
static u_int apbdma_attached = 0;
|
||||||
|
|
||||||
// static int apbdma_attached = 0;
|
if ((strncmp(device_xname(parent), "apbh", 4) == 0) &&
|
||||||
// struct imx23_dma_channel *chan;
|
(apbdma_attached & F_AHBH_DMA))
|
||||||
// int i;
|
return;
|
||||||
int error;
|
if ((strncmp(device_xname(parent), "apbx", 4) == 0) &&
|
||||||
|
(apbdma_attached & F_AHBX_DMA))
|
||||||
// if (apbdma_attached)
|
return;
|
||||||
// return;
|
|
||||||
|
|
||||||
sc->sc_dev = self;
|
sc->sc_dev = self;
|
||||||
sc->sc_iot = aa->aa_iot;
|
sc->sc_iot = aa->aa_iot;
|
||||||
sc->sc_dmat = aa->aa_dmat;
|
sc->sc_dmat = aa->aa_dmat;
|
||||||
|
|
||||||
/*
|
|
||||||
* Parent bus softc has a pointer to DMA controller device_t for
|
|
||||||
* specific bus. As different busses need different instances of the
|
|
||||||
* DMA driver. The apb_softc.dmac is set up here. Now device drivers
|
|
||||||
* which use DMA can pass apb_softc.dmac from their parent to apbdma
|
|
||||||
* functions.
|
|
||||||
*/
|
|
||||||
if (bus_space_map(sc->sc_iot,
|
if (bus_space_map(sc->sc_iot,
|
||||||
aa->aa_addr, aa->aa_size, 0, &(sc->sc_hdl))) {
|
aa->aa_addr, aa->aa_size, 0, &sc->sc_ioh)) {
|
||||||
aprint_error_dev(sc->sc_dev, "unable to map bus space\n");
|
aprint_error_dev(sc->sc_dev, "unable to map bus space\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1,
|
if (strncmp(device_xname(parent), "apbh", 4) == 0)
|
||||||
PAGE_SIZE, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamp);
|
sc->flags = F_AHBH_DMA;
|
||||||
if (error) {
|
|
||||||
aprint_error_dev(sc->sc_dev,
|
|
||||||
"couldn't create dma map. (error=%d)\n", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#ifdef notyet
|
|
||||||
if (aa->aa_addr == HW_APBHDMA_BASE && aa->aa_size == HW_APBHDMA_SIZE) {
|
|
||||||
sc->sc_channel = kmem_alloc(sizeof(struct imx23_dma_channel)
|
|
||||||
* APBH_DMA_N_CHANNELS, KM_SLEEP);
|
|
||||||
sc->n_channel = APBH_DMA_N_CHANNELS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aa->aa_addr == HW_APBXDMA_BASE && aa->aa_size == HW_APBXDMA_SIZE) {
|
if (strncmp(device_xname(parent), "apbx", 4) == 0)
|
||||||
sc->sc_channel = kmem_alloc(sizeof(struct imx23_dma_channel)
|
sc->flags = F_AHBX_DMA;
|
||||||
* APBX_DMA_N_CHANNELS, KM_SLEEP);
|
|
||||||
sc->n_channel = APBX_DMA_N_CHANNELS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sc->sc_channel == NULL) {
|
|
||||||
aprint_error_dev(sc->sc_dev, "unable to allocate memory for"
|
|
||||||
" DMA channel structures\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0; i < sc->n_channel; i++) {
|
|
||||||
chan = (struct imx23_dma_channel *)sc->sc_channel+i;
|
|
||||||
chan->sc = sc;
|
|
||||||
SIMPLEQ_INIT(&chan->head);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
apbdma_reset(sc);
|
apbdma_reset(sc);
|
||||||
// apbdma_attached = 1;
|
apbdma_init(sc);
|
||||||
|
|
||||||
|
if (sc->flags & F_AHBH_DMA)
|
||||||
|
apbdma_attached |= F_AHBH_DMA;
|
||||||
|
if (sc->flags & F_AHBX_DMA)
|
||||||
|
apbdma_attached |= F_AHBX_DMA;
|
||||||
|
|
||||||
|
sc_parent->dmac = self;
|
||||||
|
|
||||||
|
/* Initialize mutex to control concurrent access from the drivers. */
|
||||||
|
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH);
|
||||||
|
|
||||||
aprint_normal("\n");
|
aprint_normal("\n");
|
||||||
|
|
||||||
@ -195,85 +152,242 @@ apbdma_reset(struct apbdma_softc *sc)
|
|||||||
* Prepare for soft-reset by making sure that SFTRST is not currently
|
* Prepare for soft-reset by making sure that SFTRST is not currently
|
||||||
* asserted. Also clear CLKGATE so we can wait for its assertion below.
|
* asserted. Also clear CLKGATE so we can wait for its assertion below.
|
||||||
*/
|
*/
|
||||||
DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
|
DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
|
||||||
|
|
||||||
/* Wait at least a microsecond for SFTRST to deassert. */
|
/* Wait at least a microsecond for SFTRST to deassert. */
|
||||||
loop = 0;
|
loop = 0;
|
||||||
while ((DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
|
while ((DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
|
||||||
(loop < APBDMA_SOFT_RST_LOOP))
|
(loop < APBDMA_SOFT_RST_LOOP))
|
||||||
{
|
|
||||||
loop++;
|
loop++;
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear CLKGATE so we can wait for its assertion below. */
|
/* Clear CLKGATE so we can wait for its assertion below. */
|
||||||
DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
|
DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
|
||||||
|
|
||||||
/* Soft-reset the block. */
|
/* Soft-reset the block. */
|
||||||
DMACTRL_WR(sc, HW_APB_CTRL0_SET, HW_APB_CTRL0_SFTRST);
|
DMA_WR(sc, HW_APB_CTRL0_SET, HW_APB_CTRL0_SFTRST);
|
||||||
|
|
||||||
/* Wait until clock is in the gated state. */
|
/* Wait until clock is in the gated state. */
|
||||||
while (!(DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE));
|
while (!(DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE));
|
||||||
|
|
||||||
/* Bring block out of reset. */
|
/* Bring block out of reset. */
|
||||||
DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
|
DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
|
||||||
|
|
||||||
loop = 0;
|
loop = 0;
|
||||||
while ((DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
|
while ((DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
|
||||||
(loop < APBDMA_SOFT_RST_LOOP))
|
(loop < APBDMA_SOFT_RST_LOOP))
|
||||||
{
|
|
||||||
loop++;
|
loop++;
|
||||||
}
|
|
||||||
|
|
||||||
DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
|
|
||||||
|
|
||||||
/* Wait until clock is in the NON-gated state. */
|
DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
|
||||||
while (DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE);
|
|
||||||
|
|
||||||
return;
|
/* Wait until clock is in the NON-gated state. */
|
||||||
|
while (DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate DMA safe memory for commands.
|
* Initialize APB{H,X}DMA block.
|
||||||
*/
|
*/
|
||||||
void *
|
static void
|
||||||
apbdma_dmamem_alloc(device_t dmac, int channel, bus_size_t size)
|
apbdma_init(struct apbdma_softc *sc)
|
||||||
{
|
{
|
||||||
struct apbdma_softc *sc = device_private(dmac);
|
|
||||||
bus_dma_segment_t segs[1]; /* bus_dmamem_free needs. */
|
|
||||||
int rsegs;
|
|
||||||
int error;
|
|
||||||
void *ptr = NULL; /* bus_dmamem_unmap needs (size also) */
|
|
||||||
|
|
||||||
if (size > PAGE_SIZE)
|
if (sc->flags & F_AHBH_DMA) {
|
||||||
return NULL;
|
DMA_WR(sc, HW_APBH_CTRL0_SET, HW_APBH_CTRL0_AHB_BURST8_EN);
|
||||||
|
DMA_WR(sc, HW_APBH_CTRL0_SET, HW_APBH_CTRL0_APB_BURST4_EN);
|
||||||
error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, segs, 1,
|
}
|
||||||
&rsegs, BUS_DMA_NOWAIT);
|
return;
|
||||||
if (error)
|
}
|
||||||
goto out;
|
|
||||||
//XXX:
|
/*
|
||||||
printf("segs[0].ds_addr=%lx, segs[0].ds_len=%lx, rsegs=%d\n", segs[0].ds_addr, segs[0].ds_len, rsegs);
|
* Chain DMA commands together.
|
||||||
|
*
|
||||||
error = bus_dmamem_map(sc->sc_dmat, segs, 1, size, &ptr,
|
* Set src->next point to trg's physical DMA mapped address.
|
||||||
BUS_DMA_NOWAIT);
|
*/
|
||||||
if (error)
|
void
|
||||||
goto free;
|
apbdma_cmd_chain(apbdma_command_t src, apbdma_command_t trg, void *buf,
|
||||||
//XXX:
|
bus_dmamap_t dmap)
|
||||||
printf("segs[0].ds_addr=%lx, segs[0].ds_len=%lx, ptr=%p\n", segs[0].ds_addr, segs[0].ds_len, ptr);
|
{
|
||||||
|
int i;
|
||||||
error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, ptr, size, NULL,
|
bus_size_t daddr;
|
||||||
BUS_DMA_NOWAIT | BUS_DMA_WRITE);
|
bus_addr_t trg_offset;
|
||||||
if (error)
|
|
||||||
goto unmap;
|
trg_offset = (bus_addr_t)trg - (bus_addr_t)buf;
|
||||||
|
daddr = 0;
|
||||||
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, size,
|
|
||||||
BUS_DMASYNC_PREWRITE);
|
for (i = 0; i < dmap->dm_nsegs; i++) {
|
||||||
|
daddr += dmap->dm_segs[i].ds_len;
|
||||||
// return usable memory
|
if (trg_offset < daddr) {
|
||||||
unmap:
|
src->next = (void *)(dmap->dm_segs[i].ds_addr +
|
||||||
bus_dmamem_unmap(sc->sc_dmat, ptr, size);
|
(trg_offset - (daddr - dmap->dm_segs[i].ds_len)));
|
||||||
free:
|
break;
|
||||||
bus_dmamem_free(sc->sc_dmat, segs, 1);
|
}
|
||||||
out:
|
}
|
||||||
return NULL;
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set DMA command buffer.
|
||||||
|
*
|
||||||
|
* Set cmd->buffer point to physical DMA address at offset in DMA map.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
apbdma_cmd_buf(apbdma_command_t cmd, bus_addr_t offset, bus_dmamap_t dmap)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
bus_size_t daddr;
|
||||||
|
|
||||||
|
daddr = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < dmap->dm_nsegs; i++) {
|
||||||
|
daddr += dmap->dm_segs[i].ds_len;
|
||||||
|
if (offset < daddr) {
|
||||||
|
cmd->buffer = (void *)(dmap->dm_segs[i].ds_addr +
|
||||||
|
(offset - (daddr - dmap->dm_segs[i].ds_len)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize DMA channel.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
apbdma_chan_init(struct apbdma_softc *sc, unsigned int channel)
|
||||||
|
{
|
||||||
|
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
|
||||||
|
/* Enable CMDCMPLT_IRQ. */
|
||||||
|
DMA_WR(sc, HW_APB_CTRL1_SET, (1<<channel)<<16);
|
||||||
|
|
||||||
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set command chain for DMA channel.
|
||||||
|
*/
|
||||||
|
#define HW_APB_CHN_NXTCMDAR(base, channel) (base + (0x70 * channel))
|
||||||
|
void
|
||||||
|
apbdma_chan_set_chain(struct apbdma_softc *sc, unsigned int channel,
|
||||||
|
bus_dmamap_t dmap)
|
||||||
|
{
|
||||||
|
uint32_t reg;
|
||||||
|
|
||||||
|
if (sc->flags & F_AHBH_DMA)
|
||||||
|
reg = HW_APB_CHN_NXTCMDAR(HW_APBH_CH0_NXTCMDAR, channel);
|
||||||
|
else
|
||||||
|
reg = HW_APB_CHN_NXTCMDAR(HW_APBX_CH0_NXTCMDAR, channel);
|
||||||
|
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
DMA_WR(sc, reg, dmap->dm_segs[0].ds_addr);
|
||||||
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initiate DMA transfer.
|
||||||
|
*/
|
||||||
|
#define HW_APB_CHN_SEMA(base, channel) (base + (0x70 * channel))
|
||||||
|
void
|
||||||
|
apbdma_run(struct apbdma_softc *sc, unsigned int channel)
|
||||||
|
{
|
||||||
|
uint32_t reg;
|
||||||
|
uint8_t val;
|
||||||
|
|
||||||
|
if (sc->flags & F_AHBH_DMA) {
|
||||||
|
reg = HW_APB_CHN_SEMA(HW_APBH_CH0_SEMA, channel);
|
||||||
|
val = __SHIFTIN(1, HW_APBH_CH0_SEMA_INCREMENT_SEMA);
|
||||||
|
} else {
|
||||||
|
reg = HW_APB_CHN_SEMA(HW_APBX_CH0_SEMA, channel);
|
||||||
|
val = __SHIFTIN(1, HW_APBX_CH0_SEMA_INCREMENT_SEMA);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
DMA_WR(sc, reg, val);
|
||||||
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acknowledge command complete IRQ.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
apbdma_ack_intr(struct apbdma_softc *sc, unsigned int channel)
|
||||||
|
{
|
||||||
|
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
DMA_WR(sc, HW_APB_CTRL1_CLR, (1<<channel));
|
||||||
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acknowledge error IRQ.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
apbdma_ack_error_intr(struct apbdma_softc *sc, unsigned int channel)
|
||||||
|
{
|
||||||
|
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
DMA_WR(sc, HW_APB_CTRL2_CLR, (1<<channel));
|
||||||
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return reason for the IRQ.
|
||||||
|
*/
|
||||||
|
unsigned int
|
||||||
|
apbdma_intr_status(struct apbdma_softc *sc, unsigned int channel)
|
||||||
|
{
|
||||||
|
unsigned int reason;
|
||||||
|
|
||||||
|
reason = 0;
|
||||||
|
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
|
||||||
|
/* Check if this was command complete IRQ. */
|
||||||
|
if (DMA_RD(sc, HW_APB_CTRL1) & (1<<channel))
|
||||||
|
reason = DMA_IRQ_CMDCMPLT;
|
||||||
|
|
||||||
|
/* Check if error was set. */
|
||||||
|
if (DMA_RD(sc, HW_APB_CTRL2) & (1<<channel)) {
|
||||||
|
if (DMA_RD(sc, HW_APB_CTRL2) & (1<<channel)<<16)
|
||||||
|
reason = DMA_IRQ_BUS_ERROR;
|
||||||
|
else
|
||||||
|
reason = DMA_IRQ_TERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset DMA channel.
|
||||||
|
* Use only for devices on APBH bus.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
apbdma_chan_reset(struct apbdma_softc *sc, unsigned int channel)
|
||||||
|
{
|
||||||
|
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
|
||||||
|
DMA_WR(sc, HW_APB_CTRL0_SET,
|
||||||
|
__SHIFTIN((1<<channel), HW_APBH_CTRL0_RESET_CHANNEL));
|
||||||
|
while(DMA_RD(sc, HW_APB_CTRL0) & HW_APBH_CTRL0_RESET_CHANNEL);
|
||||||
|
|
||||||
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $Id: imx23_apbdmareg.h,v 1.1 2012/11/20 19:06:12 jkunz Exp $ */
|
/* $Id: imx23_apbdmareg.h,v 1.2 2013/03/03 10:33:56 jkunz Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
||||||
@ -50,4 +50,20 @@
|
|||||||
#define HW_APB_CTRL0_CLKGATE __BIT(30)
|
#define HW_APB_CTRL0_CLKGATE __BIT(30)
|
||||||
#define HW_APB_CTRL0_RSVD0 __BITS(29, 0)
|
#define HW_APB_CTRL0_RSVD0 __BITS(29, 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AHB to APB{H,X} Bridge Control Register 1.
|
||||||
|
*/
|
||||||
|
#define HW_APB_CTRL1 0x010
|
||||||
|
#define HW_APB_CTRL1_SET 0x014
|
||||||
|
#define HW_APB_CTRL1_CLR 0x018
|
||||||
|
#define HW_APB_CTRL1_TOG 0x01C
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AHB to APB{H,X} Bridge Control and Status Register 2.
|
||||||
|
*/
|
||||||
|
#define HW_APB_CTRL2 0x020
|
||||||
|
#define HW_APB_CTRL2_SET 0x024
|
||||||
|
#define HW_APB_CTRL2_CLR 0x028
|
||||||
|
#define HW_APB_CTRL2_TOG 0x02C
|
||||||
|
|
||||||
#endif /* !_ARM_IMX_IMX23_APBDMAREG_H_ */
|
#endif /* !_ARM_IMX_IMX23_APBDMAREG_H_ */
|
||||||
|
137
sys/arch/arm/imx/imx23_apbdmavar.h
Normal file
137
sys/arch/arm/imx/imx23_apbdmavar.h
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/* $Id: imx23_apbdmavar.h,v 1.1 2013/03/03 10:33:56 jkunz Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013 The NetBSD Foundation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
* by Petri Laakso.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ARM_IMX_IMX23_APBDMAVAR_H_
|
||||||
|
#define _ARM_IMX_IMX23_APBDMAVAR_H_
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/device.h>
|
||||||
|
#include <sys/mutex.h>
|
||||||
|
|
||||||
|
/* DMA command control register bits. */
|
||||||
|
#define APBDMA_CMD_XFER_COUNT __BITS(31, 16)
|
||||||
|
#define APBDMA_CMD_CMDPIOWORDS __BITS(15, 12)
|
||||||
|
#define APBDMA_CMD_RESERVED __BITS(11, 9)
|
||||||
|
#define APBDMA_CMD_HALTONTERMINATE __BIT(8)
|
||||||
|
#define APBDMA_CMD_WAIT4ENDCMD __BIT(7)
|
||||||
|
#define APBDMA_CMD_SEMAPHORE __BIT(6)
|
||||||
|
#define APBDMA_CMD_NANDWAIT4READY __BIT(5)
|
||||||
|
#define APBDMA_CMD_NANDLOCK __BIT(4)
|
||||||
|
#define APBDMA_CMD_IRQONCMPLT __BIT(3)
|
||||||
|
#define APBDMA_CMD_CHAIN __BIT(2)
|
||||||
|
#define APBDMA_CMD_COMMAND __BITS(1, 0)
|
||||||
|
|
||||||
|
/* DMA command types. */
|
||||||
|
#define APBDMA_CMD_NO_DMA_XFER 0
|
||||||
|
#define APBDMA_CMD_DMA_WRITE 1
|
||||||
|
#define APBDMA_CMD_DMA_READ 2
|
||||||
|
#define APBDMA_CMD_DMA_SENSE 3
|
||||||
|
|
||||||
|
/* Flags. */
|
||||||
|
#define F_AHBH_DMA __BIT(0)
|
||||||
|
#define F_AHBX_DMA __BIT(1)
|
||||||
|
|
||||||
|
/* Number of channels. */
|
||||||
|
#define AHBH_DMA_CHANNELS 8
|
||||||
|
#define AHBX_DMA_CHANNELS 16
|
||||||
|
|
||||||
|
/* APBH DMA channel assignments. */
|
||||||
|
#define APBH_DMA_CHANNEL_RES0 0 /* Reserved. */
|
||||||
|
#define APBH_DMA_CHANNEL_SSP1 1 /* SSP1. */
|
||||||
|
#define APBH_DMA_CHANNEL_SSP2 2 /* SSP2. */
|
||||||
|
#define APBH_DMA_CHANNEL_RES1 3 /* Reserved. */
|
||||||
|
#define APBH_DMA_CHANNEL_NAND_DEVICE0 4 /* NAND_DEVICE0. */
|
||||||
|
#define APBH_DMA_CHANNEL_NAND_DEVICE1 5 /* NAND_DEVICE1. */
|
||||||
|
#define APBH_DMA_CHANNEL_NAND_DEVICE2 6 /* NAND_DEVICE2. */
|
||||||
|
#define APBH_DMA_CHANNEL_NAND_DEVICE3 7 /* NAND_DEVICE3. */
|
||||||
|
|
||||||
|
/* APBX DMA channel assignments. */
|
||||||
|
#define APBX_DMA_CHANNEL_AUDIO_ADC 0 /* Audio ADCs. */
|
||||||
|
#define APBX_DMA_CHANNEL_AUDIO_DAC 1 /* Audio DACs. */
|
||||||
|
#define APBX_DMA_CHANNEL_SPDIF_TX 2 /* SPDIF TX. */
|
||||||
|
#define APBX_DMA_CHANNEL_I2C 3 /* I2C. */
|
||||||
|
#define APBX_DMA_CHANNEL_SAIF1 4 /* SAIF1. */
|
||||||
|
#define APBX_DMA_CHANNEL_RES0 5 /* Reserved. */
|
||||||
|
#define APBX_DMA_CHANNEL_UART1_RX 6 /* UART1 RX, IrDA RX. */
|
||||||
|
#define APBX_DMA_CHANNEL_UART1_TX 7 /* UART1 TX, IrDA TX. */
|
||||||
|
#define APBX_DMA_CHANNEL_UART2_RX 8 /* UART2 RX. */
|
||||||
|
#define APBX_DMA_CHANNEL_UART2_TX 9 /* UART2 TX. */
|
||||||
|
#define APBX_DMA_CHANNEL_SAIF2 10 /* SAIF2. */
|
||||||
|
#define APBX_DMA_CHANNEL_RES1 11 /* Reserved. */
|
||||||
|
#define APBX_DMA_CHANNEL_RES2 12 /* Reserved. */
|
||||||
|
#define APBX_DMA_CHANNEL_RES3 13 /* Reserved. */
|
||||||
|
#define APBX_DMA_CHANNEL_RES4 14 /* Reserved. */
|
||||||
|
#define APBX_DMA_CHANNEL_RES5 15 /* Reserved. */
|
||||||
|
|
||||||
|
/* Return codes for apbdma_intr_status() */
|
||||||
|
#define DMA_IRQ_CMDCMPLT 0
|
||||||
|
#define DMA_IRQ_TERM 1
|
||||||
|
#define DMA_IRQ_BUS_ERROR 2
|
||||||
|
|
||||||
|
#define PIO_WORDS_MAX 15
|
||||||
|
|
||||||
|
/*
|
||||||
|
* How many PIO words apbdma_command structure has.
|
||||||
|
*
|
||||||
|
* XXX: If you change this value, make sure drivers are prepared for that.
|
||||||
|
* That means you have to allocate enough DMA memory for command chains.
|
||||||
|
*/
|
||||||
|
#define PIO_WORDS 3
|
||||||
|
|
||||||
|
typedef struct apbdma_softc {
|
||||||
|
device_t sc_dev;
|
||||||
|
bus_dma_tag_t sc_dmat;
|
||||||
|
bus_space_handle_t sc_ioh;
|
||||||
|
bus_space_tag_t sc_iot;
|
||||||
|
kmutex_t sc_lock;
|
||||||
|
u_int flags;
|
||||||
|
} *apbdma_softc_t;
|
||||||
|
|
||||||
|
typedef struct apbdma_command {
|
||||||
|
void *next; /* Physical address. */
|
||||||
|
uint32_t control;
|
||||||
|
void *buffer; /* Physical address. */
|
||||||
|
uint32_t pio_words[PIO_WORDS];
|
||||||
|
} *apbdma_command_t;
|
||||||
|
|
||||||
|
void apbdma_cmd_chain(apbdma_command_t, apbdma_command_t, void *, bus_dmamap_t);
|
||||||
|
void apbdma_cmd_buf(apbdma_command_t, bus_addr_t, bus_dmamap_t);
|
||||||
|
void apbdma_chan_init(struct apbdma_softc *, unsigned int);
|
||||||
|
void apbdma_chan_set_chain(struct apbdma_softc *, unsigned int, bus_dmamap_t);
|
||||||
|
void apbdma_run(struct apbdma_softc *, unsigned int);
|
||||||
|
void apbdma_ack_intr(struct apbdma_softc *, unsigned int);
|
||||||
|
void apbdma_ack_error_intr(struct apbdma_softc *, unsigned int);
|
||||||
|
unsigned int apbdma_intr_status(struct apbdma_softc *, unsigned int);
|
||||||
|
void apbdma_chan_reset(struct apbdma_softc *, unsigned int);
|
||||||
|
|
||||||
|
#endif /* !_ARM_IMX_IMX23_APBDMAVAR_H_ */
|
@ -1,4 +1,4 @@
|
|||||||
/* $Id: imx23_apbhdmareg.h,v 1.1 2012/11/20 19:06:13 jkunz Exp $ */
|
/* $Id: imx23_apbhdmareg.h,v 1.2 2013/03/03 10:33:56 jkunz Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
||||||
@ -122,6 +122,30 @@
|
|||||||
#define HW_APBH_DEVSEL_CH1 __BITS(7, 4)
|
#define HW_APBH_DEVSEL_CH1 __BITS(7, 4)
|
||||||
#define HW_APBH_DEVSEL_CH0 __BITS(3, 0)
|
#define HW_APBH_DEVSEL_CH0 __BITS(3, 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* APBH DMA Channel 0 Current Command Address Register.
|
||||||
|
*/
|
||||||
|
#define HW_APBH_CH0_CURCMDAR 0x040
|
||||||
|
|
||||||
|
#define HW_APBH_CH0_CURCMDAR_CMD_ADDR __BITS(31, 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* APBH DMA Channel 0 Next Command Address.
|
||||||
|
*/
|
||||||
|
#define HW_APBH_CH0_NXTCMDAR 0x050
|
||||||
|
|
||||||
|
#define HW_APBH_CH0_NXTCMDAR_CMD_ADDR __BITS(31, 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* APBH DMA Channel 0 Semaphore Register.
|
||||||
|
*/
|
||||||
|
#define HW_APBH_CH0_SEMA 0x080
|
||||||
|
|
||||||
|
#define HW_APBH_CH0_SEMA_RSVD2 __BITS(31, 24)
|
||||||
|
#define HW_APBH_CH0_SEMA_PHORE __BITS(23, 16)
|
||||||
|
#define HW_APBH_CH0_SEMA_RSVD1 __BITS(15, 8)
|
||||||
|
#define HW_APBH_CH0_SEMA_INCREMENT_SEMA __BITS(7, 0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* APBH DMA Channel 1 Current Command Address Register.
|
* APBH DMA Channel 1 Current Command Address Register.
|
||||||
*/
|
*/
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $Id: imx23_apbxdmareg.h,v 1.1 2012/11/20 19:06:13 jkunz Exp $ */
|
/* $Id: imx23_apbxdmareg.h,v 1.2 2013/03/03 10:33:56 jkunz Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
||||||
@ -34,7 +34,31 @@
|
|||||||
|
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
|
|
||||||
#define HW_APBXDMA_BASE 0x80024000
|
#define HW_APBXDMA_BASE 0x80024000
|
||||||
#define HW_APBXDMA_SIZE 0x2000 /* 8 kB */
|
#define HW_APBXDMA_SIZE 0x2000 /* 8 kB */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* APBX DMA Channel 0 Current Command Address Register.
|
||||||
|
*/
|
||||||
|
#define HW_APBX_CH0_CURCMDAR 0x100
|
||||||
|
|
||||||
|
#define HW_APBX_CH0_CURCMDAR_CMD_ADDR __BITS(31, 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* APBX DMA Channel 0 Next Command Address Register.
|
||||||
|
*/
|
||||||
|
#define HW_APBX_CH0_NXTCMDAR 0x110
|
||||||
|
|
||||||
|
#define HW_APBX_CH0_NXTCMDAR_CMD_ADDR __BITS(31, 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* APBX DMA Channel 0 Semaphore Register.
|
||||||
|
*/
|
||||||
|
#define HW_APBX_CH0_SEMA 0x140
|
||||||
|
|
||||||
|
#define HW_APBX_CH0_SEMA_RSVD2 __BITS(31, 24)
|
||||||
|
#define HW_APBX_CH0_SEMA_PHORE __BITS(23, 16)
|
||||||
|
#define HW_APBX_CH0_SEMA_RSVD1 __BITS(15, 8)
|
||||||
|
#define HW_APBX_CH0_SEMA_INCREMENT_SEMA __BITS(7, 0)
|
||||||
|
|
||||||
#endif /* !_ARM_IMX_IMX23_APBXDMAREG_H_ */
|
#endif /* !_ARM_IMX_IMX23_APBXDMAREG_H_ */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $Id: imx23_icollreg.h,v 1.2 2012/12/16 19:40:00 jkunz Exp $ */
|
/* $Id: imx23_icollreg.h,v 1.3 2013/03/03 10:33:56 jkunz Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
||||||
@ -63,7 +63,7 @@
|
|||||||
* suspended. */
|
* suspended. */
|
||||||
#define IRQ_GPMI_DMA 13 /* From DMA channel for GPMI */
|
#define IRQ_GPMI_DMA 13 /* From DMA channel for GPMI */
|
||||||
#define IRQ_SSP1_DMA 14 /* From DMA channel for SSP1 */
|
#define IRQ_SSP1_DMA 14 /* From DMA channel for SSP1 */
|
||||||
#define IRQ_SSP_ERROR 15 /* SSP1 device-level error and status */
|
#define IRQ_SSP1_ERROR 15 /* SSP1 device-level error and status */
|
||||||
#define IRQ_GPIO0 16 /* GPIO bank 0 interrupt */
|
#define IRQ_GPIO0 16 /* GPIO bank 0 interrupt */
|
||||||
#define IRQ_GPIO1 17 /* GPIO bank 1 interrupt */
|
#define IRQ_GPIO1 17 /* GPIO bank 1 interrupt */
|
||||||
#define IRQ_GPIO2 18 /* GPIO bank 2 interrupt */
|
#define IRQ_GPIO2 18 /* GPIO bank 2 interrupt */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $Id: imx23_ssp.c,v 1.2 2012/12/16 19:45:52 jkunz Exp $ */
|
/* $Id: imx23_ssp.c,v 1.3 2013/03/03 10:33:56 jkunz Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
||||||
@ -33,10 +33,16 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/bus.h>
|
#include <sys/bus.h>
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
|
#include <sys/condvar.h>
|
||||||
#include <sys/device.h>
|
#include <sys/device.h>
|
||||||
#include <sys/errno.h>
|
#include <sys/errno.h>
|
||||||
|
#include <sys/mutex.h>
|
||||||
#include <sys/systm.h>
|
#include <sys/systm.h>
|
||||||
|
|
||||||
|
#include <arm/pic/picvar.h>
|
||||||
|
|
||||||
|
#include <arm/imx/imx23_apbdmavar.h>
|
||||||
|
#include <arm/imx/imx23_icollreg.h>
|
||||||
#include <arm/imx/imx23_sspreg.h>
|
#include <arm/imx/imx23_sspreg.h>
|
||||||
#include <arm/imx/imx23var.h>
|
#include <arm/imx/imx23var.h>
|
||||||
|
|
||||||
@ -46,15 +52,33 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* SD/MMC host controller driver for i.MX23.
|
* SD/MMC host controller driver for i.MX23.
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
*
|
||||||
|
* - Add support for SMC_CAPS_AUTO_STOP.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct issp_softc {
|
#define DMA_MAXNSEGS ((MAXPHYS / PAGE_SIZE) + 1)
|
||||||
|
|
||||||
|
typedef struct issp_softc {
|
||||||
device_t sc_dev;
|
device_t sc_dev;
|
||||||
bus_space_tag_t sc_iot;
|
apbdma_softc_t sc_dmac;
|
||||||
|
bus_dma_tag_t sc_dmat;
|
||||||
|
bus_dmamap_t sc_dmamp;
|
||||||
|
bus_size_t sc_chnsiz;
|
||||||
|
bus_dma_segment_t sc_ds[1];
|
||||||
|
int sc_rseg;
|
||||||
bus_space_handle_t sc_hdl;
|
bus_space_handle_t sc_hdl;
|
||||||
|
bus_space_tag_t sc_iot;
|
||||||
device_t sc_sdmmc;
|
device_t sc_sdmmc;
|
||||||
device_t dmac;
|
kmutex_t sc_lock;
|
||||||
};
|
struct kcondvar sc_intr_cv;
|
||||||
|
unsigned int dma_channel;
|
||||||
|
uint32_t sc_dma_error;
|
||||||
|
uint32_t sc_irq_error;
|
||||||
|
uint8_t sc_state;
|
||||||
|
uint8_t sc_bus_width;
|
||||||
|
} *issp_softc_t;
|
||||||
|
|
||||||
static int issp_match(device_t, cfdata_t, void *);
|
static int issp_match(device_t, cfdata_t, void *);
|
||||||
static void issp_attach(device_t, device_t, void *);
|
static void issp_attach(device_t, device_t, void *);
|
||||||
@ -62,7 +86,16 @@ static int issp_activate(device_t, enum devact);
|
|||||||
|
|
||||||
static void issp_reset(struct issp_softc *);
|
static void issp_reset(struct issp_softc *);
|
||||||
static void issp_init(struct issp_softc *);
|
static void issp_init(struct issp_softc *);
|
||||||
static uint32_t issp_set_sck(struct issp_softc *, uint32_t target);
|
static uint32_t issp_set_sck(struct issp_softc *, uint32_t);
|
||||||
|
static int issp_dma_intr(void *);
|
||||||
|
static int issp_error_intr(void *);
|
||||||
|
static void issp_ack_intr(struct issp_softc *);
|
||||||
|
static void issp_create_dma_cmd_list_multi(issp_softc_t, void *,
|
||||||
|
struct sdmmc_command *);
|
||||||
|
static void issp_create_dma_cmd_list_single(issp_softc_t, void *,
|
||||||
|
struct sdmmc_command *);
|
||||||
|
static void issp_create_dma_cmd_list(issp_softc_t, void *,
|
||||||
|
struct sdmmc_command *);
|
||||||
|
|
||||||
/* sdmmc(4) driver chip function prototypes. */
|
/* sdmmc(4) driver chip function prototypes. */
|
||||||
static int issp_host_reset(sdmmc_chipset_handle_t);
|
static int issp_host_reset(sdmmc_chipset_handle_t);
|
||||||
@ -75,7 +108,7 @@ static int issp_bus_clock(sdmmc_chipset_handle_t, int);
|
|||||||
static int issp_bus_width(sdmmc_chipset_handle_t, int);
|
static int issp_bus_width(sdmmc_chipset_handle_t, int);
|
||||||
static int issp_bus_rod(sdmmc_chipset_handle_t, int);
|
static int issp_bus_rod(sdmmc_chipset_handle_t, int);
|
||||||
static void issp_exec_command(sdmmc_chipset_handle_t,
|
static void issp_exec_command(sdmmc_chipset_handle_t,
|
||||||
struct sdmmc_command *);
|
struct sdmmc_command *);
|
||||||
static void issp_card_enable_intr(sdmmc_chipset_handle_t, int);
|
static void issp_card_enable_intr(sdmmc_chipset_handle_t, int);
|
||||||
static void issp_card_intr_ack(sdmmc_chipset_handle_t);
|
static void issp_card_intr_ack(sdmmc_chipset_handle_t);
|
||||||
|
|
||||||
@ -102,7 +135,8 @@ CFATTACH_DECL3_NEW(ssp,
|
|||||||
issp_activate,
|
issp_activate,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
0);
|
0
|
||||||
|
);
|
||||||
|
|
||||||
#define SSP_SOFT_RST_LOOP 455 /* At least 1 us ... */
|
#define SSP_SOFT_RST_LOOP 455 /* At least 1 us ... */
|
||||||
|
|
||||||
@ -115,24 +149,37 @@ CFATTACH_DECL3_NEW(ssp,
|
|||||||
#define SSP_CLK_MIN 400 /* 400 kHz */
|
#define SSP_CLK_MIN 400 /* 400 kHz */
|
||||||
#define SSP_CLK_MAX 48000 /* 48 MHz */
|
#define SSP_CLK_MAX 48000 /* 48 MHz */
|
||||||
|
|
||||||
#define SSP_BUSY (HW_SSP_STATUS_CMD_BUSY | \
|
/* DATA_TIMEOUT is calculated as: * (1 / SSP_CLK) * (DATA_TIMEOUT * 4096) */
|
||||||
HW_SSP_STATUS_DATA_BUSY | \
|
#define DATA_TIMEOUT 0x4240 /* 723ms */
|
||||||
HW_SSP_STATUS_BUSY)
|
|
||||||
|
|
||||||
#define SSP_RUN_ERR (HW_SSP_STATUS_RESP_CRC_ERR | \
|
|
||||||
HW_SSP_STATUS_RESP_ERR | \
|
|
||||||
HW_SSP_STATUS_RESP_TIMEOUT | \
|
|
||||||
HW_SSP_STATUS_DATA_CRC_ERR | \
|
|
||||||
HW_SSP_STATUS_TIMEOUT)
|
|
||||||
|
|
||||||
#define BLKIO_NONE 0
|
|
||||||
#define BLKIO_RD 1
|
|
||||||
#define BLKIO_WR 2
|
|
||||||
|
|
||||||
#define BUS_WIDTH_1_BIT 0x0
|
#define BUS_WIDTH_1_BIT 0x0
|
||||||
#define BUS_WIDTH_4_BIT 0x1
|
#define BUS_WIDTH_4_BIT 0x1
|
||||||
#define BUS_WIDTH_8_BIT 0x2
|
#define BUS_WIDTH_8_BIT 0x2
|
||||||
|
|
||||||
|
#define SSP1_ATTACHED 1
|
||||||
|
#define SSP2_ATTACHED 2
|
||||||
|
|
||||||
|
/* Flags for sc_state. */
|
||||||
|
#define SSP_STATE_IDLE 0
|
||||||
|
#define SSP_STATE_DMA 1
|
||||||
|
|
||||||
|
#define PIO_WORD_CTRL0 0
|
||||||
|
#define PIO_WORD_CMD0 1
|
||||||
|
#define PIO_WORD_CMD1 2
|
||||||
|
|
||||||
|
#define HW_SSP_CTRL1_IRQ_MASK ( \
|
||||||
|
HW_SSP_CTRL1_SDIO_IRQ | \
|
||||||
|
HW_SSP_CTRL1_RESP_ERR_IRQ | \
|
||||||
|
HW_SSP_CTRL1_RESP_TIMEOUT_IRQ | \
|
||||||
|
HW_SSP_CTRL1_DATA_TIMEOUT_IRQ | \
|
||||||
|
HW_SSP_CTRL1_DATA_CRC_IRQ | \
|
||||||
|
HW_SSP_CTRL1_FIFO_UNDERRUN_IRQ | \
|
||||||
|
HW_SSP_CTRL1_RECV_TIMEOUT_IRQ | \
|
||||||
|
HW_SSP_CTRL1_FIFO_OVERRUN_IRQ)
|
||||||
|
|
||||||
|
/* SSP does not support over 64k transfer size. */
|
||||||
|
#define MAX_TRANSFER_SIZE 65536
|
||||||
|
|
||||||
static int
|
static int
|
||||||
issp_match(device_t parent, cfdata_t match, void *aux)
|
issp_match(device_t parent, cfdata_t match, void *aux)
|
||||||
{
|
{
|
||||||
@ -154,18 +201,108 @@ issp_attach(device_t parent, device_t self, void *aux)
|
|||||||
struct apb_softc *sc_parent = device_private(parent);
|
struct apb_softc *sc_parent = device_private(parent);
|
||||||
struct apb_attach_args *aa = aux;
|
struct apb_attach_args *aa = aux;
|
||||||
struct sdmmcbus_attach_args saa;
|
struct sdmmcbus_attach_args saa;
|
||||||
static int issp_attached = 0;
|
static int ssp_attached = 0;
|
||||||
|
int error;
|
||||||
if (issp_attached)
|
void *intr;
|
||||||
return;
|
|
||||||
|
|
||||||
sc->sc_dev = self;
|
sc->sc_dev = self;
|
||||||
sc->sc_iot = aa->aa_iot;
|
sc->sc_iot = aa->aa_iot;
|
||||||
sc->dmac = sc_parent->dmac;
|
sc->sc_dmat = aa->aa_dmat;
|
||||||
|
|
||||||
if (bus_space_map(sc->sc_iot,
|
/* Test if device instance is already attached. */
|
||||||
aa->aa_addr, aa->aa_size, 0, &(sc->sc_hdl))) {
|
if (aa->aa_addr == HW_SSP1_BASE && ISSET(ssp_attached, SSP1_ATTACHED)) {
|
||||||
aprint_error_dev(sc->sc_dev, "unable to map bus space\n");
|
aprint_error_dev(sc->sc_dev, "SSP1 already attached\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (aa->aa_addr == HW_SSP2_BASE && ISSET(ssp_attached, SSP2_ATTACHED)) {
|
||||||
|
aprint_error_dev(sc->sc_dev, "SSP2 already attached\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aa->aa_addr == HW_SSP1_BASE) {
|
||||||
|
sc->dma_channel = APBH_DMA_CHANNEL_SSP1;
|
||||||
|
}
|
||||||
|
if (aa->aa_addr == HW_SSP2_BASE) {
|
||||||
|
sc->dma_channel = APBH_DMA_CHANNEL_SSP2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This driver requires DMA functionality from the bus.
|
||||||
|
* Parent bus passes handle to the DMA controller instance. */
|
||||||
|
if (sc_parent->dmac == NULL) {
|
||||||
|
aprint_error_dev(sc->sc_dev, "DMA functionality missing\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sc->sc_dmac = device_private(sc_parent->dmac);
|
||||||
|
|
||||||
|
/* Initialize lock. */
|
||||||
|
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SDMMC);
|
||||||
|
|
||||||
|
/* Condvar to wait interrupt complete. */
|
||||||
|
cv_init(&sc->sc_intr_cv, "ssp_intr");
|
||||||
|
|
||||||
|
/* Establish interrupt handlers for SSP errors and SSP DMA. */
|
||||||
|
if (aa->aa_addr == HW_SSP1_BASE) {
|
||||||
|
intr = intr_establish(IRQ_SSP1_DMA, IPL_SDMMC, IST_LEVEL,
|
||||||
|
issp_dma_intr, sc);
|
||||||
|
if (intr == NULL) {
|
||||||
|
aprint_error_dev(sc->sc_dev, "Unable to establish "
|
||||||
|
"interrupt for SSP1 DMA\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
intr = intr_establish(IRQ_SSP1_ERROR, IPL_SDMMC, IST_LEVEL,
|
||||||
|
issp_error_intr, sc);
|
||||||
|
if (intr == NULL) {
|
||||||
|
aprint_error_dev(sc->sc_dev, "Unable to establish "
|
||||||
|
"interrupt for SSP1 ERROR\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aa->aa_addr == HW_SSP2_BASE) {
|
||||||
|
intr = intr_establish(IRQ_SSP2_DMA, IPL_SDMMC, IST_LEVEL,
|
||||||
|
issp_dma_intr, sc);
|
||||||
|
if (intr == NULL) {
|
||||||
|
aprint_error_dev(sc->sc_dev, "Unable to establish "
|
||||||
|
"interrupt for SSP2 DMA\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
intr = intr_establish(IRQ_SSP2_ERROR, IPL_SDMMC, IST_LEVEL,
|
||||||
|
issp_error_intr, sc);
|
||||||
|
if (intr == NULL) {
|
||||||
|
aprint_error_dev(sc->sc_dev, "Unable to establish "
|
||||||
|
"interrupt for SSP2 ERROR\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate DMA handle. */
|
||||||
|
error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, 1, MAXPHYS,
|
||||||
|
0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamp);
|
||||||
|
if (error) {
|
||||||
|
aprint_error_dev(sc->sc_dev,
|
||||||
|
"Unable to allocate DMA handle\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate memory for DMA command chain. */
|
||||||
|
sc->sc_chnsiz = sizeof(struct apbdma_command) *
|
||||||
|
(MAX_TRANSFER_SIZE / SDMMC_SECTOR_SIZE);
|
||||||
|
|
||||||
|
error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_chnsiz, PAGE_SIZE, 0,
|
||||||
|
sc->sc_ds, 1, &sc->sc_rseg, BUS_DMA_NOWAIT);
|
||||||
|
if (error) {
|
||||||
|
aprint_error_dev(sc->sc_dev,
|
||||||
|
"Unable to allocate DMA memory\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize DMA channel. */
|
||||||
|
apbdma_chan_init(sc->sc_dmac, sc->dma_channel);
|
||||||
|
|
||||||
|
/* Map SSP bus space. */
|
||||||
|
if (bus_space_map(sc->sc_iot, aa->aa_addr, aa->aa_size, 0,
|
||||||
|
&sc->sc_hdl)) {
|
||||||
|
aprint_error_dev(sc->sc_dev, "Unable to map SSP bus space\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +314,7 @@ issp_attach(device_t parent, device_t self, void *aux)
|
|||||||
__SHIFTOUT(issp_vers, HW_SSP_VERSION_MAJOR),
|
__SHIFTOUT(issp_vers, HW_SSP_VERSION_MAJOR),
|
||||||
__SHIFTOUT(issp_vers, HW_SSP_VERSION_MINOR));
|
__SHIFTOUT(issp_vers, HW_SSP_VERSION_MINOR));
|
||||||
|
|
||||||
|
/* Attach sdmmc to ssp bus. */
|
||||||
saa.saa_busname = "sdmmc";
|
saa.saa_busname = "sdmmc";
|
||||||
saa.saa_sct = &issp_functions;
|
saa.saa_sct = &issp_functions;
|
||||||
saa.saa_spi_sct = NULL;
|
saa.saa_spi_sct = NULL;
|
||||||
@ -184,8 +322,8 @@ issp_attach(device_t parent, device_t self, void *aux)
|
|||||||
saa.saa_dmat = aa->aa_dmat;
|
saa.saa_dmat = aa->aa_dmat;
|
||||||
saa.saa_clkmin = SSP_CLK_MIN;
|
saa.saa_clkmin = SSP_CLK_MIN;
|
||||||
saa.saa_clkmax = SSP_CLK_MAX;
|
saa.saa_clkmax = SSP_CLK_MAX;
|
||||||
/* Add SMC_CAPS_DMA capability when DMA funtionality is implemented. */
|
saa.saa_caps = SMC_CAPS_DMA | SMC_CAPS_4BIT_MODE |
|
||||||
saa.saa_caps = SMC_CAPS_4BIT_MODE | SMC_CAPS_SINGLE_ONLY;
|
SMC_CAPS_MULTI_SEG_DMA;
|
||||||
|
|
||||||
sc->sc_sdmmc = config_found(sc->sc_dev, &saa, NULL);
|
sc->sc_sdmmc = config_found(sc->sc_dev, &saa, NULL);
|
||||||
if (sc->sc_sdmmc == NULL) {
|
if (sc->sc_sdmmc == NULL) {
|
||||||
@ -193,7 +331,11 @@ issp_attach(device_t parent, device_t self, void *aux)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
issp_attached = 1;
|
/* Device instance was succesfully attached. */
|
||||||
|
if (aa->aa_addr == HW_SSP1_BASE)
|
||||||
|
ssp_attached |= SSP1_ATTACHED;
|
||||||
|
if (aa->aa_addr == HW_SSP2_BASE)
|
||||||
|
ssp_attached |= SSP2_ATTACHED;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -211,16 +353,14 @@ static int
|
|||||||
issp_host_reset(sdmmc_chipset_handle_t sch)
|
issp_host_reset(sdmmc_chipset_handle_t sch)
|
||||||
{
|
{
|
||||||
struct issp_softc *sc = sch;
|
struct issp_softc *sc = sch;
|
||||||
|
|
||||||
issp_reset(sc);
|
issp_reset(sc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static uint32_t
|
||||||
issp_host_ocr(sdmmc_chipset_handle_t sch)
|
issp_host_ocr(sdmmc_chipset_handle_t sch)
|
||||||
{
|
{
|
||||||
/* SSP supports at least 3.2-3.3v */
|
/* SSP supports at least 3.2 - 3.3v */
|
||||||
return MMC_OCR_3_2V_3_3V;
|
return MMC_OCR_3_2V_3_3V;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,29 +377,6 @@ issp_host_maxblklen(sdmmc_chipset_handle_t sch)
|
|||||||
static int
|
static int
|
||||||
issp_card_detect(sdmmc_chipset_handle_t sch)
|
issp_card_detect(sdmmc_chipset_handle_t sch)
|
||||||
{
|
{
|
||||||
/* struct issp_softc *sc = sch;
|
|
||||||
*
|
|
||||||
* In the perfect world I'll just:
|
|
||||||
* return SSP_RD(sc, HW_SSP_STATUS) & HW_SSP_STATUS_CARD_DETECT;
|
|
||||||
* and call it a day.
|
|
||||||
*
|
|
||||||
* But on i.MX23 OLinuXino MAXI, SSP1_DETECT is not used for the SD
|
|
||||||
* card detection but SSP1_DATA3 is, as Tsvetan put it:
|
|
||||||
*
|
|
||||||
* < Tsvetan> if you want to know if SD card is inserted watch
|
|
||||||
* CD/DAT3/CS port
|
|
||||||
* < Tsvetan> without card there is R20 weak pulldown
|
|
||||||
* < Tsvetan> all cards have 40K pullup to this pin
|
|
||||||
* < Tsvetan> so when card is inserted you will read it high
|
|
||||||
*
|
|
||||||
* Which means I should to do something like this:
|
|
||||||
* #if BOARDTYPE == MAXI (Possibly MINI & MICRO)
|
|
||||||
* return GPIO_READ(PIN_125) & PIN_125
|
|
||||||
* #else
|
|
||||||
* return SSP_RD(sc, STATUS) & CARD_DETECT;
|
|
||||||
* #endif
|
|
||||||
* Until GPIO functionality is not present I am just going to */
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,13 +400,22 @@ issp_bus_clock(sdmmc_chipset_handle_t sch, int clock)
|
|||||||
struct issp_softc *sc = sch;
|
struct issp_softc *sc = sch;
|
||||||
uint32_t sck;
|
uint32_t sck;
|
||||||
|
|
||||||
sck = issp_set_sck(sc, clock * 1000);
|
if (clock < SSP_CLK_MIN)
|
||||||
|
sck = issp_set_sck(sc, SSP_CLK_MIN * 1000);
|
||||||
|
else
|
||||||
|
sck = issp_set_sck(sc, clock * 1000);
|
||||||
|
|
||||||
/* Notify user if we didn't get exact clock rate from SSP that was
|
/* Notify user if we didn't get the exact clock rate from SSP that was
|
||||||
* requested. */
|
* requested from the SDMMC subsystem. */
|
||||||
if (sck != clock * 1000)
|
if (sck != clock * 1000) {
|
||||||
aprint_normal_dev(sc->sc_dev, "requested clock %dHz, "
|
sck = sck / 1000;
|
||||||
"but got %dHz\n", clock * 1000, sck);
|
if (((sck) / 1000) != 0)
|
||||||
|
aprint_normal_dev(sc->sc_dev, "bus clock @ %u.%03u "
|
||||||
|
"MHz\n", sck / 1000, sck % 1000);
|
||||||
|
else
|
||||||
|
aprint_normal_dev(sc->sc_dev, "bus clock @ %u KHz\n",
|
||||||
|
sck % 1000);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -298,27 +424,21 @@ static int
|
|||||||
issp_bus_width(sdmmc_chipset_handle_t sch, int width)
|
issp_bus_width(sdmmc_chipset_handle_t sch, int width)
|
||||||
{
|
{
|
||||||
struct issp_softc *sc = sch;
|
struct issp_softc *sc = sch;
|
||||||
uint32_t reg;
|
|
||||||
|
|
||||||
reg = SSP_RD(sc, HW_SSP_CTRL0);
|
|
||||||
reg &= ~(HW_SSP_CTRL0_BUS_WIDTH);
|
|
||||||
|
|
||||||
switch(width) {
|
switch(width) {
|
||||||
case(1):
|
case(1):
|
||||||
reg |= __SHIFTIN(BUS_WIDTH_1_BIT, HW_SSP_CTRL0_BUS_WIDTH);
|
sc->sc_bus_width = BUS_WIDTH_1_BIT;
|
||||||
break;
|
break;
|
||||||
case(4):
|
case(4):
|
||||||
reg |= __SHIFTIN(BUS_WIDTH_4_BIT, HW_SSP_CTRL0_BUS_WIDTH);
|
sc->sc_bus_width = BUS_WIDTH_4_BIT;
|
||||||
break;
|
break;
|
||||||
case(8):
|
case(8):
|
||||||
reg |= __SHIFTIN(BUS_WIDTH_8_BIT, HW_SSP_CTRL0_BUS_WIDTH);
|
sc->sc_bus_width = BUS_WIDTH_8_BIT;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
SSP_WR(sc, HW_SSP_CTRL0, reg);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,112 +452,104 @@ issp_bus_rod(sdmmc_chipset_handle_t sch, int rod)
|
|||||||
static void
|
static void
|
||||||
issp_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
|
issp_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
|
||||||
{
|
{
|
||||||
struct issp_softc *sc = sch;
|
issp_softc_t sc = sch;
|
||||||
uint32_t reg;
|
void *dma_chain;
|
||||||
uint32_t do_blkio;
|
int error;
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
do_blkio = 0;
|
/* SSP does not support over 64k transfer size. */
|
||||||
|
if (cmd->c_data != NULL && cmd->c_datalen > MAX_TRANSFER_SIZE) {
|
||||||
/* Wait until SSP done. (data I/O error + retry...) */
|
aprint_error_dev(sc->sc_dev, "transfer size over %d: %d\n",
|
||||||
while (SSP_RD(sc, HW_SSP_STATUS) & SSP_BUSY)
|
MAX_TRANSFER_SIZE, cmd->c_datalen);
|
||||||
;
|
cmd->c_error = ENODEV;
|
||||||
|
return;
|
||||||
/* Set expected response type. */
|
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_CLR,
|
|
||||||
HW_SSP_CTRL0_GET_RESP | HW_SSP_CTRL0_LONG_RESP);
|
|
||||||
|
|
||||||
if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
|
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_GET_RESP);
|
|
||||||
if (ISSET(cmd->c_flags, SCF_RSP_136))
|
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_LONG_RESP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If CMD does not need CRC validation, tell it to SSP. */
|
/* Map dma_chain to point allocated previously allocated DMA chain. */
|
||||||
if (ISSET(cmd->c_flags, SCF_RSP_CRC))
|
error = bus_dmamem_map(sc->sc_dmat, sc->sc_ds, 1, sc->sc_chnsiz,
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_IGNORE_CRC);
|
&dma_chain, BUS_DMA_NOWAIT);
|
||||||
else
|
if (error) {
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_IGNORE_CRC);
|
aprint_error_dev(sc->sc_dev, "bus_dmamem_map: %d\n", error);
|
||||||
|
cmd->c_error = error;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set command. */
|
error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, dma_chain,
|
||||||
SSP_WR(sc, HW_SSP_CMD0_CLR, HW_SSP_CMD0_CMD);
|
sc->sc_chnsiz, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE);
|
||||||
SSP_WR(sc, HW_SSP_CMD0_SET,
|
if (error) {
|
||||||
__SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD));
|
aprint_error_dev(sc->sc_dev, "bus_dmamap_load: %d\n", error);
|
||||||
|
cmd->c_error = error;
|
||||||
|
goto dmamem_unmap;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set command argument. */
|
memset(dma_chain, 0, sc->sc_chnsiz);
|
||||||
SSP_WR(sc, HW_SSP_CMD1, cmd->c_arg);
|
|
||||||
|
|
||||||
/* Is data to be transferred? */
|
/* Setup DMA command chain.*/
|
||||||
if (cmd->c_datalen > 0 && cmd->c_data != NULL) {
|
if (cmd->c_data != NULL && (cmd->c_datalen / cmd->c_blklen) > 1) {
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_DATA_XFER);
|
/* Multi block transfer. */
|
||||||
/* Transfer XFER_COUNT of 8-bit words. */
|
issp_create_dma_cmd_list_multi(sc, dma_chain, cmd);
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_XFER_COUNT);
|
} else if (cmd->c_data != NULL && cmd->c_datalen) {
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_SET,
|
/* Single block transfer. */
|
||||||
__SHIFTIN(cmd->c_datalen, HW_SSP_CTRL0_XFER_COUNT));
|
issp_create_dma_cmd_list_single(sc, dma_chain, cmd);
|
||||||
|
|
||||||
/* XXX: why 8CYC? Bit is never cleaned. */
|
|
||||||
SSP_WR(sc, HW_SSP_CMD0_SET, HW_SSP_CMD0_APPEND_8CYC);
|
|
||||||
|
|
||||||
if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
|
|
||||||
/* Read mode. */
|
|
||||||
do_blkio |= BLKIO_RD;
|
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_READ);
|
|
||||||
} else {
|
|
||||||
/* Write mode. */
|
|
||||||
do_blkio |= BLKIO_WR;
|
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_READ);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/* No data to be transferred. */
|
/* Only command, no data. */
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_DATA_XFER);
|
issp_create_dma_cmd_list(sc, dma_chain, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run the command. */
|
/* Tell DMA controller where it can find just initialized DMA chain. */
|
||||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_RUN);
|
apbdma_chan_set_chain(sc->sc_dmac, sc->dma_channel, sc->sc_dmamp);
|
||||||
|
|
||||||
if (ISSET(do_blkio, BLKIO_RD)) {
|
/* Synchronize command chain before DMA controller accesses it. */
|
||||||
for (i = 0; i < cmd->c_datalen / 4; i++) {
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, sc->sc_chnsiz,
|
||||||
/* Wait until data arrives to FIFO. */
|
BUS_DMASYNC_PREWRITE);
|
||||||
while (SSP_RD(sc, HW_SSP_STATUS)
|
|
||||||
& HW_SSP_STATUS_FIFO_EMPTY) {
|
sc->sc_state = SSP_STATE_DMA;
|
||||||
/* Abort if error while waiting. */
|
sc->sc_irq_error = 0;
|
||||||
if (SSP_RD(sc, HW_SSP_STATUS) & SSP_RUN_ERR) {
|
sc->sc_dma_error = 0;
|
||||||
aprint_normal_dev(sc->sc_dev,
|
cmd->c_error = 0;
|
||||||
"RD_ERR: %x\n",
|
|
||||||
SSP_RD(sc, HW_SSP_STATUS));
|
mutex_enter(&sc->sc_lock);
|
||||||
cmd->c_error = 1;
|
|
||||||
goto pioerr;
|
/* Run DMA command chain. */
|
||||||
}
|
apbdma_run(sc->sc_dmac, sc->dma_channel);
|
||||||
}
|
|
||||||
*((uint32_t *)cmd->c_data+i) = SSP_RD(sc, HW_SSP_DATA);
|
/* Wait DMA to complete. */
|
||||||
|
while (sc->sc_state == SSP_STATE_DMA)
|
||||||
|
cv_wait(&sc->sc_intr_cv, &sc->sc_lock);
|
||||||
|
|
||||||
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, sc->sc_chnsiz,
|
||||||
|
BUS_DMASYNC_POSTWRITE);
|
||||||
|
|
||||||
|
if (sc->sc_dma_error) {
|
||||||
|
if (sc->sc_dma_error == DMA_IRQ_TERM) {
|
||||||
|
apbdma_chan_reset(sc->sc_dmac, sc->dma_channel);
|
||||||
|
cmd->c_error = sc->sc_dma_error;
|
||||||
}
|
}
|
||||||
} else if (ISSET(do_blkio, BLKIO_WR)) {
|
else if (sc->sc_dma_error == DMA_IRQ_BUS_ERROR) {
|
||||||
for (i = 0; i < (cmd->c_datalen / 4); i++) {
|
aprint_error_dev(sc->sc_dev, "DMA_IRQ_BUS_ERROR: %d\n",
|
||||||
while (SSP_RD(sc, HW_SSP_STATUS)
|
sc->sc_irq_error);
|
||||||
& HW_SSP_STATUS_FIFO_FULL) {
|
cmd->c_error = sc->sc_dma_error;
|
||||||
/* Abort if error while waiting. */
|
|
||||||
if (SSP_RD(sc, HW_SSP_STATUS) & SSP_RUN_ERR) {
|
|
||||||
aprint_normal_dev(sc->sc_dev,
|
|
||||||
"WR_ERR: %x\n",
|
|
||||||
SSP_RD(sc, HW_SSP_STATUS));
|
|
||||||
cmd->c_error = 1;
|
|
||||||
goto pioerr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SSP_WR(sc, HW_SSP_DATA, *((uint32_t *)cmd->c_data+i));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait until SSP is done. */
|
if (sc->sc_irq_error) {
|
||||||
while (SSP_RD(sc, HW_SSP_STATUS) & SSP_BUSY)
|
/* Do not log RESP_TIMEOUT_IRQ error if bus width is 0 as it is
|
||||||
;
|
* expected during SD card initialization phase. */
|
||||||
|
if (sc->sc_bus_width) {
|
||||||
|
aprint_error_dev(sc->sc_dev, "SSP_ERROR_IRQ: %d\n",
|
||||||
|
sc->sc_irq_error);
|
||||||
|
}
|
||||||
|
else if(!(sc->sc_irq_error & HW_SSP_CTRL1_RESP_TIMEOUT_IRQ)) {
|
||||||
|
aprint_error_dev(sc->sc_dev, "SSP_ERROR_IRQ: %d\n",
|
||||||
|
sc->sc_irq_error);
|
||||||
|
}
|
||||||
|
|
||||||
/* Check if the command ran successfully. */
|
/* Shift unsigned error code so it fits nicely to signed int. */
|
||||||
reg = SSP_RD(sc, HW_SSP_STATUS);
|
cmd->c_error = sc->sc_irq_error >> 8;
|
||||||
if (reg & SSP_RUN_ERR)
|
}
|
||||||
cmd->c_error = reg & SSP_RUN_ERR;
|
|
||||||
|
|
||||||
/* Read response if such was requested. */
|
/* Check reponse from the card if such was requested. */
|
||||||
if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
|
if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
|
||||||
cmd->c_resp[0] = SSP_RD(sc, HW_SSP_SDRESP0);
|
cmd->c_resp[0] = SSP_RD(sc, HW_SSP_SDRESP0);
|
||||||
if (ISSET(cmd->c_flags, SCF_RSP_136)) {
|
if (ISSET(cmd->c_flags, SCF_RSP_136)) {
|
||||||
@ -457,7 +569,12 @@ issp_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
|
|||||||
cmd->c_resp[3] >>= 8;
|
cmd->c_resp[3] >>= 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pioerr:
|
|
||||||
|
bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamp);
|
||||||
|
dmamem_unmap:
|
||||||
|
bus_dmamem_unmap(sc->sc_dmat, dma_chain, sc->sc_chnsiz);
|
||||||
|
out:
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,10 +582,7 @@ static void
|
|||||||
issp_card_enable_intr(sdmmc_chipset_handle_t sch, int irq)
|
issp_card_enable_intr(sdmmc_chipset_handle_t sch, int irq)
|
||||||
{
|
{
|
||||||
struct issp_softc *sc = sch;
|
struct issp_softc *sc = sch;
|
||||||
|
aprint_error_dev(sc->sc_dev, "issp_card_enable_intr not implemented\n");
|
||||||
aprint_normal_dev(sc->sc_dev,
|
|
||||||
"issp_card_enable_intr NOT IMPLEMENTED!\n");
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,9 +590,7 @@ static void
|
|||||||
issp_card_intr_ack(sdmmc_chipset_handle_t sch)
|
issp_card_intr_ack(sdmmc_chipset_handle_t sch)
|
||||||
{
|
{
|
||||||
struct issp_softc *sc = sch;
|
struct issp_softc *sc = sch;
|
||||||
|
aprint_error_dev(sc->sc_dev, "issp_card_intr_ack not implemented\n");
|
||||||
aprint_normal_dev(sc->sc_dev, "issp_card_intr_ack NOT IMPLEMENTED!\n");
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,13 +640,6 @@ issp_reset(struct issp_softc *sc)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* DATA_TIMEOUT is calculated as:
|
|
||||||
* (1 / SSP_CLK) * (DATA_TIMEOUT * 4096)
|
|
||||||
*/
|
|
||||||
#define DATA_TIMEOUT 0x4240 /* 723ms */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize SSP controller to SD/MMC mode.
|
* Initialize SSP controller to SD/MMC mode.
|
||||||
*/
|
*/
|
||||||
@ -543,27 +648,39 @@ issp_init(struct issp_softc *sc)
|
|||||||
{
|
{
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
|
|
||||||
/* Initial data bus width is 1-bit. */
|
|
||||||
reg = SSP_RD(sc, HW_SSP_CTRL0);
|
reg = SSP_RD(sc, HW_SSP_CTRL0);
|
||||||
|
reg |= HW_SSP_CTRL0_ENABLE;
|
||||||
|
|
||||||
|
/* Initial data bus width is 1-bit. */
|
||||||
reg &= ~(HW_SSP_CTRL0_BUS_WIDTH);
|
reg &= ~(HW_SSP_CTRL0_BUS_WIDTH);
|
||||||
reg |= __SHIFTIN(BUS_WIDTH_1_BIT, HW_SSP_CTRL0_BUS_WIDTH) |
|
reg |= __SHIFTIN(BUS_WIDTH_1_BIT, HW_SSP_CTRL0_BUS_WIDTH) |
|
||||||
HW_SSP_CTRL0_WAIT_FOR_IRQ | HW_SSP_CTRL0_ENABLE;
|
HW_SSP_CTRL0_WAIT_FOR_IRQ | HW_SSP_CTRL0_ENABLE;
|
||||||
SSP_WR(sc, HW_SSP_CTRL0, reg);
|
SSP_WR(sc, HW_SSP_CTRL0, reg);
|
||||||
|
sc->sc_bus_width = BUS_WIDTH_1_BIT;
|
||||||
|
|
||||||
/* Set data timeout. */
|
/* Set data timeout. */
|
||||||
reg = SSP_RD(sc, HW_SSP_TIMING);
|
reg = SSP_RD(sc, HW_SSP_TIMING);
|
||||||
reg &= ~(HW_SSP_TIMING_TIMEOUT);
|
reg &= ~(HW_SSP_TIMING_TIMEOUT);
|
||||||
reg |= __SHIFTIN(DATA_TIMEOUT, HW_SSP_TIMING_TIMEOUT);
|
reg |= __SHIFTIN(DATA_TIMEOUT, HW_SSP_TIMING_TIMEOUT);
|
||||||
|
SSP_WR(sc, HW_SSP_TIMING, reg);
|
||||||
|
|
||||||
/* Set initial clock rate to minimum. */
|
/* Set initial clock rate to minimum. */
|
||||||
issp_set_sck(sc, SSP_CLK_MIN * 1000);
|
issp_set_sck(sc, SSP_CLK_MIN * 1000);
|
||||||
|
|
||||||
SSP_WR(sc, HW_SSP_TIMING, reg);
|
|
||||||
/* Enable SD/MMC mode and use use 8-bits per word. */
|
|
||||||
reg = SSP_RD(sc, HW_SSP_CTRL1);
|
reg = SSP_RD(sc, HW_SSP_CTRL1);
|
||||||
|
/* Enable all but SDIO IRQ's. */
|
||||||
|
reg |= HW_SSP_CTRL1_RESP_ERR_IRQ_EN |
|
||||||
|
HW_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN |
|
||||||
|
HW_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |
|
||||||
|
HW_SSP_CTRL1_DATA_CRC_IRQ_EN |
|
||||||
|
HW_SSP_CTRL1_FIFO_UNDERRUN_EN |
|
||||||
|
HW_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
|
||||||
|
HW_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN;
|
||||||
|
reg |= HW_SSP_CTRL1_DMA_ENABLE;
|
||||||
|
reg |= HW_SSP_CTRL1_POLARITY;
|
||||||
|
/* Set SD/MMC mode and use use 8-bits per word. */
|
||||||
reg &= ~(HW_SSP_CTRL1_WORD_LENGTH | HW_SSP_CTRL1_SSP_MODE);
|
reg &= ~(HW_SSP_CTRL1_WORD_LENGTH | HW_SSP_CTRL1_SSP_MODE);
|
||||||
reg |= HW_SSP_CTRL1_POLARITY |
|
reg |= __SHIFTIN(0x7, HW_SSP_CTRL1_WORD_LENGTH) |
|
||||||
__SHIFTIN(0x7, HW_SSP_CTRL1_WORD_LENGTH) |
|
|
||||||
__SHIFTIN(0x3, HW_SSP_CTRL1_SSP_MODE);
|
__SHIFTIN(0x3, HW_SSP_CTRL1_SSP_MODE);
|
||||||
SSP_WR(sc, HW_SSP_CTRL1, reg);
|
SSP_WR(sc, HW_SSP_CTRL1, reg);
|
||||||
|
|
||||||
@ -575,7 +692,7 @@ issp_init(struct issp_softc *sc)
|
|||||||
*
|
*
|
||||||
* SSP_SCK is calculated as: SSP_CLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE))
|
* SSP_SCK is calculated as: SSP_CLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE))
|
||||||
*
|
*
|
||||||
* issp_set_sck find the most suitable CLOCK_DIVIDE and CLOCK_RATE register
|
* issp_set_sck finds the most suitable CLOCK_DIVIDE and CLOCK_RATE register
|
||||||
* values for the target clock rate by iterating through all possible register
|
* values for the target clock rate by iterating through all possible register
|
||||||
* values.
|
* values.
|
||||||
*/
|
*/
|
||||||
@ -612,3 +729,280 @@ out:
|
|||||||
|
|
||||||
return SSP_CLK / (div * (1 + rate));
|
return SSP_CLK / (div * (1 + rate));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQ from DMA.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
issp_dma_intr(void *arg)
|
||||||
|
{
|
||||||
|
issp_softc_t sc = arg;
|
||||||
|
unsigned int dma_err;
|
||||||
|
|
||||||
|
dma_err = apbdma_intr_status(sc->sc_dmac, sc->dma_channel);
|
||||||
|
|
||||||
|
if (dma_err) {
|
||||||
|
apbdma_ack_error_intr(sc->sc_dmac, sc->dma_channel);
|
||||||
|
} else {
|
||||||
|
apbdma_ack_intr(sc->sc_dmac, sc->dma_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
|
||||||
|
sc->sc_dma_error = dma_err;
|
||||||
|
sc->sc_state = SSP_STATE_IDLE;
|
||||||
|
|
||||||
|
/* Signal thread that interrupt was handled. */
|
||||||
|
cv_signal(&sc->sc_intr_cv);
|
||||||
|
|
||||||
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
/* Return 1 to acknowledge IRQ. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQ from SSP block.
|
||||||
|
*
|
||||||
|
* When SSP receives IRQ it terminates ongoing DMA transfer by issuing DMATERM
|
||||||
|
* signal to DMA block.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
issp_error_intr(void *arg)
|
||||||
|
{
|
||||||
|
issp_softc_t sc = arg;
|
||||||
|
|
||||||
|
mutex_enter(&sc->sc_lock);
|
||||||
|
|
||||||
|
sc->sc_irq_error =
|
||||||
|
SSP_RD(sc, HW_SSP_CTRL1) & HW_SSP_CTRL1_IRQ_MASK;
|
||||||
|
|
||||||
|
issp_ack_intr(sc);
|
||||||
|
|
||||||
|
mutex_exit(&sc->sc_lock);
|
||||||
|
|
||||||
|
/* Return 1 to acknowledge IRQ. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acknowledge SSP error IRQ.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
issp_ack_intr(struct issp_softc *sc)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Acknowledge all IRQ's. */
|
||||||
|
SSP_WR(sc, HW_SSP_CTRL1_CLR, HW_SSP_CTRL1_IRQ_MASK);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up multi block DMA transfer.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
issp_create_dma_cmd_list_multi(issp_softc_t sc, void *dma_chain,
|
||||||
|
struct sdmmc_command *cmd)
|
||||||
|
{
|
||||||
|
apbdma_command_t dma_cmd;
|
||||||
|
int blocks;
|
||||||
|
int nblk;
|
||||||
|
|
||||||
|
blocks = cmd->c_datalen / cmd->c_blklen;
|
||||||
|
nblk = 0;
|
||||||
|
dma_cmd = dma_chain;
|
||||||
|
|
||||||
|
/* HEAD */
|
||||||
|
apbdma_cmd_buf(&dma_cmd[nblk], cmd->c_blklen * nblk, cmd->c_dmamap);
|
||||||
|
apbdma_cmd_chain(&dma_cmd[nblk], &dma_cmd[nblk+1], dma_chain,
|
||||||
|
sc->sc_dmamp);
|
||||||
|
|
||||||
|
dma_cmd[nblk].control =
|
||||||
|
__SHIFTIN(cmd->c_blklen, APBDMA_CMD_XFER_COUNT) |
|
||||||
|
__SHIFTIN(3, APBDMA_CMD_CMDPIOWORDS) | APBDMA_CMD_HALTONTERMINATE |
|
||||||
|
APBDMA_CMD_CHAIN;
|
||||||
|
|
||||||
|
if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
|
||||||
|
dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |=
|
||||||
|
HW_SSP_CTRL0_IGNORE_CRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_DATA_XFER |
|
||||||
|
__SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
|
||||||
|
HW_SSP_CTRL0_WAIT_FOR_IRQ |
|
||||||
|
__SHIFTIN(cmd->c_datalen, HW_SSP_CTRL0_XFER_COUNT);
|
||||||
|
|
||||||
|
if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
|
||||||
|
dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |=
|
||||||
|
HW_SSP_CTRL0_GET_RESP;
|
||||||
|
if (ISSET(cmd->c_flags, SCF_RSP_136)) {
|
||||||
|
dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |=
|
||||||
|
HW_SSP_CTRL0_LONG_RESP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_ENABLE;
|
||||||
|
|
||||||
|
dma_cmd[nblk].pio_words[PIO_WORD_CMD0] =
|
||||||
|
__SHIFTIN(ffs(cmd->c_blklen) - 1, HW_SSP_CMD0_BLOCK_SIZE) |
|
||||||
|
__SHIFTIN(blocks - 1, HW_SSP_CMD0_BLOCK_COUNT) |
|
||||||
|
__SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
|
||||||
|
|
||||||
|
dma_cmd[nblk].pio_words[PIO_WORD_CMD1] = cmd->c_arg;
|
||||||
|
|
||||||
|
if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
|
||||||
|
dma_cmd[nblk].control |=
|
||||||
|
__SHIFTIN(APBDMA_CMD_DMA_WRITE, APBDMA_CMD_COMMAND);
|
||||||
|
dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_READ;
|
||||||
|
} else {
|
||||||
|
dma_cmd[nblk].control |=
|
||||||
|
__SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
nblk++;
|
||||||
|
|
||||||
|
/* BODY: Build commands for blocks between head and tail, if any. */
|
||||||
|
for (; nblk < blocks - 1; nblk++) {
|
||||||
|
|
||||||
|
apbdma_cmd_buf(&dma_cmd[nblk], cmd->c_blklen * nblk,
|
||||||
|
cmd->c_dmamap);
|
||||||
|
|
||||||
|
apbdma_cmd_chain(&dma_cmd[nblk], &dma_cmd[nblk+1], dma_chain,
|
||||||
|
sc->sc_dmamp);
|
||||||
|
|
||||||
|
dma_cmd[nblk].control =
|
||||||
|
__SHIFTIN(cmd->c_blklen, APBDMA_CMD_XFER_COUNT) |
|
||||||
|
APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_CHAIN;
|
||||||
|
|
||||||
|
if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
|
||||||
|
dma_cmd[nblk].control |=
|
||||||
|
__SHIFTIN(APBDMA_CMD_DMA_WRITE,
|
||||||
|
APBDMA_CMD_COMMAND);
|
||||||
|
} else {
|
||||||
|
dma_cmd[nblk].control |=
|
||||||
|
__SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TAIL
|
||||||
|
*
|
||||||
|
* TODO: Send CMD12/STOP with last DMA command to support
|
||||||
|
* SMC_CAPS_AUTO_STOP.
|
||||||
|
*/
|
||||||
|
apbdma_cmd_buf(&dma_cmd[nblk], cmd->c_blklen * nblk, cmd->c_dmamap);
|
||||||
|
/* next = NULL */
|
||||||
|
dma_cmd[nblk].control =
|
||||||
|
__SHIFTIN(cmd->c_blklen, APBDMA_CMD_XFER_COUNT) |
|
||||||
|
APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_WAIT4ENDCMD |
|
||||||
|
APBDMA_CMD_SEMAPHORE | APBDMA_CMD_IRQONCMPLT;
|
||||||
|
|
||||||
|
if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
|
||||||
|
dma_cmd[nblk].control |= __SHIFTIN(APBDMA_CMD_DMA_WRITE,
|
||||||
|
APBDMA_CMD_COMMAND);
|
||||||
|
} else {
|
||||||
|
dma_cmd[nblk].control |= __SHIFTIN(APBDMA_CMD_DMA_READ,
|
||||||
|
APBDMA_CMD_COMMAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up single block DMA transfer.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
issp_create_dma_cmd_list_single(issp_softc_t sc, void *dma_chain,
|
||||||
|
struct sdmmc_command *cmd)
|
||||||
|
{
|
||||||
|
apbdma_command_t dma_cmd;
|
||||||
|
|
||||||
|
dma_cmd = dma_chain;
|
||||||
|
|
||||||
|
dma_cmd[0].control = __SHIFTIN(cmd->c_datalen, APBDMA_CMD_XFER_COUNT) |
|
||||||
|
__SHIFTIN(3, APBDMA_CMD_CMDPIOWORDS) |
|
||||||
|
APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_WAIT4ENDCMD |
|
||||||
|
APBDMA_CMD_SEMAPHORE | APBDMA_CMD_IRQONCMPLT;
|
||||||
|
|
||||||
|
/* Transfer single block to the beginning of the DMA buffer. */
|
||||||
|
apbdma_cmd_buf(&dma_cmd[0], 0, cmd->c_dmamap);
|
||||||
|
|
||||||
|
if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
|
||||||
|
HW_SSP_CTRL0_IGNORE_CRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
|
||||||
|
HW_SSP_CTRL0_DATA_XFER |
|
||||||
|
__SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
|
||||||
|
HW_SSP_CTRL0_WAIT_FOR_IRQ |
|
||||||
|
__SHIFTIN(cmd->c_datalen, HW_SSP_CTRL0_XFER_COUNT);
|
||||||
|
|
||||||
|
if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_GET_RESP;
|
||||||
|
if (ISSET(cmd->c_flags, SCF_RSP_136)) {
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
|
||||||
|
HW_SSP_CTRL0_LONG_RESP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_ENABLE;
|
||||||
|
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CMD0] =
|
||||||
|
HW_SSP_CMD0_APPEND_8CYC |
|
||||||
|
__SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CMD1] = cmd->c_arg;
|
||||||
|
|
||||||
|
if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
|
||||||
|
dma_cmd[0].control |=
|
||||||
|
__SHIFTIN(APBDMA_CMD_DMA_WRITE, APBDMA_CMD_COMMAND);
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_READ;
|
||||||
|
} else {
|
||||||
|
dma_cmd[0].control |=
|
||||||
|
__SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do DMA PIO (issue CMD). No block transfers.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
issp_create_dma_cmd_list(issp_softc_t sc, void *dma_chain,
|
||||||
|
struct sdmmc_command *cmd)
|
||||||
|
{
|
||||||
|
apbdma_command_t dma_cmd;
|
||||||
|
|
||||||
|
dma_cmd = dma_chain;
|
||||||
|
|
||||||
|
dma_cmd[0].control = __SHIFTIN(3, APBDMA_CMD_CMDPIOWORDS) |
|
||||||
|
APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_WAIT4ENDCMD |
|
||||||
|
APBDMA_CMD_SEMAPHORE | APBDMA_CMD_IRQONCMPLT |
|
||||||
|
__SHIFTIN(APBDMA_CMD_NO_DMA_XFER, APBDMA_CMD_COMMAND);
|
||||||
|
|
||||||
|
if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
|
||||||
|
HW_SSP_CTRL0_IGNORE_CRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
|
||||||
|
__SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
|
||||||
|
HW_SSP_CTRL0_WAIT_FOR_IRQ;
|
||||||
|
|
||||||
|
if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_GET_RESP;
|
||||||
|
if (ISSET(cmd->c_flags, SCF_RSP_136)) {
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
|
||||||
|
HW_SSP_CTRL0_LONG_RESP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_ENABLE;
|
||||||
|
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CMD0] =
|
||||||
|
__SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
|
||||||
|
dma_cmd[0].pio_words[PIO_WORD_CMD1] = cmd->c_arg;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# $Id: IMX23_OLINUXINO,v 1.2 2012/12/16 19:45:52 jkunz Exp $
|
# $Id: IMX23_OLINUXINO,v 1.3 2013/03/03 10:33:56 jkunz Exp $
|
||||||
#
|
#
|
||||||
# IMX23_OLINUXINO -- Olimex i.MX23 OLinuXino kernel configuration file.
|
# IMX23_OLINUXINO -- Olimex i.MX23 OLinuXino kernel configuration file.
|
||||||
#
|
#
|
||||||
@ -7,7 +7,7 @@ include "arch/evbarm/conf/std.imx23_olinuxino"
|
|||||||
|
|
||||||
maxusers 8
|
maxusers 8
|
||||||
|
|
||||||
config netbsd root on ld0a type ?
|
config netbsd root on ? type ?
|
||||||
|
|
||||||
# The main bus device
|
# The main bus device
|
||||||
mainbus0 at root
|
mainbus0 at root
|
||||||
@ -16,10 +16,10 @@ mainbus0 at root
|
|||||||
cpu0 at mainbus?
|
cpu0 at mainbus?
|
||||||
|
|
||||||
# APBH bus
|
# APBH bus
|
||||||
apbh0 at mainbus? base 0x80000000 size 0x00040000
|
apbh0 at mainbus? base 0x80000000 size 0x40000
|
||||||
|
|
||||||
# APBH DMA
|
# APBH DMA
|
||||||
#apbdma0 at apbh? addr 0x80004000 size 0x2000 irq -1
|
apbdma0 at apbh? addr 0x80004000 size 0x2000 irq -1
|
||||||
|
|
||||||
# Interrupt controller
|
# Interrupt controller
|
||||||
icoll0 at apbh? addr 0x80000000 size 0x2000 irq -1
|
icoll0 at apbh? addr 0x80000000 size 0x2000 irq -1
|
||||||
@ -27,13 +27,13 @@ icoll0 at apbh? addr 0x80000000 size 0x2000 irq -1
|
|||||||
# Synchronous serial port for SD/MMC
|
# Synchronous serial port for SD/MMC
|
||||||
ssp0 at apbh? addr 0x80010000 size 0x2000 irq 15
|
ssp0 at apbh? addr 0x80010000 size 0x2000 irq 15
|
||||||
sdmmc* at ssp?
|
sdmmc* at ssp?
|
||||||
ld* at sdmmc?
|
ld* at sdmmc?
|
||||||
|
|
||||||
# APBX bus
|
# APBX bus
|
||||||
apbx0 at mainbus? base 0x80040000 size 0x00040000
|
apbx0 at mainbus? base 0x80040000 size 0x40000
|
||||||
|
|
||||||
# APBX DMA
|
# APBX DMA
|
||||||
#apbdma1 at apbx? addr 0x80024000 size 0x2000 irq -1
|
apbdma1 at apbx? addr 0x80024000 size 0x2000 irq -1
|
||||||
|
|
||||||
# Timers and rotary decoder
|
# Timers and rotary decoder
|
||||||
timrot0 at apbx? addr 0x80068020 size 0x20 irq 28
|
timrot0 at apbx? addr 0x80068020 size 0x20 irq 28
|
||||||
@ -50,4 +50,8 @@ options HZ=100
|
|||||||
file-system FFS
|
file-system FFS
|
||||||
file-system EXT2FS
|
file-system EXT2FS
|
||||||
file-system MSDOSFS
|
file-system MSDOSFS
|
||||||
|
file-system KERNFS
|
||||||
|
file-system PROCFS
|
||||||
|
file-system PTYFS
|
||||||
|
|
||||||
|
pseudo-device pty # pseudo-terminals
|
||||||
|
Loading…
Reference in New Issue
Block a user