arm:mmc driver refactor and timeout fix for BeagleBoneBlack.

When we send MMC commads that contain data the controller provides no
description of the order of events and we need to be able to send data
from and to the controller "when needed". Changed the code to react
on buffer read and buffer write ready based on interrupts.

Change-Id: I60c9140bf0e45b74be6475054564d4e1bd89f21e
This commit is contained in:
Kees Jongenburger 2013-06-18 21:39:04 +02:00 committed by Kees Jongenburger
parent 8bac458ae9
commit 061fed753e
7 changed files with 254 additions and 369 deletions

View file

@ -1,6 +1,6 @@
# Makefile for the mmc driver.
PROG= mmc
SRCS= mmcblk.c mmchost_dummy.c mmclog.h sdhcreg.h sdmmcreg.h
SRCS= mmcblk.c mmchost_dummy.c sdhcreg.h sdmmcreg.h
.if ${MACHINE_ARCH} == "earm"

View file

@ -27,13 +27,8 @@ The SD protocol is well defined and the imported the netbsd sdhcreg and
sdmmcreg headers will allow us to make the MMC interface more generic. We would
like mmchost_mmchs to be split in a generic part and a specific part.
* Using interrupts
At time of writing the interrupt handlers on the ARM port are not implemented.
Once that is done we will be able to rewrite the code to use proper interrupt
handlers.
* 4 and 8 bits access
The driver currently only reads data over a single data line. Adding support
* 8 bits access
The driver currently only reads data over 1 or 4 bits address lines. Adding support
for 4 or 8 bits(for movinands) mode is very welcome.
* DMA.

View file

