Contribution from Petri Laakso: Initial support for SD card controller.
iMX233-OLinuXino can now boot and run from its own SD card.
This commit is contained in:
parent
8a7d722d8b
commit
c332c3c01b
|
@ -1,4 +1,4 @@
|
|||
/* $Id: imx23_ssp.c,v 1.1 2012/11/20 19:06:14 jkunz Exp $ */
|
||||
/* $Id: imx23_ssp.c,v 1.2 2012/12/16 19:45:52 jkunz Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
||||
|
@ -33,15 +33,10 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/cpu.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <arm/pic/picvar.h>
|
||||
|
||||
#include <arm/imx/imx23_apbdma.h>
|
||||
#include <arm/imx/imx23_icollreg.h>
|
||||
#include <arm/imx/imx23_sspreg.h>
|
||||
#include <arm/imx/imx23var.h>
|
||||
|
||||
|
@ -50,7 +45,7 @@
|
|||
#include <dev/sdmmc/sdmmcvar.h>
|
||||
|
||||
/*
|
||||
* SD/MMC host controller driver for i.MX233.
|
||||
* SD/MMC host controller driver for i.MX23.
|
||||
*/
|
||||
|
||||
struct issp_softc {
|
||||
|
@ -65,7 +60,11 @@ static int issp_match(device_t, cfdata_t, void *);
|
|||
static void issp_attach(device_t, device_t, void *);
|
||||
static int issp_activate(device_t, enum devact);
|
||||
|
||||
/* sdmmc chip function prototypes. */
|
||||
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);
|
||||
|
||||
/* sdmmc(4) driver chip function prototypes. */
|
||||
static int issp_host_reset(sdmmc_chipset_handle_t);
|
||||
static uint32_t issp_host_ocr(sdmmc_chipset_handle_t);
|
||||
static int issp_host_maxblklen(sdmmc_chipset_handle_t);
|
||||
|
@ -80,23 +79,6 @@ static void issp_exec_command(sdmmc_chipset_handle_t,
|
|||
static void issp_card_enable_intr(sdmmc_chipset_handle_t, int);
|
||||
static void issp_card_intr_ack(sdmmc_chipset_handle_t);
|
||||
|
||||
/* Used from the above callbacks. */
|
||||
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);
|
||||
|
||||
#define SSP_SOFT_RST_LOOP 455 /* At least 1 us ... */
|
||||
|
||||
CFATTACH_DECL3_NEW(ssp,
|
||||
sizeof(struct issp_softc),
|
||||
issp_match,
|
||||
issp_attach,
|
||||
NULL,
|
||||
issp_activate,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
|
||||
static struct sdmmc_chip_functions issp_functions = {
|
||||
.host_reset = issp_host_reset,
|
||||
.host_ocr = issp_host_ocr,
|
||||
|
@ -112,22 +94,45 @@ static struct sdmmc_chip_functions issp_functions = {
|
|||
.card_intr_ack = issp_card_intr_ack
|
||||
};
|
||||
|
||||
#define SSP_READ(sc, reg) \
|
||||
CFATTACH_DECL3_NEW(ssp,
|
||||
sizeof(struct issp_softc),
|
||||
issp_match,
|
||||
issp_attach,
|
||||
NULL,
|
||||
issp_activate,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
|
||||
#define SSP_SOFT_RST_LOOP 455 /* At least 1 us ... */
|
||||
|
||||
#define SSP_RD(sc, reg) \
|
||||
bus_space_read_4(sc->sc_iot, sc->sc_hdl, (reg))
|
||||
#define SSP_WRITE(sc, reg, val) \
|
||||
#define SSP_WR(sc, reg, val) \
|
||||
bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val))
|
||||
|
||||
#define SSP_CLK 96000000 /* CLK_SSP from PLL is 96 MHz */
|
||||
#define SSP_CLK_MIN 2 /* 2 kHz */
|
||||
#define SSP_CLK_MAX 48000 /* 48 MHz */
|
||||
/* SSP_CMD_TIMEOUT is calculated as (1.0/SSP_SCK)*(SSP_CMD_TIMEOUT*4096) */
|
||||
#define SSP_CMD_TIMEOUT 0xffff /* 2.8 seconds. */
|
||||
#define SSP_STATUS_ERR (HW_SSP_STATUS_RESP_CRC_ERR | \
|
||||
#define SSP_CLK 96000000 /* CLK_SSP from PLL is 96 MHz */
|
||||
#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
|
||||
|
||||
#define BUS_WIDTH_1_BIT 0x0
|
||||
#define BUS_WIDTH_4_BIT 0x1
|
||||
#define BUS_WIDTH_8_BIT 0x2
|
||||
|
||||
static int
|
||||
issp_match(device_t parent, cfdata_t match, void *aux)
|
||||
{
|
||||
|
@ -145,25 +150,18 @@ issp_match(device_t parent, cfdata_t match, void *aux)
|
|||
static void
|
||||
issp_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
static int issp_attached = 0;
|
||||
struct issp_softc *sc = device_private(self);
|
||||
struct apb_softc *scp = device_private(parent);
|
||||
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;
|
||||
|
||||
//XXX:
|
||||
if (scp == NULL)
|
||||
printf("ISSP_ATTACH: scp == NULL\n");
|
||||
if (scp->dmac == NULL)
|
||||
printf("ISSP_ATTACH: scp->dmac == NULL\n");
|
||||
|
||||
sc->sc_dev = self;
|
||||
sc->sc_iot = aa->aa_iot;
|
||||
sc->dmac = scp->dmac;
|
||||
sc->dmac = sc_parent->dmac;
|
||||
|
||||
if (bus_space_map(sc->sc_iot,
|
||||
aa->aa_addr, aa->aa_size, 0, &(sc->sc_hdl))) {
|
||||
|
@ -172,9 +170,9 @@ issp_attach(device_t parent, device_t self, void *aux)
|
|||
}
|
||||
|
||||
issp_reset(sc);
|
||||
issp_init(sc);
|
||||
issp_init(sc);
|
||||
|
||||
uint32_t issp_vers = SSP_READ(sc, HW_SSP_VERSION);
|
||||
uint32_t issp_vers = SSP_RD(sc, HW_SSP_VERSION);
|
||||
aprint_normal(": SSP Block v%" __PRIuBIT ".%" __PRIuBIT "\n",
|
||||
__SHIFTOUT(issp_vers, HW_SSP_VERSION_MAJOR),
|
||||
__SHIFTOUT(issp_vers, HW_SSP_VERSION_MINOR));
|
||||
|
@ -186,7 +184,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;
|
||||
saa.saa_caps = SMC_CAPS_4BIT_MODE | SMC_CAPS_DMA;
|
||||
/* Add SMC_CAPS_DMA capability when DMA funtionality is implemented. */
|
||||
saa.saa_caps = SMC_CAPS_4BIT_MODE | SMC_CAPS_SINGLE_ONLY;
|
||||
|
||||
sc->sc_sdmmc = config_found(sc->sc_dev, &saa, NULL);
|
||||
if (sc->sc_sdmmc == NULL) {
|
||||
|
@ -228,7 +227,6 @@ issp_host_ocr(sdmmc_chipset_handle_t sch)
|
|||
static int
|
||||
issp_host_maxblklen(sdmmc_chipset_handle_t sch)
|
||||
{
|
||||
/* XXX: This value was made up. */
|
||||
return 512;
|
||||
}
|
||||
|
||||
|
@ -242,10 +240,10 @@ issp_card_detect(sdmmc_chipset_handle_t sch)
|
|||
/* struct issp_softc *sc = sch;
|
||||
*
|
||||
* In the perfect world I'll just:
|
||||
* return SSP_READ(sc, HW_SSP_STATUS) & HW_SSP_STATUS_CARD_DETECT;
|
||||
* return SSP_RD(sc, HW_SSP_STATUS) & HW_SSP_STATUS_CARD_DETECT;
|
||||
* and call it a day.
|
||||
*
|
||||
* But on i.MX233 OLinuXino MAXI, SSP1_DETECT is not used for the SD
|
||||
* 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
|
||||
|
@ -258,7 +256,7 @@ issp_card_detect(sdmmc_chipset_handle_t sch)
|
|||
* #if BOARDTYPE == MAXI (Possibly MINI & MICRO)
|
||||
* return GPIO_READ(PIN_125) & PIN_125
|
||||
* #else
|
||||
* return SSP_READ(sc, STATUS) & CARD_DETECT;
|
||||
* return SSP_RD(sc, STATUS) & CARD_DETECT;
|
||||
* #endif
|
||||
* Until GPIO functionality is not present I am just going to */
|
||||
|
||||
|
@ -273,9 +271,9 @@ issp_write_protect(sdmmc_chipset_handle_t sch)
|
|||
}
|
||||
|
||||
static int
|
||||
issp_bus_power(sdmmc_chipset_handle_t sch, uint32_t power)
|
||||
issp_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
|
||||
{
|
||||
/* i.MX233 does not support setting bus power. */
|
||||
/* i.MX23 SSP does not support setting bus power. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -285,9 +283,13 @@ issp_bus_clock(sdmmc_chipset_handle_t sch, int clock)
|
|||
struct issp_softc *sc = sch;
|
||||
uint32_t sck;
|
||||
|
||||
aprint_normal_dev(sc->sc_dev, "requested clock %d Hz", clock * 1000);
|
||||
sck = issp_set_sck(sc, clock * 1000);
|
||||
aprint_normal(", got %d Hz\n", sck);
|
||||
|
||||
/* 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -295,8 +297,29 @@ issp_bus_clock(sdmmc_chipset_handle_t sch, int clock)
|
|||
static int
|
||||
issp_bus_width(sdmmc_chipset_handle_t sch, int width)
|
||||
{
|
||||
/* Return error if other than 4-bit width is requested. */
|
||||
return width - 4;
|
||||
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);
|
||||
break;
|
||||
case(4):
|
||||
reg |= __SHIFTIN(BUS_WIDTH_4_BIT, HW_SSP_CTRL0_BUS_WIDTH);
|
||||
break;
|
||||
case(8):
|
||||
reg |= __SHIFTIN(BUS_WIDTH_8_BIT, HW_SSP_CTRL0_BUS_WIDTH);
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSP_WR(sc, HW_SSP_CTRL0, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -311,60 +334,130 @@ 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;
|
||||
|
||||
/* Set excepted response type. */
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0_CLR,
|
||||
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_WRITE(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_GET_RESP);
|
||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_GET_RESP);
|
||||
if (ISSET(cmd->c_flags, SCF_RSP_136))
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_LONG_RESP);
|
||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_LONG_RESP);
|
||||
}
|
||||
|
||||
/* If CMD does not need CRC validation, tell it to SSP. */
|
||||
if (ISSET(cmd->c_flags, SCF_RSP_CRC))
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_IGNORE_CRC);
|
||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_IGNORE_CRC);
|
||||
else
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_IGNORE_CRC);
|
||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_IGNORE_CRC);
|
||||
|
||||
/* Set command. */
|
||||
SSP_WRITE(sc, HW_SSP_CMD0_CLR, HW_SSP_CMD0_CMD);
|
||||
SSP_WRITE(sc, HW_SSP_CMD0_SET,
|
||||
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));
|
||||
|
||||
/* Set command argument. */
|
||||
SSP_WRITE(sc, HW_SSP_CMD1, cmd->c_arg);
|
||||
SSP_WR(sc, HW_SSP_CMD1, cmd->c_arg);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
} else {
|
||||
/* No data to be transferred. */
|
||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_DATA_XFER);
|
||||
}
|
||||
|
||||
/* Run the command. */
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_RUN);
|
||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_RUN);
|
||||
|
||||
/* Wait until SSP has processed the command. */
|
||||
while (SSP_READ(sc, HW_SSP_STATUS) &
|
||||
(HW_SSP_STATUS_CMD_BUSY | HW_SSP_STATUS_BUSY))
|
||||
;
|
||||
|
||||
/* Check if the command ran without errors. */
|
||||
reg = SSP_READ(sc, HW_SSP_STATUS);
|
||||
|
||||
if (reg & SSP_STATUS_ERR)
|
||||
cmd->c_error = reg & SSP_STATUS_ERR;
|
||||
|
||||
/* Read response if such was requested. */
|
||||
if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
|
||||
cmd->c_resp[0] = SSP_READ(sc, HW_SSP_SDRESP0);
|
||||
if (ISSET(cmd->c_flags, SCF_RSP_136)) {
|
||||
cmd->c_resp[1] = SSP_READ(sc, HW_SSP_SDRESP1);
|
||||
cmd->c_resp[2] = SSP_READ(sc, HW_SSP_SDRESP2);
|
||||
cmd->c_resp[3] = SSP_READ(sc, HW_SSP_SDRESP3);
|
||||
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);
|
||||
}
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
apbdma_dmamem_alloc()
|
||||
apbdma_do_dma()
|
||||
wait_until_done()
|
||||
*/
|
||||
/* Wait until SSP is done. */
|
||||
while (SSP_RD(sc, HW_SSP_STATUS) & SSP_BUSY)
|
||||
;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Read response 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)) {
|
||||
cmd->c_resp[1] = SSP_RD(sc, HW_SSP_SDRESP1);
|
||||
cmd->c_resp[2] = SSP_RD(sc, HW_SSP_SDRESP2);
|
||||
cmd->c_resp[3] = SSP_RD(sc, HW_SSP_SDRESP3);
|
||||
/*
|
||||
* Remove CRC7 + LSB by rotating all bits right by 8 to
|
||||
* make sdmmc __bitfield() happy.
|
||||
*/
|
||||
cmd->c_resp[0] >>= 8; /* Remove CRC7 + LSB. */
|
||||
cmd->c_resp[0] |= (0x000000FF & cmd->c_resp[1]) << 24;
|
||||
cmd->c_resp[1] >>= 8;
|
||||
cmd->c_resp[1] |= (0x000000FF & cmd->c_resp[2]) << 24;
|
||||
cmd->c_resp[2] >>= 8;
|
||||
cmd->c_resp[2] |= (0x000000FF & cmd->c_resp[3]) << 24;
|
||||
cmd->c_resp[3] >>= 8;
|
||||
}
|
||||
}
|
||||
pioerr:
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -374,7 +467,7 @@ 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");
|
||||
"issp_card_enable_intr NOT IMPLEMENTED!\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -392,7 +485,7 @@ issp_card_intr_ack(sdmmc_chipset_handle_t sch)
|
|||
/*
|
||||
* Reset the SSP block.
|
||||
*
|
||||
* Inspired by i.MX233 RM "39.3.10 Correct Way to Soft Reset a Block"
|
||||
* Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block"
|
||||
*/
|
||||
static void
|
||||
issp_reset(struct issp_softc *sc)
|
||||
|
@ -402,65 +495,77 @@ issp_reset(struct issp_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.
|
||||
*/
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_SFTRST);
|
||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_SFTRST);
|
||||
|
||||
/* Wait at least a microsecond for SFTRST to deassert. */
|
||||
loop = 0;
|
||||
while ((SSP_READ(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_SFTRST) ||
|
||||
while ((SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_SFTRST) ||
|
||||
(loop < SSP_SOFT_RST_LOOP))
|
||||
loop++;
|
||||
|
||||
/* Clear CLKGATE so we can wait for its assertion below. */
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_CLKGATE);
|
||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_CLKGATE);
|
||||
|
||||
/* Soft-reset the block. */
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_SFTRST);
|
||||
SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_SFTRST);
|
||||
|
||||
/* Wait until clock is in the gated state. */
|
||||
while (!(SSP_READ(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_CLKGATE));
|
||||
while (!(SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_CLKGATE));
|
||||
|
||||
/* Bring block out of reset. */
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_SFTRST);
|
||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_SFTRST);
|
||||
|
||||
loop = 0;
|
||||
while ((SSP_READ(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_SFTRST) ||
|
||||
while ((SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_SFTRST) ||
|
||||
(loop < SSP_SOFT_RST_LOOP))
|
||||
loop++;
|
||||
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_CLKGATE);
|
||||
|
||||
SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_CLKGATE);
|
||||
|
||||
/* Wait until clock is in the NON-gated state. */
|
||||
while (SSP_READ(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_CLKGATE);
|
||||
while (SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_CLKGATE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize common options.
|
||||
* DATA_TIMEOUT is calculated as:
|
||||
* (1 / SSP_CLK) * (DATA_TIMEOUT * 4096)
|
||||
*/
|
||||
#define DATA_TIMEOUT 0x4240 /* 723ms */
|
||||
|
||||
/*
|
||||
* Initialize SSP controller to SD/MMC mode.
|
||||
*/
|
||||
static void
|
||||
issp_init(struct issp_softc *sc)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* Initialize SD/MMC controller. */
|
||||
reg = SSP_READ(sc, HW_SSP_CTRL0);
|
||||
/* Initial data bus width is 1-bit. */
|
||||
reg = SSP_RD(sc, HW_SSP_CTRL0);
|
||||
reg &= ~(HW_SSP_CTRL0_BUS_WIDTH);
|
||||
reg |= __SHIFTIN(0x1, HW_SSP_CTRL0_BUS_WIDTH) | HW_SSP_CTRL0_ENABLE;
|
||||
SSP_WRITE(sc, HW_SSP_CTRL0, reg);
|
||||
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);
|
||||
|
||||
reg = SSP_READ(sc, HW_SSP_CTRL1);
|
||||
/* Set data timeout. */
|
||||
reg = SSP_RD(sc, HW_SSP_TIMING);
|
||||
reg &= ~(HW_SSP_TIMING_TIMEOUT);
|
||||
reg |= __SHIFTIN(DATA_TIMEOUT, HW_SSP_TIMING_TIMEOUT);
|
||||
|
||||
/* 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);
|
||||
reg &= ~(HW_SSP_CTRL1_WORD_LENGTH | HW_SSP_CTRL1_SSP_MODE);
|
||||
reg |= HW_SSP_CTRL1_POLARITY |
|
||||
__SHIFTIN(0x7, HW_SSP_CTRL1_WORD_LENGTH) |
|
||||
__SHIFTIN(0x3, HW_SSP_CTRL1_SSP_MODE);
|
||||
SSP_WRITE(sc, HW_SSP_CTRL1, reg);
|
||||
|
||||
/* Set command timeout. */
|
||||
reg = SSP_READ(sc, HW_SSP_TIMING);
|
||||
reg &= ~(HW_SSP_TIMING_TIMEOUT);
|
||||
reg |= __SHIFTIN(SSP_CMD_TIMEOUT, HW_SSP_TIMING_TIMEOUT);
|
||||
SSP_WRITE(sc, HW_SSP_TIMING, reg);
|
||||
SSP_WR(sc, HW_SSP_CTRL1, reg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -499,11 +604,11 @@ issp_set_sck(struct issp_softc *sc, uint32_t target)
|
|||
}
|
||||
}
|
||||
out:
|
||||
reg = SSP_READ(sc, HW_SSP_TIMING);
|
||||
reg = SSP_RD(sc, HW_SSP_TIMING);
|
||||
reg &= ~(HW_SSP_TIMING_CLOCK_DIVIDE | HW_SSP_TIMING_CLOCK_RATE);
|
||||
reg |= __SHIFTIN(div, HW_SSP_TIMING_CLOCK_DIVIDE) |
|
||||
__SHIFTIN(rate, HW_SSP_TIMING_CLOCK_RATE);
|
||||
SSP_WRITE(sc, HW_SSP_TIMING, reg);
|
||||
SSP_WR(sc, HW_SSP_TIMING, reg);
|
||||
|
||||
return SSP_CLK / (d * (1 + r));
|
||||
return SSP_CLK / (div * (1 + rate));
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $Id: IMX23_OLINUXINO,v 1.1 2012/11/20 19:08:45 jkunz Exp $
|
||||
# $Id: IMX23_OLINUXINO,v 1.2 2012/12/16 19:45:52 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 ? type ?
|
||||
config netbsd root on ld0a type ?
|
||||
|
||||
# The main bus device
|
||||
mainbus0 at root
|
||||
|
@ -25,9 +25,9 @@ apbh0 at mainbus? base 0x80000000 size 0x00040000
|
|||
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?
|
||||
ssp0 at apbh? addr 0x80010000 size 0x2000 irq 15
|
||||
sdmmc* at ssp?
|
||||
ld* at sdmmc?
|
||||
|
||||
# APBX bus
|
||||
apbx0 at mainbus? base 0x80040000 size 0x00040000
|
||||
|
@ -47,11 +47,7 @@ options MEMSIZE=64
|
|||
options DDB
|
||||
options HZ=100
|
||||
|
||||
options MEMORY_DISK_HOOKS
|
||||
options MEMORY_DISK_IS_ROOT
|
||||
options MEMORY_DISK_ROOT_SIZE=12288 # 6 megs
|
||||
options MEMORY_DISK_RBFLAGS=RB_SINGLE
|
||||
|
||||
pseudo-device md
|
||||
|
||||
file-system FFS
|
||||
file-system EXT2FS
|
||||
file-system MSDOSFS
|
||||
|
||||
|
|
Loading…
Reference in New Issue