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.
|
||||
@ -30,53 +30,25 @@
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <arm/imx/imx23_apbdma.h>
|
||||
#include <arm/imx/imx23_apbdmareg.h>
|
||||
#include <arm/imx/imx23_apbdmavar.h>
|
||||
#include <arm/imx/imx23_apbhdmareg.h>
|
||||
#include <arm/imx/imx23_apbxdmareg.h>
|
||||
#include <arm/imx/imx23_apbdma.h>
|
||||
#include <arm/imx/imx23var.h>
|
||||
|
||||
static int apbdma_match(device_t, cfdata_t, void *);
|
||||
static void apbdma_attach(device_t, device_t, void *);
|
||||
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,
|
||||
sizeof(struct apbdma_softc),
|
||||
apbdma_match,
|
||||
@ -88,6 +60,14 @@ CFATTACH_DECL3_NEW(apbdma,
|
||||
0);
|
||||
|
||||
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
|
||||
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 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;
|
||||
// struct imx23_dma_channel *chan;
|
||||
// int i;
|
||||
int error;
|
||||
|
||||
// if (apbdma_attached)
|
||||
// return;
|
||||
if ((strncmp(device_xname(parent), "apbh", 4) == 0) &&
|
||||
(apbdma_attached & F_AHBH_DMA))
|
||||
return;
|
||||
if ((strncmp(device_xname(parent), "apbx", 4) == 0) &&
|
||||
(apbdma_attached & F_AHBX_DMA))
|
||||
return;
|
||||
|
||||
sc->sc_dev = self;
|
||||
sc->sc_iot = aa->aa_iot;
|
||||
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,
|
||||
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");
|
||||
return;
|
||||
}
|
||||
|
||||
error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1,
|
||||
PAGE_SIZE, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamp);
|
||||
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 (strncmp(device_xname(parent), "apbh", 4) == 0)
|
||||
sc->flags = F_AHBH_DMA;
|
||||
|
||||
if (aa->aa_addr == HW_APBXDMA_BASE && aa->aa_size == HW_APBXDMA_SIZE) {
|
||||
sc->sc_channel = kmem_alloc(sizeof(struct imx23_dma_channel)
|
||||
* APBX_DMA_N_CHANNELS, KM_SLEEP);
|
||||
sc->n_channel = APBX_DMA_N_CHANNELS;
|
||||
}
|
||||
if (strncmp(device_xname(parent), "apbx", 4) == 0)
|
||||
sc->flags = F_AHBX_DMA;
|
||||
|
||||
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_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");
|
||||
|
||||
@ -195,85 +152,242 @@ apbdma_reset(struct apbdma_softc *sc)
|
||||
* Prepare for soft-reset by making sure that SFTRST is not currently
|
||||
* 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. */
|
||||
loop = 0;
|
||||
while ((DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
|
||||
(loop < APBDMA_SOFT_RST_LOOP))
|
||||
{
|
||||
while ((DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
|
||||
(loop < APBDMA_SOFT_RST_LOOP))
|
||||
loop++;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
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. */
|
||||
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. */
|
||||
DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
|
||||
DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
|
||||
|
||||
loop = 0;
|
||||
while ((DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
|
||||
(loop < APBDMA_SOFT_RST_LOOP))
|
||||
{
|
||||
while ((DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
|
||||
(loop < APBDMA_SOFT_RST_LOOP))
|
||||
loop++;
|
||||
}
|
||||
|
||||
DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
|
||||
|
||||
/* Wait until clock is in the NON-gated state. */
|
||||
while (DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE);
|
||||
DMA_WR(sc, HW_APB_CTRL0_CLR, 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 *
|
||||
apbdma_dmamem_alloc(device_t dmac, int channel, bus_size_t size)
|
||||
static void
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, segs, 1,
|
||||
&rsegs, BUS_DMA_NOWAIT);
|
||||
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);
|
||||
|
||||
error = bus_dmamem_map(sc->sc_dmat, segs, 1, size, &ptr,
|
||||
BUS_DMA_NOWAIT);
|
||||
if (error)
|
||||
goto free;
|
||||
//XXX:
|
||||
printf("segs[0].ds_addr=%lx, segs[0].ds_len=%lx, ptr=%p\n", segs[0].ds_addr, segs[0].ds_len, ptr);
|
||||
|
||||
error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, ptr, size, NULL,
|
||||
BUS_DMA_NOWAIT | BUS_DMA_WRITE);
|
||||
if (error)
|
||||
goto unmap;
|
||||
|
||||
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, size,
|
||||
BUS_DMASYNC_PREWRITE);
|
||||
|
||||
// return usable memory
|
||||
unmap:
|
||||
bus_dmamem_unmap(sc->sc_dmat, ptr, size);
|
||||
free:
|
||||
bus_dmamem_free(sc->sc_dmat, segs, 1);
|
||||
out:
|
||||
return NULL;
|
||||
if (sc->flags & F_AHBH_DMA) {
|
||||
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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Chain DMA commands together.
|
||||
*
|
||||
* Set src->next point to trg's physical DMA mapped address.
|
||||
*/
|
||||
void
|
||||
apbdma_cmd_chain(apbdma_command_t src, apbdma_command_t trg, void *buf,
|
||||
bus_dmamap_t dmap)
|
||||
{
|
||||
int i;
|
||||
bus_size_t daddr;
|
||||
bus_addr_t trg_offset;
|
||||
|
||||
trg_offset = (bus_addr_t)trg - (bus_addr_t)buf;
|
||||
daddr = 0;
|
||||
|
||||
for (i = 0; i < dmap->dm_nsegs; i++) {
|
||||
daddr += dmap->dm_segs[i].ds_len;
|
||||
if (trg_offset < daddr) {
|
||||
src->next = (void *)(dmap->dm_segs[i].ds_addr +
|
||||
(trg_offset - (daddr - dmap->dm_segs[i].ds_len)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@ -50,4 +50,20 @@
|
||||
#define HW_APB_CTRL0_CLKGATE __BIT(30)
|
||||
#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_ */
|
||||
|
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.
|
||||
@ -122,6 +122,30 @@
|
||||
#define HW_APBH_DEVSEL_CH1 __BITS(7, 4)
|
||||
#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.
|
||||
*/
|
||||
|
@ -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.
|
||||
@ -34,7 +34,31 @@
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#define HW_APBXDMA_BASE 0x80024000
|
||||
#define HW_APBXDMA_SIZE 0x2000 /* 8 kB */
|
||||
#define HW_APBXDMA_BASE 0x80024000
|
||||
#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_ */
|
||||
|
@ -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.
|
||||
@ -63,7 +63,7 @@
|
||||
* suspended. */
|
||||
#define IRQ_GPMI_DMA 13 /* From DMA channel for GPMI */
|
||||
#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_GPIO1 17 /* GPIO bank 1 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.
|
||||
@ -33,10 +33,16 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/mutex.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/imx23var.h>
|
||||
|
||||
@ -46,15 +52,33 @@
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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_tag_t sc_iot;
|
||||
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 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_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. */
|
||||
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_rod(sdmmc_chipset_handle_t, int);
|
||||
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_intr_ack(sdmmc_chipset_handle_t);
|
||||
|
||||
@ -102,7 +135,8 @@ CFATTACH_DECL3_NEW(ssp,
|
||||
issp_activate,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
0
|
||||
);
|
||||
|
||||
#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_MAX 48000 /* 48 MHz */
|
||||
|
||||
#define SSP_BUSY (HW_SSP_STATUS_CMD_BUSY | \
|
||||
HW_SSP_STATUS_DATA_BUSY | \
|
||||
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
|
||||
/* DATA_TIMEOUT is calculated as: * (1 / SSP_CLK) * (DATA_TIMEOUT * 4096) */
|
||||
#define DATA_TIMEOUT 0x4240 /* 723ms */
|
||||
|
||||
#define BUS_WIDTH_1_BIT 0x0
|
||||
#define BUS_WIDTH_4_BIT 0x1
|
||||
#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
|
||||
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_attach_args *aa = aux;
|
||||
struct sdmmcbus_attach_args saa;
|
||||
static int issp_attached = 0;
|
||||
|
||||
if (issp_attached)
|
||||
return;
|
||||
static int ssp_attached = 0;
|
||||
int error;
|
||||
void *intr;
|
||||
|
||||
sc->sc_dev = self;
|
||||
sc->sc_iot = aa->aa_iot;
|
||||
sc->dmac = sc_parent->dmac;
|
||||
sc->sc_dmat = aa->aa_dmat;
|
||||
|
||||
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 bus space\n");
|
||||
/* Test if device instance is already attached. */
|
||||
if (aa->aa_addr == HW_SSP1_BASE && ISSET(ssp_attached, SSP1_ATTACHED)) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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_MINOR));
|
||||
|
||||
/* Attach sdmmc to ssp bus. */
|
||||
saa.saa_busname = "sdmmc";
|
||||
saa.saa_sct = &issp_functions;
|
||||
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_clkmin = SSP_CLK_MIN;
|
||||
saa.saa_clkmax = SSP_CLK_MAX;
|
||||
/* Add SMC_CAPS_DMA capability when DMA funtionality is implemented. */
|
||||
saa.saa_caps = SMC_CAPS_4BIT_MODE | SMC_CAPS_SINGLE_ONLY;
|
||||
saa.saa_caps = SMC_CAPS_DMA | SMC_CAPS_4BIT_MODE |
|
||||
SMC_CAPS_MULTI_SEG_DMA;
|
||||
|
||||
sc->sc_sdmmc = config_found(sc->sc_dev, &saa, NULL);
|
||||
if (sc->sc_sdmmc == NULL) {
|
||||
@ -193,7 +331,11 @@ issp_attach(device_t parent, device_t self, void *aux)
|
||||
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;
|
||||
}
|
||||
@ -211,16 +353,14 @@ static int
|
||||
issp_host_reset(sdmmc_chipset_handle_t sch)
|
||||
{
|
||||
struct issp_softc *sc = sch;
|
||||
|
||||
issp_reset(sc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
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;
|
||||
}
|
||||
|
||||
@ -237,29 +377,6 @@ issp_host_maxblklen(sdmmc_chipset_handle_t sch)
|
||||
static int
|
||||
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;
|
||||
}
|
||||
|
||||
@ -283,13 +400,22 @@ issp_bus_clock(sdmmc_chipset_handle_t sch, int clock)
|
||||
struct issp_softc *sc = sch;
|
||||
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
|
||||
* requested. */
|
||||
if (sck != clock * 1000)
|
||||
aprint_normal_dev(sc->sc_dev, "requested clock %dHz, "
|
||||
"but got %dHz\n", clock * 1000, sck);
|
||||
/* Notify user if we didn't get the exact clock rate from SSP that was
|
||||
* requested from the SDMMC subsystem. */
|
||||
if (sck != clock * 1000) {
|
||||
sck = sck / 1000;
|
||||
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;
|
||||
}
|
||||
@ -298,27 +424,21 @@ static int
|
||||
issp_bus_width(sdmmc_chipset_handle_t sch, int width)
|
||||
{
|
||||
struct issp_softc *sc = sch;
|
||||
uint32_t reg;
|
||||
|
||||
reg = SSP_RD(sc, HW_SSP_CTRL0);
|
||||
reg &= ~(HW_SSP_CTRL0_BUS_WIDTH);
|
||||
|
||||
switch(width) {
|
||||
case(1):
|
||||
reg |= __SHIFTIN(BUS_WIDTH_1_BIT, HW_SSP_CTRL0_BUS_WIDTH);
|
||||
sc->sc_bus_width = BUS_WIDTH_1_BIT;
|
||||
break;
|
||||
case(4):
|
||||
reg |= __SHIFTIN(BUS_WIDTH_4_BIT, HW_SSP_CTRL0_BUS_WIDTH);
|
||||
sc->sc_bus_width = BUS_WIDTH_4_BIT;
|
||||
break;
|
||||
case(8):
|
||||
reg |= __SHIFTIN(BUS_WIDTH_8_BIT, HW_SSP_CTRL0_BUS_WIDTH);
|
||||
sc->sc_bus_width = BUS_WIDTH_8_BIT;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSP_WR(sc, HW_SSP_CTRL0, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -332,112 +452,104 @@ issp_bus_rod(sdmmc_chipset_handle_t sch, int rod)
|
||||
static void
|
||||
issp_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
|
||||
{
|
||||
struct issp_softc *sc = sch;
|
||||
uint32_t reg;
|
||||
uint32_t do_blkio;
|
||||
uint32_t i;
|
||||
issp_softc_t sc = sch;
|
||||
void *dma_chain;
|
||||
int error;
|
||||
|
||||
do_blkio = 0;
|
||||
|
||||
/* Wait until SSP done. (data I/O error + retry...) */
|
||||
while (SSP_RD(sc, HW_SSP_STATUS) & SSP_BUSY)
|
||||
;
|
||||
|
||||
/* 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);
|
||||
/* SSP does not support over 64k transfer size. */
|
||||
if (cmd->c_data != NULL && cmd->c_datalen > MAX_TRANSFER_SIZE) {
|
||||
aprint_error_dev(sc->sc_dev, "transfer size over %d: %d\n",
|
||||
MAX_TRANSFER_SIZE, cmd->c_datalen);
|
||||
cmd->c_error = ENODEV;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If CMD does not need CRC validation, tell it to SSP. */
|
||||
if (ISSET(cmd->c_flags, SCF_RSP_CRC))
|
||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_IGNORE_CRC);
|
||||
else
|
||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_IGNORE_CRC);
|
||||
/* Map dma_chain to point allocated previously allocated DMA chain. */
|
||||
error = bus_dmamem_map(sc->sc_dmat, sc->sc_ds, 1, sc->sc_chnsiz,
|
||||
&dma_chain, BUS_DMA_NOWAIT);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "bus_dmamem_map: %d\n", error);
|
||||
cmd->c_error = error;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set command. */
|
||||
SSP_WR(sc, HW_SSP_CMD0_CLR, HW_SSP_CMD0_CMD);
|
||||
SSP_WR(sc, HW_SSP_CMD0_SET,
|
||||
__SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD));
|
||||
error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, dma_chain,
|
||||
sc->sc_chnsiz, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "bus_dmamap_load: %d\n", error);
|
||||
cmd->c_error = error;
|
||||
goto dmamem_unmap;
|
||||
}
|
||||
|
||||
/* Set command argument. */
|
||||
SSP_WR(sc, HW_SSP_CMD1, cmd->c_arg);
|
||||
memset(dma_chain, 0, sc->sc_chnsiz);
|
||||
|
||||
/* Is data to be transferred? */
|
||||
if (cmd->c_datalen > 0 && cmd->c_data != NULL) {
|
||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_DATA_XFER);
|
||||
/* Transfer XFER_COUNT of 8-bit words. */
|
||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_XFER_COUNT);
|
||||
SSP_WR(sc, HW_SSP_CTRL0_SET,
|
||||
__SHIFTIN(cmd->c_datalen, HW_SSP_CTRL0_XFER_COUNT));
|
||||
|
||||
/* 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);
|
||||
}
|
||||
/* Setup DMA command chain.*/
|
||||
if (cmd->c_data != NULL && (cmd->c_datalen / cmd->c_blklen) > 1) {
|
||||
/* Multi block transfer. */
|
||||
issp_create_dma_cmd_list_multi(sc, dma_chain, cmd);
|
||||
} else if (cmd->c_data != NULL && cmd->c_datalen) {
|
||||
/* Single block transfer. */
|
||||
issp_create_dma_cmd_list_single(sc, dma_chain, cmd);
|
||||
} else {
|
||||
/* No data to be transferred. */
|
||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_DATA_XFER);
|
||||
/* Only command, no data. */
|
||||
issp_create_dma_cmd_list(sc, dma_chain, cmd);
|
||||
}
|
||||
|
||||
/* Run the command. */
|
||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_RUN);
|
||||
/* Tell DMA controller where it can find just initialized DMA chain. */
|
||||
apbdma_chan_set_chain(sc->sc_dmac, sc->dma_channel, sc->sc_dmamp);
|
||||
|
||||
if (ISSET(do_blkio, BLKIO_RD)) {
|
||||
for (i = 0; i < cmd->c_datalen / 4; i++) {
|
||||
/* Wait until data arrives to FIFO. */
|
||||
while (SSP_RD(sc, HW_SSP_STATUS)
|
||||
& HW_SSP_STATUS_FIFO_EMPTY) {
|
||||
/* Abort if error while waiting. */
|
||||
if (SSP_RD(sc, HW_SSP_STATUS) & SSP_RUN_ERR) {
|
||||
aprint_normal_dev(sc->sc_dev,
|
||||
"RD_ERR: %x\n",
|
||||
SSP_RD(sc, HW_SSP_STATUS));
|
||||
cmd->c_error = 1;
|
||||
goto pioerr;
|
||||
}
|
||||
}
|
||||
*((uint32_t *)cmd->c_data+i) = SSP_RD(sc, HW_SSP_DATA);
|
||||
/* Synchronize command chain before DMA controller accesses it. */
|
||||
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, sc->sc_chnsiz,
|
||||
BUS_DMASYNC_PREWRITE);
|
||||
|
||||
sc->sc_state = SSP_STATE_DMA;
|
||||
sc->sc_irq_error = 0;
|
||||
sc->sc_dma_error = 0;
|
||||
cmd->c_error = 0;
|
||||
|
||||
mutex_enter(&sc->sc_lock);
|
||||
|
||||
/* Run DMA command chain. */
|
||||
apbdma_run(sc->sc_dmac, sc->dma_channel);
|
||||
|
||||
/* 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)) {
|
||||
for (i = 0; i < (cmd->c_datalen / 4); i++) {
|
||||
while (SSP_RD(sc, HW_SSP_STATUS)
|
||||
& HW_SSP_STATUS_FIFO_FULL) {
|
||||
/* 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));
|
||||
else if (sc->sc_dma_error == DMA_IRQ_BUS_ERROR) {
|
||||
aprint_error_dev(sc->sc_dev, "DMA_IRQ_BUS_ERROR: %d\n",
|
||||
sc->sc_irq_error);
|
||||
cmd->c_error = sc->sc_dma_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait until SSP is done. */
|
||||
while (SSP_RD(sc, HW_SSP_STATUS) & SSP_BUSY)
|
||||
;
|
||||
if (sc->sc_irq_error) {
|
||||
/* 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. */
|
||||
reg = SSP_RD(sc, HW_SSP_STATUS);
|
||||
if (reg & SSP_RUN_ERR)
|
||||
cmd->c_error = reg & SSP_RUN_ERR;
|
||||
/* Shift unsigned error code so it fits nicely to signed int. */
|
||||
cmd->c_error = sc->sc_irq_error >> 8;
|
||||
}
|
||||
|
||||
/* Read response if such was requested. */
|
||||
/* Check reponse from the card if such was requested. */
|
||||
if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
|
||||
cmd->c_resp[0] = SSP_RD(sc, HW_SSP_SDRESP0);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -465,10 +582,7 @@ static void
|
||||
issp_card_enable_intr(sdmmc_chipset_handle_t sch, int irq)
|
||||
{
|
||||
struct issp_softc *sc = sch;
|
||||
|
||||
aprint_normal_dev(sc->sc_dev,
|
||||
"issp_card_enable_intr NOT IMPLEMENTED!\n");
|
||||
|
||||
aprint_error_dev(sc->sc_dev, "issp_card_enable_intr not implemented\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -476,9 +590,7 @@ static void
|
||||
issp_card_intr_ack(sdmmc_chipset_handle_t sch)
|
||||
{
|
||||
struct issp_softc *sc = sch;
|
||||
|
||||
aprint_normal_dev(sc->sc_dev, "issp_card_intr_ack NOT IMPLEMENTED!\n");
|
||||
|
||||
aprint_error_dev(sc->sc_dev, "issp_card_intr_ack not implemented\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -528,13 +640,6 @@ issp_reset(struct issp_softc *sc)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DATA_TIMEOUT is calculated as:
|
||||
* (1 / SSP_CLK) * (DATA_TIMEOUT * 4096)
|
||||
*/
|
||||
#define DATA_TIMEOUT 0x4240 /* 723ms */
|
||||
|
||||
/*
|
||||
* Initialize SSP controller to SD/MMC mode.
|
||||
*/
|
||||
@ -543,27 +648,39 @@ issp_init(struct issp_softc *sc)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* Initial data bus width is 1-bit. */
|
||||
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 |= __SHIFTIN(BUS_WIDTH_1_BIT, HW_SSP_CTRL0_BUS_WIDTH) |
|
||||
HW_SSP_CTRL0_WAIT_FOR_IRQ | HW_SSP_CTRL0_ENABLE;
|
||||
SSP_WR(sc, HW_SSP_CTRL0, reg);
|
||||
sc->sc_bus_width = BUS_WIDTH_1_BIT;
|
||||
|
||||
/* Set data timeout. */
|
||||
reg = SSP_RD(sc, HW_SSP_TIMING);
|
||||
reg &= ~(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. */
|
||||
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);
|
||||
/* 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_POLARITY |
|
||||
__SHIFTIN(0x7, HW_SSP_CTRL1_WORD_LENGTH) |
|
||||
reg |= __SHIFTIN(0x7, HW_SSP_CTRL1_WORD_LENGTH) |
|
||||
__SHIFTIN(0x3, HW_SSP_CTRL1_SSP_MODE);
|
||||
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))
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
@ -612,3 +729,280 @@ out:
|
||||
|
||||
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.
|
||||
#
|
||||
@ -7,7 +7,7 @@ include "arch/evbarm/conf/std.imx23_olinuxino"
|
||||
|
||||
maxusers 8
|
||||
|
||||
config netbsd root on ld0a type ?
|
||||
config netbsd root on ? type ?
|
||||
|
||||
# The main bus device
|
||||
mainbus0 at root
|
||||
@ -16,10 +16,10 @@ mainbus0 at root
|
||||
cpu0 at mainbus?
|
||||
|
||||
# APBH bus
|
||||
apbh0 at mainbus? base 0x80000000 size 0x00040000
|
||||
apbh0 at mainbus? base 0x80000000 size 0x40000
|
||||
|
||||
# APBH DMA
|
||||
#apbdma0 at apbh? addr 0x80004000 size 0x2000 irq -1
|
||||
apbdma0 at apbh? addr 0x80004000 size 0x2000 irq -1
|
||||
|
||||
# Interrupt controller
|
||||
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
|
||||
ssp0 at apbh? addr 0x80010000 size 0x2000 irq 15
|
||||
sdmmc* at ssp?
|
||||
ld* at sdmmc?
|
||||
ld* at sdmmc?
|
||||
|
||||
# APBX bus
|
||||
apbx0 at mainbus? base 0x80040000 size 0x00040000
|
||||
apbx0 at mainbus? base 0x80040000 size 0x40000
|
||||
|
||||
# APBX DMA
|
||||
#apbdma1 at apbx? addr 0x80024000 size 0x2000 irq -1
|
||||
apbdma1 at apbx? addr 0x80024000 size 0x2000 irq -1
|
||||
|
||||
# Timers and rotary decoder
|
||||
timrot0 at apbx? addr 0x80068020 size 0x20 irq 28
|
||||
@ -50,4 +50,8 @@ options HZ=100
|
||||
file-system FFS
|
||||
file-system EXT2FS
|
||||
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