@ -6,6 +6,7 @@
#include <minix/driver.h>
#include <minix/blockdriver.h>
#include <minix/drvlib.h>
#include <minix/log.h>
#include <minix/minlib.h>
/* system headers */
@ -20,10 +21,9 @@
/* local headers */
#include "mmchost.h"
#include "mmclog.h"
/* used for logging */
static struct mmclog log = {
static struct log log = {
.name = "mmc_block",
.log_level = LEVEL_INFO,
.log_func = default_log
@ -62,8 +62,7 @@ static void block_signal_handler_cb(int signo);
void
bdr_alarm(clock_t stamp)
{
mmc_log_debug(&log, "alarm %d\n", stamp);
log_debug(&log, "alarm %d\n", stamp);
}
static int apply_env();
@ -91,7 +90,7 @@ static struct blockdriver mmc_driver = {
static void
hw_intr(unsigned int irqs)
{
mmc_log_debug(&log, "Hardware inter left over\n");
log_debug(&log, "Hardware inter left over\n");
host.hw_intr(irqs);
}
@ -118,7 +117,7 @@ apply_env()
} else if (strncmp(driver, "dummy", strlen("dummy") + 1) == 0) {
host_initialize_host_structure_dummy(&host);
} else {
mmc_log_warn(&log, "Unknown driver %s\n", driver);
log_warn(&log, "Unknown driver %s\n", driver);
}
/* Initialize the verbosity level. */
v = 0;
@ -131,7 +130,7 @@ apply_env()
v = 0;
env_parse("instance", "d", 0, &v, 0, 3);
if (host.host_set_instance(&host, v)) {
mmc_log_warn(&log, "Failed to set mmc instance to %d\n", v);
log_warn(&log, "Failed to set mmc instance to %d\n", v);
return -1; /* NOT OK */
}
return OK;
@ -153,15 +152,14 @@ block_open(dev_t minor, int access)
i = j = part_count = sub_part_count = 0;
if (!slot) {
mmc_log_debug(&log,
"Not handling open on non existing slot\n");
log_debug(&log, "Not handling open on non existing slot\n");
return EIO;
}
assert(slot->host != NULL);
if (!slot->host->card_detect(slot)) {
mmc_log_debug(&log, "No card inserted in the SD slot\n");
log_debug(&log, "No card inserted in the SD slot\n");
return EIO;
}
@ -169,14 +167,14 @@ block_open(dev_t minor, int access)
if (slot->card.state == SD_MODE_DATA_TRANSFER_MODE) {
assert(slot->card.open_ct >= 0);
slot->card.open_ct++;
mmc_log_trace(&log, "increased open count to %d\n",
log_trace(&log, "increased open count to %d\n",
slot->card.open_ct);
return OK;
}
/* We did not have an sd-card inserted so we are going to probe for it
*/
mmc_log_debug(&log, "First open on (%d)\n", minor);
log_debug(&log, "First open on (%d)\n", minor);
if (!host.card_initialize(slot)) {
// * TODO: set card state to INVALID until removed? */
return EIO;
@ -185,27 +183,27 @@ block_open(dev_t minor, int access)
partition(&mmc_driver, 0 /* first card on bus */ , P_PRIMARY,
0 /* atapi device?? */ );
mmc_log_trace(&log, "descr \toffset(bytes) size(bytes)\n", minor);
log_trace(&log, "descr \toffset(bytes) size(bytes)\n", minor);
mmc_log_trace(&log, "disk %d\t0x%016llx 0x%016llx\n", i,
log_trace(&log, "disk %d\t0x%016llx 0x%016llx\n", i,
slot->card.part[0].dv_base, slot->card.part[0].dv_size);
for (i = 1; i < 5; i++) {
if (slot->card.part[i].dv_size == 0)
continue;
part_count++;
mmc_log_trace(&log, "part %d\t0x%016llx 0x%016llx\n", i,
log_trace(&log, "part %d\t0x%016llx 0x%016llx\n", i,
slot->card.part[i].dv_base, slot->card.part[i].dv_size);
for (j = 0; j < 4; j++) {
if (slot->card.subpart[(i - 1) * 4 + j].dv_size == 0)
continue;
sub_part_count++;
mmc_log_trace(&log,
log_trace(&log,
" sub %d/%d\t0x%016llx 0x%016llx\n", i, j,
slot->card.subpart[(i - 1) * 4 + j].dv_base,
slot->card.subpart[(i - 1) * 4 + j].dv_size);
}
}
mmc_log_debug(&log, "Found %d partitions and %d sub partitions\n",
log_debug(&log, "Found %d partitions and %d sub partitions\n",
part_count, sub_part_count);
slot->card.open_ct++;
assert(slot->card.open_ct == 1);
@ -222,8 +220,7 @@ block_close(dev_t minor)
slot = get_slot(minor);
if (!slot) {
mmc_log_debug(&log,
"Not handling open on non existing slot\n");
log_debug(&log, "Not handling open on non existing slot\n");
return EIO;
}
@ -236,14 +233,13 @@ block_close(dev_t minor)
* return */
if (slot->card.open_ct > 1) {
slot->card.open_ct--;
mmc_log_trace(&log, "decreased open count to %d\n",
log_trace(&log, "decreased open count to %d\n",
slot->card.open_ct);
return OK;
}
assert(slot->card.open_ct == 1);
mmc_log_debug(&log,
"freeing the block device as it is no longer used\n");
log_debug(&log, "freeing the block device as it is no longer used\n");
/* release the card as check the open_ct should be 0 */
slot->host->card_release(&slot->card);
@ -307,23 +303,23 @@ block_transfer(dev_t minor, /* minor device number */
/* Get the current "device" geometry */
dev = block_part(minor);
if (dev == NULL) {
mmc_log_warn(&log,
log_warn(&log,
"Transfer requested on unknown device minor(%d)\n", minor);
/* Unknown device */
return ENXIO;
}
mmc_log_trace(&log, "I/O on minor(%d) %s at 0x%016llx\n", minor,
log_trace(&log, "I/O on minor(%d) %s at 0x%016llx\n", minor,
(do_write) ? "Write" : "Read", position);
slot = get_slot(minor);
assert(slot);
if (slot->card.blk_size == 0) {
mmc_log_warn(&log, "Request on a card with block size of 0\n");
log_warn(&log, "Request on a card with block size of 0\n");
return EINVAL;
}
if (slot->card.blk_size > COPYBUFF_SIZE) {
mmc_log_warn(&log,
log_warn(&log,
"Card block size (%d) exceeds internal buffer size %d\n",
slot->card.blk_size, COPYBUFF_SIZE);
return EINVAL;
@ -333,7 +329,7 @@ block_transfer(dev_t minor, /* minor device number */
* parameters of transfers, in those cases we return EINVAL */
if (position % slot->card.blk_size != 0) {
/* Starting at a block boundary */
mmc_log_warn(&log,
log_warn(&log,
"Requests must start at a block boundary"
"(start,block size)=(%016llx,%08x)\n", position,
slot->card.blk_size);
@ -346,7 +342,7 @@ block_transfer(dev_t minor, /* minor device number */
/* Are we trying to start reading past the end */
if (position >= dev->dv_size) {
mmc_log_warn(&log, "start reading past drive size\n");
log_warn(&log, "start reading past drive size\n");
return 0;
};
@ -356,7 +352,7 @@ block_transfer(dev_t minor, /* minor device number */
assert(ciov != NULL);
if (ciov->iov_size % blk_size != 0) {
/* transfer a multiple of blk_size */
mmc_log_warn(&log,
log_warn(&log,
"Requests must start at a block boundary "
"(start,block size)=(%016llx,%08x)\n", position,
slot->card.blk_size);
@ -364,7 +360,7 @@ block_transfer(dev_t minor, /* minor device number */
}
if (ciov->iov_size <= 0) {
mmc_log_warn(&log,
log_warn(&log,
"Invalid iov size for iov %d of %d size\n",
counter, nr_req, ciov->iov_size);
return EINVAL;
@ -385,7 +381,7 @@ block_transfer(dev_t minor, /* minor device number */
io_size = dev->dv_size - (position + bytes_written);
};
mmc_log_trace(&log,
log_trace(&log,
"I/O %s request(%d/%d) iov(grant,size,iosize,"
"offset)=(%d,%d,%d,%d)\n",
(do_write) ? "write" : "read", counter + 1, nr_req,
@ -400,7 +396,7 @@ block_transfer(dev_t minor, /* minor device number */
i * blk_size, (vir_bytes) copybuff,
blk_size);
if (r != OK) {
mmc_log_warn(&log,
log_warn(&log,
"I/O write error: %s iov(base,size)=(%d,%d)"
" at offset=%d\n",
strerror(_SIGN r), ciov->iov_addr,
@ -424,7 +420,7 @@ block_transfer(dev_t minor, /* minor device number */
r = copyto(endpt, ciov->iov_addr, i * blk_size,
(vir_bytes) copybuff, blk_size);
if (r != OK) {
mmc_log_warn(&log,
log_warn(&log,
"I/O read error: %s iov(base,size)=(%d,%d)"
" at offset=%d\n",
strerror(_SIGN r), ciov->iov_addr,
@ -448,13 +444,13 @@ block_ioctl(dev_t minor,
{
/* IOCTL handling */
struct sd_slot *slot;
mmc_log_trace(&log,
log_trace(&log,
"enter (minor,request,endpoint,grant)=(%d,%lu,%d)\n", minor,
request, endpt, grant);
slot = get_slot(minor);
if (!slot) {
mmc_log_warn(&log,
log_warn(&log,
"Doing ioctl on non existing block device(%d)\n", minor);
return EINVAL;
}
@ -462,7 +458,7 @@ block_ioctl(dev_t minor,
switch (request) {
case DIOCOPENCT:
// TODO: add a check for card validity */
mmc_log_trace(&log, "returning open count %d\n",
log_trace(&log, "returning open count %d\n",
slot->card.open_ct);
/* return the current open count */
return sys_safecopyto(endpt, grant, 0,
@ -496,14 +492,14 @@ block_part(dev_t minor)
dev = NULL;
slot = get_slot(minor);
if (!slot) {
mmc_log_warn(&log,
log_warn(&log,
"Device information requested for non existing partition "
"minor(%d)\n", minor);
return NULL;
}
if (!slot->host->card_detect(slot)) {
mmc_log_warn(&log,
log_warn(&log,
"Device information requested from empty slot(%d)\n",
minor);
return NULL;
@ -512,19 +508,19 @@ block_part(dev_t minor)
if (minor < 5) {
/* we are talking about the first disk */
dev = &slot->card.part[minor];
mmc_log_trace(&log,
log_trace(&log,
"returning partition(%d) (base,size)=(0x%016llx,0x%016llx)\n",
minor, dev->dv_base, dev->dv_size);
} else if (minor >= 128 && minor < 128 + 16) {
/* sub partitions of the first disk we don't care about the
* rest */
dev = &slot->card.subpart[minor - 128];
mmc_log_trace(&log,
log_trace(&log,
"returning sub partition(%d) (base,size)=(0x%016llx,0x%016llx)\n",
minor - 128, dev->dv_base, dev->dv_size);
} else {
mmc_log_warn(&log,
log_warn(&log,
"Device information requested for non existing "
"partition minor(%d)\n", minor);
}
@ -537,16 +533,14 @@ block_part(dev_t minor)
static void
sef_local_startup()
{
mmc_log_info(&log, "Initializing the MMC block device\n");
log_info(&log, "Initializing the MMC block device\n");
if (apply_env()) {
mmc_log_warn(&log,
"Failed while applying environment settings\n");
log_warn(&log, "Failed while applying environment settings\n");
exit(EXIT_FAILURE);
}
if (host.host_init(&host)) {
mmc_log_warn(&log,
"Failed to initialize the host controller\n");
log_warn(&log, "Failed to initialize the host controller\n");
exit(EXIT_FAILURE);
}
/*
@ -574,16 +568,16 @@ block_system_event_cb(int type, sef_init_info_t * info)
* sef_local_startup */
switch (type) {
case SEF_INIT_FRESH:
mmc_log_info(&log, "System event framework fresh start\n");
log_info(&log, "System event framework fresh start\n");
break;
case SEF_INIT_LU:
/* Restore the state. post update */
mmc_log_info(&log, "System event framework live update\n");
log_info(&log, "System event framework live update\n");
break;
case SEF_INIT_RESTART:
mmc_log_info(&log, "System event framework post restart\n");
log_info(&log, "System event framework post restart\n");
break;
}
blockdriver_announce(type);
@ -598,7 +592,7 @@ block_signal_handler_cb(int signo)
{
struct sd_slot *slot;
mmc_log_debug(&log, "System event framework signal handler sig(%d)\n",
log_debug(&log, "System event framework signal handler sig(%d)\n",
signo);
/* Only check for termination signal, ignore anything else. */
if (signo != SIGTERM)
@ -609,12 +603,12 @@ block_signal_handler_cb(int signo)
slot = get_slot(0);
assert(slot);
if (slot->card.open_ct > 0) {
mmc_log_debug(&log, "Not responding to SIGTERM (open count=%d)\n",
log_debug(&log, "Not responding to SIGTERM (open count=%d)\n",
slot->card.open_ct);
return;
}
mmc_log_info(&log, "MMC driver exit");
log_info(&log, "MMC driver exit");
exit(0);
}
@ -642,7 +636,7 @@ get_slot(dev_t minor)
/* a minor from the first disk */
return &host.slot[0];
} else {
mmc_log_trace(&log,
log_trace(&log,
"Device information requested for non existing partition "
"minor(%d)\n", minor);
return NULL;
@ -655,7 +649,7 @@ set_log_level(int level)
if (level < 0 || level >= 4) {
return;
}
mmc_log_info(&log, "Setting verbosity level to %d\n", level);
log_info(&log, "Setting verbosity level to %d\n", level);
log.log_level = level;
if (host.set_log_level) {
host.set_log_level(level);

View file

@ -1,6 +1,7 @@
/* kernel headers */
#include <minix/blockdriver.h>
#include <minix/minlib.h>
#include <minix/log.h>
/* usr headers */
#include <stdio.h>
@ -10,14 +11,13 @@
#include <unistd.h>
/* local headers */
#include "mmclog.h"
#include "mmchost.h"
#include "sdmmcreg.h"
/*
* Define a structure to be used for logging
*/
static struct mmclog log = {
static struct log log = {
.name = "mmc_host_memory",
.log_level = LEVEL_INFO,
.log_func = default_log
@ -36,7 +36,7 @@ init_dummy_sdcard(struct sd_slot *slot)
assert(slot != NULL);
mmc_log_info(&log, "Using a dummy card \n");
log_info(&log, "Using a dummy card \n");
if (dummy_data == NULL) {
dummy_data = malloc(DUMMY_BLOCK_SIZE * DUMMY_SIZE_IN_BLOCKS);
if (dummy_data == NULL) {
@ -81,7 +81,7 @@ dummy_set_log_level(int level)
int
dummy_host_set_instance(struct mmc_host *host, int instance)
{
mmc_log_info(&log, "Using instance number %d\n", instance);
log_info(&log, "Using instance number %d\n", instance);
if (instance != 0) {
return EIO;
}

View file

@ -3,6 +3,7 @@
#include <minix/com.h>
#include <minix/vm.h>
#include <minix/spin.h>
#include <minix/log.h>
#include <minix/mmio.h>
#include <sys/mman.h>
#include <sys/time.h>
@ -18,7 +19,6 @@
#include <unistd.h>
/* local headers */
#include "mmclog.h"
#include "mmchost.h"
/* header imported from netbsd */
@ -41,12 +41,12 @@ static int hook_id = 1;
#endif
#endif
#define SANE_TIMEOUT 500000 /* 500 MS */
#define SANE_TIMEOUT 500000 /* 500 ms */
/*
* Define a structure to be used for logging
*/
static struct mmclog log = {
static struct log log = {
.name = "mmc_host_mmchs",
.log_level = LEVEL_INFO,
.log_func = default_log
@ -98,7 +98,7 @@ mmchs_init(uint32_t instance)
while (!(read32(base_address + MMCHS_SD_SYSSTATUS)
& MMCHS_SD_SYSSTATUS_RESETDONE)) {
if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log, "mmc init timeout\n");
log_warn(&log, "mmc init timeout\n");
return 1;
}
}
@ -159,7 +159,7 @@ mmchs_init(uint32_t instance)
while ((read32(base_address + MMCHS_SD_HCTL) & MMCHS_SD_HCTL_SDBP)
!= MMCHS_SD_HCTL_SDBP_ON) {
if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log, "mmc init timeout SDBP not set\n");
log_warn(&log, "mmc init timeout SDBP not set\n");
return 1;
}
}
@ -181,9 +181,8 @@ mmchs_init(uint32_t instance)
spin_init(&spin, SANE_TIMEOUT);
while ((read32(base_address + MMCHS_SD_SYSCTL) & MMCHS_SD_SYSCTL_ICS)
!= MMCHS_SD_SYSCTL_ICS_STABLE) {
if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log, "clock not stable\n");
log_warn(&log, "clock not stable\n");
return 1;
}
}
@ -200,9 +199,7 @@ mmchs_init(uint32_t instance)
MMCHS_SD_IE_TC_ENABLE_ENABLE);
/* enable error interrupts */
/* NOTE: We are currently skipping the BADA interrupt it does get
* raised for unknown reasons */
set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_ERROR_MASK, 0x0fffffffu);
set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_ERROR_MASK, 0xffffffffu);
/* clear the error interrupts */
set32(base_address + MMCHS_SD_STAT, MMCHS_SD_STAT_ERROR_MASK,
@ -219,15 +216,14 @@ mmchs_init(uint32_t instance)
while ((read32(base_address + MMCHS_SD_STAT) & MMCHS_SD_STAT_CC)
!= MMCHS_SD_STAT_CC_RAISED) {
if (read32(base_address + MMCHS_SD_STAT) & 0x8000) {
mmc_log_warn(&log, "%s, error stat %x\n",
log_warn(&log, "%s, error stat %x\n",
__FUNCTION__,
read32(base_address + MMCHS_SD_STAT));
return 1;
}
if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log,
"Interrupt not raised during init\n");
log_warn(&log, "Interrupt not raised during init\n");
return 1;
}
}
@ -261,10 +257,86 @@ mmchs_init(uint32_t instance)
return 0;
}
void
intr_deassert(int mask)
{
if (read32(base_address + MMCHS_SD_STAT) & 0x8000) {
log_warn(&log, "%s, error stat %08x\n", __FUNCTION__,
read32(base_address + MMCHS_SD_STAT));
set32(base_address + MMCHS_SD_STAT, MMCHS_SD_STAT_ERROR_MASK,
0xffffffffu);
} else {
write32(base_address + MMCHS_SD_STAT, mask);
}
}
/* pointer to the data to transfer used in bwr and brr */
unsigned char *io_data;
int io_len;
void
handle_bwr()
{
/* handle buffer write ready interrupts. These happen in a non
* predictable way (eg. we send a request but don't know if we are
* first doing to get a request completed before we are allowed to
* send the data to the harware or not */
uint32_t value;
uint32_t count;
assert(read32(base_address +
MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BWE_EN);
assert(io_data != NULL);
for (count = 0; count < io_len; count += 4) {
while (!(read32(base_address +
MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BWE_EN)) {
log_warn(&log,
"Error expected Buffer to be write enabled(%d)\n",
count);
}
*((char *) &value) = io_data[count];
*((char *) &value + 1) = io_data[count + 1];
*((char *) &value + 2) = io_data[count + 2];
*((char *) &value + 3) = io_data[count + 3];
write32(base_address + MMCHS_SD_DATA, value);
}
intr_deassert(MMCHS_SD_IE_BWR_ENABLE);
/* expect buffer to be write enabled */
io_data = NULL;
}
void
handle_brr()
{
/* handle buffer read ready interrupts. genrally these happen afther
* the data is read from the sd card. */
uint32_t value;
uint32_t count;
/* Problem BRE should be true */
assert(read32(base_address +
MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BRE_EN);
assert(io_data != NULL);
for (count = 0; count < io_len; count += 4) {
value = read32(base_address + MMCHS_SD_DATA);
io_data[count] = *((char *) &value);
io_data[count + 1] = *((char *) &value + 1);
io_data[count + 2] = *((char *) &value + 2);
io_data[count + 3] = *((char *) &value + 3);
}
/* clear bbr interrupt */
intr_deassert(MMCHS_SD_IE_BRR_ENABLE_ENABLE);
io_data = NULL;
}
static void
mmchs_hw_intr(unsigned int irqs)
{
mmc_log_warn(&log, "Hardware interrupt left over\n");
log_warn(&log, "Hardware interrupt left over (0x%08lx)\n",
read32(base_address + MMCHS_SD_STAT));
#ifdef USE_INTR
if (sys_irqenable(&hook_id) != OK)
@ -300,27 +372,47 @@ intr_wait(int mask)
switch (_ENDPOINT_P(m.m_source)) {
case CLOCK:
/* Timeout. */
// w_timeout(); /* a.o. set w_status */
mmc_log_warn(&log, "TIMEOUT\n");
log_warn(&log, "TIMEOUT\n");
return 1;
break;
case HARDWARE:
v = read32(base_address + MMCHS_SD_STAT);
if (v & mask) {
sys_setalarm(0, 0);
return 0;
} else if (v & (1 << 15)) {
return 1; /* error */
} else {
mmc_log_debug(&log,
while ((v =
read32(base_address +
MMCHS_SD_STAT)) != 0) {
if (v & MMCHS_SD_IE_BWR_ENABLE) {
handle_bwr();
continue;
}
if (v & MMCHS_SD_IE_BRR_ENABLE) {
handle_brr();
continue;
}
if (v & mask) {
/* this is the normal return
* path, the mask given
* matches the pending
* interrupt. canel the alarm
* and return */
sys_setalarm(0, 0);
return 0;
} else if (v & (1 << 15)) {
return 1; /* error */
}
log_warn(&log,
"unexpected HW interrupt 0x%08x mask 0X%08x\n",
v, mask);
if (sys_irqenable(&hook_id) != OK)
printf
("Failed to re-enable irqenable irq\n");
continue;
// return 1;
}
/* if we end up here re-enable interrupts for
* the next round */
if (sys_irqenable(&hook_id) != OK)
printf
("Failed to re-enable irqenable irq\n");
break;
default:
/*
* unhandled message. queue it and
@ -329,7 +421,6 @@ intr_wait(int mask)
blockdriver_mq_queue(&m, ipc_status);
}
} else {
mmc_log_debug(&log, "Other\n");
/*
* unhandled message. queue it and handle it in the
* blockdriver loop.
@ -348,15 +439,23 @@ intr_wait(int mask)
counter++;
v = read32(base_address + MMCHS_SD_STAT);
if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log,
log_warn(&log,
"Timeout waiting for interrupt (%d) value 0x%08x mask 0x%08x\n",
counter, v, mask);
return 1;
}
if (v & MMCHS_SD_IE_BWR_ENABLE) {
handle_bwr();
continue;
}
if (v & MMCHS_SD_IE_BRR_ENABLE) {
handle_brr();
continue;
}
if (v & mask) {
return 0;
} else if (v & 0xFF00) {
mmc_log_debug(&log,
log_debug(&log,
"unexpected HW interrupt (%d) 0x%08x mask 0x%08x\n",
v, mask);
return 1;
@ -366,36 +465,26 @@ intr_wait(int mask)
#endif /* USE_INTR */
}
void
intr_assert(int mask)
{
if (read32(base_address + MMCHS_SD_STAT) & 0x8000) {
mmc_log_debug(&log, "%s, error stat %08x\n", __FUNCTION__,
read32(base_address + MMCHS_SD_STAT));
set32(base_address + MMCHS_SD_STAT, MMCHS_SD_STAT_ERROR_MASK,
0xffffffffu);
} else {
write32(base_address + MMCHS_SD_STAT, mask);
}
}
int
mmchs_send_cmd(uint32_t command, uint32_t arg)
{
/* Read current interrupt status and fail it an interrupt is already
* asserted */
assert(read32(base_address + MMCHS_SD_STAT) == 0);
/* Set arguments */
write32(base_address + MMCHS_SD_ARG, arg);
/* Set command */
set32(base_address + MMCHS_SD_CMD, MMCHS_SD_CMD_MASK, command);
if (intr_wait(MMCHS_SD_STAT_CC | MMCHS_SD_IE_TC_ENABLE_CLEAR)) {
intr_assert(MMCHS_SD_STAT_CC);
mmc_log_warn(&log, "Failure waiting for interrupt\n");
if (intr_wait(MMCHS_SD_STAT_CC)) {
uint32_t v = read32(base_address + MMCHS_SD_STAT);
intr_deassert(MMCHS_SD_STAT_CC);
log_warn(&log, "Failure waiting for interrupt 0x%lx\n", v);
return 1;
}
intr_deassert(MMCHS_SD_STAT_CC);
if ((command & MMCHS_SD_CMD_RSP_TYPE) ==
MMCHS_SD_CMD_RSP_TYPE_48B_BUSY) {
@ -404,18 +493,10 @@ mmchs_send_cmd(uint32_t command, uint32_t arg)
*/
if ((read32(base_address + MMCHS_SD_STAT)
& MMCHS_SD_IE_TC_ENABLE_ENABLE) == 0) {
mmc_log_warn(&log, "TC should be raised\n");
}
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_TC_ENABLE_CLEAR);
if (intr_wait(MMCHS_SD_STAT_CC | MMCHS_SD_IE_TC_ENABLE_CLEAR)) {
intr_assert(MMCHS_SD_STAT_CC);
mmc_log_warn(&log, "Failure waiting for clear\n");
return 1;
log_warn(&log, "TC should be raised\n");
}
intr_deassert(MMCHS_SD_STAT_TC);
}
intr_assert(MMCHS_SD_STAT_CC);
return 0;
}
@ -426,8 +507,6 @@ mmc_send_cmd(struct mmc_command *c)
/* convert the command to a hsmmc command */
int ret;
uint32_t cmd, arg;
uint32_t count;
uint32_t value;
cmd = MMCHS_SD_CMD_INDX_CMD(c->cmd);
arg = c->args;
@ -449,11 +528,10 @@ mmc_send_cmd(struct mmc_command *c)
}
/* read single block */
if (c->cmd == MMC_READ_BLOCK_SINGLE) {
if ((c->cmd == MMC_READ_BLOCK_SINGLE) || (c->cmd == SD_APP_SEND_SCR)) {
cmd |= MMCHS_SD_CMD_DP_DATA; /* Command with data transfer */
cmd |= MMCHS_SD_CMD_MSBS_SINGLE; /* single block */
cmd |= MMCHS_SD_CMD_DDIR_READ; /* read data from card */
}
/* write single block */
@ -465,7 +543,7 @@ mmc_send_cmd(struct mmc_command *c)
/* check we are in a sane state */
if ((read32(base_address + MMCHS_SD_STAT) & 0xffffu)) {
mmc_log_warn(&log, "%s, interrupt already raised stat %08x\n",
log_warn(&log, "%s, interrupt already raised stat %08x\n",
__FUNCTION__, read32(base_address + MMCHS_SD_STAT));
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_CC_ENABLE_CLEAR);
@ -483,12 +561,50 @@ mmc_send_cmd(struct mmc_command *c)
MMCHS_SD_IE_BWR_ENABLE,
MMCHS_SD_IE_BWR_ENABLE_ENABLE);
}
io_data = c->data;
io_len = c->data_len;
assert(io_len <= 0xFFF); /* only 12 bits */
assert(io_data != NULL);
set32(base_address + MMCHS_SD_BLK, MMCHS_SD_BLK_BLEN, io_len);
}
set32(base_address + MMCHS_SD_BLK, MMCHS_SD_BLK_BLEN, 512);
ret = mmchs_send_cmd(cmd, arg);
if (cmd & MMCHS_SD_CMD_DP_DATA) {
assert(c->data_len);
if (cmd & MMCHS_SD_CMD_DDIR_READ) {
/* Wait for TC */
if (intr_wait(MMCHS_SD_IE_TC_ENABLE_ENABLE)) {
intr_deassert(MMCHS_SD_IE_TC_ENABLE_ENABLE);
log_warn(&log,
"(Read) Timeout waiting for interrupt\n");
return 1;
}
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_TC_ENABLE_CLEAR);
/* disable the bbr interrupt */
set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BRR_ENABLE,
MMCHS_SD_IE_BRR_ENABLE_DISABLE);
} else {
/* Wait for TC */
if (intr_wait(MMCHS_SD_IE_TC_ENABLE_ENABLE)) {
intr_deassert(MMCHS_SD_IE_TC_ENABLE_CLEAR);
log_warn(&log,
"(Write) Timeout waiting for transfer complete\n");
return 1;
}
intr_deassert(MMCHS_SD_IE_TC_ENABLE_CLEAR);
set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BWR_ENABLE,
MMCHS_SD_IE_BWR_ENABLE_DISABLE);
}
}
/* copy response into cmd->resp */
switch (c->resp_type) {
case RESP_LEN_48_CHK_BUSY:
@ -507,98 +623,6 @@ mmc_send_cmd(struct mmc_command *c)
return 1;
}
if (cmd & MMCHS_SD_CMD_DP_DATA) {
count = 0;
assert(c->data_len);
if (cmd & MMCHS_SD_CMD_DDIR_READ) {
if (intr_wait(MMCHS_SD_IE_BRR_ENABLE_ENABLE)) {
intr_assert(MMCHS_SD_IE_BRR_ENABLE_ENABLE);
mmc_log_warn(&log,
"Timeout waiting for interrupt\n");
return 1;
}
if (!(read32(base_address +
MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BRE_EN))
{
mmc_log_warn(&log,
"Problem BRE should be true\n");
return 1; /* We are not allowed to read
* data from the data buffer */
}
for (count = 0; count < c->data_len; count += 4) {
value = read32(base_address + MMCHS_SD_DATA);
c->data[count] = *((char *) &value);
c->data[count + 1] = *((char *) &value + 1);
c->data[count + 2] = *((char *) &value + 2);
c->data[count + 3] = *((char *) &value + 3);
}
/* Wait for TC */
if (intr_wait(MMCHS_SD_IE_TC_ENABLE_ENABLE)) {
intr_assert(MMCHS_SD_IE_TC_ENABLE_ENABLE);
mmc_log_warn(&log,
"Timeout waiting for interrupt\n");
return 1;
}
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_TC_ENABLE_CLEAR);
/* clear and disable the bbr interrupt */
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_BRR_ENABLE_CLEAR);
set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BRR_ENABLE,
MMCHS_SD_IE_BRR_ENABLE_DISABLE);
} else {
/* Wait for the MMCHS_SD_IE_BWR_ENABLE interrupt */
if (intr_wait(MMCHS_SD_IE_BWR_ENABLE)) {
intr_assert(MMCHS_SD_IE_BWR_ENABLE);
mmc_log_warn(&log, "WFI failed\n");
return 1;
}
/* clear the interrupt directly */
intr_assert(MMCHS_SD_IE_BWR_ENABLE);
if (!(read32(base_address +
MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BWE_EN))
{
mmc_log_warn(&log,
"Error expected Buffer to be write enabled\n");
return 1; /* not ready to write data */
}
for (count = 0; count < 512; count += 4) {
while (!(read32(base_address +
MMCHS_SD_PSTATE) &
MMCHS_SD_PSTATE_BWE_EN)) {
mmc_log_trace(&log,
"Error expected Buffer to be write enabled(%d)\n",
count);
}
*((char *) &value) = c->data[count];
*((char *) &value + 1) = c->data[count + 1];
*((char *) &value + 2) = c->data[count + 2];
*((char *) &value + 3) = c->data[count + 3];
write32(base_address + MMCHS_SD_DATA, value);
}
/* Wait for TC */
if (intr_wait(MMCHS_SD_IE_TC_ENABLE_CLEAR)) {
intr_assert(MMCHS_SD_IE_TC_ENABLE_CLEAR);
mmc_log_warn(&log,
"(Write) Timeout waiting for transfer complete\n");
return 1;
}
intr_assert(MMCHS_SD_IE_TC_ENABLE_CLEAR);
set32(base_address + MMCHS_SD_IE,
MMCHS_SD_IE_BWR_ENABLE,
MMCHS_SD_IE_BWR_ENABLE_DISABLE);
}
}
return ret;
}
@ -642,14 +666,13 @@ card_identification()
if (mmc_send_cmd(&command)) {
/* We currently only support 2.0, and 1.0 won't respond to
* this request */
mmc_log_warn(&log, "%s, non SDHC card inserted\n",
__FUNCTION__);
log_warn(&log, "%s, non SDHC card inserted\n", __FUNCTION__);
return 1;
}
if (!(command.resp[0]
== (MMCHS_SD_ARG_CMD8_VHS | MMCHS_SD_ARG_CMD8_CHECK_PATTERN))) {
mmc_log_warn(&log, "%s, check pattern check failed %08x\n",
log_warn(&log, "%s, check pattern check failed %08x\n",
__FUNCTION__, command.resp[0]);
return 1;
}
@ -661,13 +684,6 @@ card_query_voltage_and_type(struct sd_card_regs *card)
{
struct mmc_command command;
spin_t spin;
command.cmd = MMC_APP_CMD;
command.resp_type = RESP_LEN_48;
command.args = MMC_ARG_RCA(0x0); /* RCA=0000 */
if (mmc_send_cmd(&command)) {
return 1;
}
command.cmd = SD_APP_OP_COND;
command.resp_type = RESP_LEN_48;
@ -679,18 +695,12 @@ card_query_voltage_and_type(struct sd_card_regs *card)
MMC_OCR_2_7V_2_8V;
command.args |= MMC_OCR_HCS; /* RCA=0000 */
if (mmc_send_cmd(&command)) {
if (mmc_send_app_cmd(card, &command)) {
return 1;
}
/* @todo wait for max 1 ms */
spin_init(&spin, SANE_TIMEOUT);
while (!(command.resp[0] & MMC_OCR_MEM_READY)) {
command.cmd = MMC_APP_CMD;
command.resp_type = RESP_LEN_48;
command.args = MMC_ARG_RCA(0x0); /* RCA=0000 */
if (mmc_send_cmd(&command)) {
return 1;
}
/* Send ADMD41 */
/* 0x1 << 30 == send HCS (Host capacity support) and get OCR
@ -703,7 +713,7 @@ card_query_voltage_and_type(struct sd_card_regs *card)
| MMC_OCR_2_8V_2_9V | MMC_OCR_2_7V_2_8V;
command.args |= MMC_OCR_HCS; /* RCA=0000 */
if (mmc_send_cmd(&command)) {
if (mmc_send_app_cmd(card, &command)) {
return 1;
}
@ -712,8 +722,7 @@ card_query_voltage_and_type(struct sd_card_regs *card)
break;
}
if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log,
"TIMEOUT waiting for the SD card\n");
log_warn(&log, "TIMEOUT waiting for the SD card\n");
}
}
@ -777,12 +786,12 @@ card_csd(struct sd_card_regs *card)
card->csd[3] = command.resp[3];
if (SD_CSD_CSDVER(card->csd) != SD_CSD_CSDVER_2_0) {
mmc_log_warn(&log, "Version 2.0 of CSD register expected\n");
log_warn(&log, "Version 2.0 of CSD register expected\n");
return 1;
}
/* sanity check */
// mmc_log_warn(&log,"size = %llu bytes\n", (long long
// log_warn(&log,"size = %llu bytes\n", (long long
// unsigned)SD_CSD_V2_CAPACITY( card->csd) * 512);
return 0;
}
@ -801,7 +810,6 @@ select_card(struct sd_card_regs *card)
}
return 0;
}
int
read_single_block(struct sd_card_regs *card,
uint32_t blknr, unsigned char *buf)
@ -815,7 +823,7 @@ read_single_block(struct sd_card_regs *card,
command.data_len = 512;
if (mmc_send_cmd(&command)) {
mmc_log_warn(&log, "Error sending command\n");
log_warn(&log, "Error sending command\n");
return 1;
}
@ -828,8 +836,6 @@ write_single_block(struct sd_card_regs *card,
{
struct mmc_command command;
set32(base_address + MMCHS_SD_BLK, MMCHS_SD_BLK_BLEN, 512);
command.cmd = MMC_WRITE_BLOCK_SINGLE;
command.args = blknr;
command.resp_type = RESP_LEN_48;
@ -838,7 +844,7 @@ write_single_block(struct sd_card_regs *card,
/* write single block */
if (mmc_send_cmd(&command)) {
mmc_log_warn(&log, "Write single block command failed\n");
log_warn(&log, "Write single block command failed\n");
return 1;
}
@ -863,7 +869,7 @@ mmchs_set_log_level(int level)
int
mmchs_host_set_instance(struct mmc_host *host, int instance)
{
mmc_log_info(&log, "Using instance number %d\n", instance);
log_info(&log, "Using instance number %d\n", instance);
if (instance != 0) {
return EIO;
}
@ -895,40 +901,38 @@ mmchs_card_initialize(struct sd_slot *slot)
card->slot = slot;
if (card_goto_idle_state()) {
mmc_log_warn(&log, "Failed to go idle state\n");
log_warn(&log, "Failed to go idle state\n");
return NULL;
}
if (card_identification()) {
mmc_log_warn(&log, "Failed to do card_identification\n");
log_warn(&log, "Failed to do card_identification\n");
return NULL;
}
if (card_query_voltage_and_type(&slot->card.regs)) {
mmc_log_warn(&log,
"Failed to do card_query_voltage_and_type\n");
log_warn(&log, "Failed to do card_query_voltage_and_type\n");
return NULL;
}
if (card_identify(&slot->card.regs)) {
mmc_log_warn(&log, "Failed to identify card\n");
log_warn(&log, "Failed to identify card\n");
return NULL;
}
/* We have now initialized the hardware identified the card */
if (card_csd(&slot->card.regs)) {
mmc_log_warn(&log,
"failed to read csd (card specific data)\n");
log_warn(&log, "failed to read csd (card specific data)\n");
return NULL;
}
if (select_card(&slot->card.regs)) {
mmc_log_warn(&log, "Failed to select card\n");
log_warn(&log, "Failed to select card\n");
return NULL;
}
if (SD_CSD_READ_BL_LEN(slot->card.regs.csd) != 0x09) {
/* for CSD version 2.0 the value is fixed to 0x09 and means a
* block size of 512 */
mmc_log_warn(&log, "Block size expect to be 512\n");
log_warn(&log, "Block size expect to be 512\n");
return NULL;
}
@ -981,6 +985,7 @@ mmchs_card_release(struct sd_card *card)
card->open_ct--;
card->state = SD_MODE_UNINITIALIZED;
/* TODO:Set card state */
return OK;
}

View file

@ -1,112 +0,0 @@
#ifndef __MMCLOG_H__
#define __MMCLOG_H__
/*
* Simple logging functions for the MMC layer
*/
/*
* LEVEL_NONE do not log anything.
* LEVEL_WARN Information that needs to be known.
* LEVEL_INFO Basic information like startup messages and occasional events.
* LEVEL_DEBUG debug statements about things happening that are less expected.
* LEVEL_TRACE Way to much information for anybody.
*/
#define LEVEL_NONE 0
#define LEVEL_WARN 1
#define LEVEL_INFO 2
#define LEVEL_DEBUG 3
#define LEVEL_TRACE 4
static const char *level_string[5] = {
"none",
"warn",
"info",
"debug",
"trace"
};
/*
* struct to be initialized by the user of the logging system.
*
* name: The name attribute is used in logging statements do differentiate
* drivers
*
* log_level The level attribute describes the requested logging level. a level
* of 1 will only print warnings while a level of 4 will print all the trace
* information.
*
* log_func The logging function to use to log, mmclog.h provides default_log
* to display information on the kernel output buffer. As a bonus if the
* requested log level is debug or trace the method , file and line number will
* be printed to the steam.
*/
struct mmclog { const char *name; int log_level;
/* the logging function itself */
void (*log_func) (struct mmclog * driver,
int level,
const char *file,
const char *function, int line, const char *fmt, ...);
};
#define __mmc_log(driver,log_level, fmt, args...) \
((driver)->log_func(driver,log_level, \
__FILE__, __FUNCTION__, __LINE__,\
fmt, ## args))
/* Log a warning */
#define mmc_log_warn(driver, fmt, args...) \
__mmc_log(driver, LEVEL_WARN, fmt, ## args)
/* Log an information message */
#define mmc_log_info(driver, fmt, args...) \
__mmc_log(driver, LEVEL_INFO, fmt, ## args)
/* log debugging output */
#define mmc_log_debug(driver, fmt, args...) \
__mmc_log(driver, LEVEL_DEBUG, fmt, ## args)
/* log trace output */
#define mmc_log_trace(driver, fmt, args...) \
__mmc_log(driver, LEVEL_TRACE, fmt, ## args)
#endif /* __MMCLOG_H__ */
static void
default_log(struct mmclog *driver,
int level,
const char *file, const char *function, int line, const char *fmt, ...)
{
va_list args;
if (level > driver->log_level) {
return;
}
/* If the wanted level is debug also display line/method information */
if (driver->log_level >= LEVEL_DEBUG) {
fprintf(stderr, "%s(%s):%s+%d(%s):", driver->name,
level_string[level], file, line, function);
} else {
fprintf(stderr, "%s(%s)", driver->name, level_string[level]);
}
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
#ifdef hacks
static void
hexdump(unsigned char *d, unsigned int size)
{
int s;
for (s = 0; s < size; s += 4) {
fprintf(stdout, "0x%04x 0x%02X%02X%02X%02X %c%c%c%c\n", s,
(unsigned int) d[s], (unsigned int) d[s + 1],
(unsigned int) d[s + 2], (unsigned int) d[s + 3], d[s],
d[s + 1], d[s + 2], d[s + 3]);
}
}
#endif

View file

@ -159,6 +159,9 @@
#define MMCHS_SD_STAT_CC (0x1 << 0) /* Command complete status */
#define MMCHS_SD_STAT_CC_UNRAISED (0x0 << 0) /* Command not completed */
#define MMCHS_SD_STAT_CC_RAISED (0x1 << 0) /* Command completed */
#define MMCHS_SD_STAT_TC (0x1 << 1) /* Transfer complete status */
#define MMCHS_SD_STAT_TC_UNRAISED (0x0 << 1) /* Transfer not completed */
#define MMCHS_SD_STAT_TC_RAISED (0x1 << 1) /* Transfer completed */
#define MMCHS_SD_IE_ERROR_MASK (0xff << 15 | 0x3 << 24 | 0x03 << 28)