mmc:development

* let busy loops timeout.
* Start using interrupt handlers.
* Allocate the ramdisk only when used.

Change-Id: Ie08d66eefef3c8cd3ee16c04f74a9a50cc12b021
This commit is contained in:
Kees Jongenburger 2012-10-19 15:44:10 +02:00 committed by Kees Jongenburger
parent c31c70743a
commit 3de9b14567
6 changed files with 286 additions and 109 deletions

View file

@ -6,6 +6,8 @@
#include <minix/driver.h> #include <minix/driver.h>
#include <minix/blockdriver.h> #include <minix/blockdriver.h>
#include <minix/drvlib.h> #include <minix/drvlib.h>
#include <minix/minlib.h>
/* system headers */ /* system headers */
#include <sys/ioc_disk.h> /* disk IOCTL's */ #include <sys/ioc_disk.h> /* disk IOCTL's */
@ -24,25 +26,13 @@
/* used for logging */ /* used for logging */
static struct mmclog log = { static struct mmclog log = {
.name = "mmc_block", .name = "mmc_block",
.log_level = LEVEL_DEBUG, .log_level = LEVEL_INFO,
.log_func = default_log .log_func = default_log
}; };
/* holding the current host controller */ /* holding the current host controller */
static struct mmc_host host; static struct mmc_host host;
/*@TODO REMOVE THIS */
void
read_tsc_64(u64_t * t)
{
}
u32_t
tsc_64_to_micros(u64_t tsc)
{
return 0;
}
#define SUB_PER_DRIVE (NR_PARTITIONS * NR_PARTITIONS) #define SUB_PER_DRIVE (NR_PARTITIONS * NR_PARTITIONS)
#define NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE) #define NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE)
@ -70,12 +60,18 @@ static void sef_local_startup();
static int block_system_event_cb(int type, sef_init_info_t * info); static int block_system_event_cb(int type, sef_init_info_t * info);
static void block_signal_handler_cb(int signo); static void block_signal_handler_cb(int signo);
static int apply_env(); void
bdr_alarm(clock_t stamp)
{
mmc_log_debug(&log, "alarm %d\n", stamp);
}
static int apply_env();
static void hw_intr(unsigned int irqs);
#if 0
/* set the global logging level */ /* set the global logging level */
static void set_log_level(int level); static void set_log_level(int level);
#endif
/* Entry points for the BLOCK driver. */ /* Entry points for the BLOCK driver. */
static struct blockdriver mmc_driver = { static struct blockdriver mmc_driver = {
@ -87,20 +83,28 @@ static struct blockdriver mmc_driver = {
NULL, /* no need to clean up (yet) */ NULL, /* no need to clean up (yet) */
block_part, /* return partition information */ block_part, /* return partition information */
NULL, /* no geometry */ NULL, /* no geometry */
NULL, /* no interrupt processing */ hw_intr, /* left over interrupts */
NULL, /* no alarm processing */ bdr_alarm, /* no alarm processing */
NULL, /* no processing of other messages */ NULL, /* no processing of other messages */
NULL /* no threading support */ NULL /* no threading support */
}; };
static void
hw_intr(unsigned int irqs)
{
mmc_log_debug(&log, "Hardware inter left over\n");
host.hw_intr(irqs);
}
static int static int
apply_env() apply_env()
{ {
long v;
/* apply the env setting passed to this driver parameters accepted /* apply the env setting passed to this driver parameters accepted
* log_level=[0-4] (NONE,WARNING,INFO,DEBUG,TRACE) instance=[0-3] * log_level=[0-4] (NONE,WARN,INFO,DEBUG,TRACE) instance=[0-3]
* instance/bus number to use for this driver Passing these arguments * instance/bus number to use for this driver Passing these arguments
* is done when starting the driver using the service command in the * is done when starting the driver using the service command in the
* following way service up /sbin/mmc -args "log_level=2 instance=1 * following way service up /sbin/mmc -args "log_level=2 instance=1
* driver=dummy" -dev /dev/c2d0 */ * driver=dummy" -dev /dev/c2d0 */
char driver[16]; char driver[16];
memset(driver, '\0', 16); memset(driver, '\0', 16);
@ -115,10 +119,6 @@ apply_env()
} else { } else {
mmc_log_warn(&log, "Unknown driver %s\n", driver); mmc_log_warn(&log, "Unknown driver %s\n", driver);
} }
#if 0
long v;
/* The following code(env_parse) uses strtol.c and needs __aeabi_idiv */
/* @TODO: re-enable this function when __aeabi_idiv will be present */
/* Initialize the verbosity level. */ /* Initialize the verbosity level. */
v = 0; v = 0;
if (env_parse("log_level", "d", 0, &v, LEVEL_NONE, if (env_parse("log_level", "d", 0, &v, LEVEL_NONE,
@ -133,7 +133,6 @@ apply_env()
mmc_log_warn(&log, "Failed to set mmc instance to %d\n", v); mmc_log_warn(&log, "Failed to set mmc instance to %d\n", v);
return -1; /* NOT OK */ return -1; /* NOT OK */
} }
#endif
return OK; return OK;
} }
@ -185,27 +184,27 @@ block_open(dev_t minor, int access)
partition(&mmc_driver, 0 /* first card on bus */ , P_PRIMARY, partition(&mmc_driver, 0 /* first card on bus */ , P_PRIMARY,
0 /* atapi device?? */ ); 0 /* atapi device?? */ );
mmc_log_debug(&log, "descr \toffset(bytes) size(bytes)\n", minor); mmc_log_trace(&log, "descr \toffset(bytes) size(bytes)\n", minor);
mmc_log_debug(&log, "disk %d\t0x%016llx 0x%016llx\n", i, mmc_log_trace(&log, "disk %d\t0x%016llx 0x%016llx\n", i,
slot->card.part[0].dv_base, slot->card.part[0].dv_size); slot->card.part[0].dv_base, slot->card.part[0].dv_size);
for (i = 1; i < 5; i++) { for (i = 1; i < 5; i++) {
if (slot->card.part[i].dv_size == 0) if (slot->card.part[i].dv_size == 0)
continue; continue;
part_count++; part_count++;
mmc_log_debug(&log, "part %d\t0x%016llx 0x%016llx\n", i, mmc_log_trace(&log, "part %d\t0x%016llx 0x%016llx\n", i,
slot->card.part[i].dv_base, slot->card.part[i].dv_size); slot->card.part[i].dv_base, slot->card.part[i].dv_size);
for (j = 0; j < 4; j++) { for (j = 0; j < 4; j++) {
if (slot->card.subpart[(i - 1) * 4 + j].dv_size == 0) if (slot->card.subpart[(i - 1) * 4 + j].dv_size == 0)
continue; continue;
sub_part_count++; sub_part_count++;
mmc_log_debug(&log, mmc_log_trace(&log,
" sub %d/%d\t0x%016llx 0x%016llx\n", i, j, " 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_base,
slot->card.subpart[(i - 1) * 4 + j].dv_size); slot->card.subpart[(i - 1) * 4 + j].dv_size);
} }
} }
mmc_log_info(&log, "Found %d partitions and %d sub partitions\n", mmc_log_debug(&log, "Found %d partitions and %d sub partitions\n",
part_count, sub_part_count); part_count, sub_part_count);
slot->card.open_ct++; slot->card.open_ct++;
assert(slot->card.open_ct == 1); assert(slot->card.open_ct == 1);
@ -312,7 +311,7 @@ block_transfer(dev_t minor, /* minor device number */
/* Unknown device */ /* Unknown device */
return ENXIO; return ENXIO;
} }
mmc_log_trace(&log, "I/O %d %s 0x%llx\n", minor, mmc_log_trace(&log, "I/O on minor(%d) %s at 0x%016llx\n", minor,
(do_write) ? "Write" : "Read", position); (do_write) ? "Write" : "Read", position);
slot = get_slot(minor); slot = get_slot(minor);
@ -636,20 +635,18 @@ get_slot(dev_t minor)
} }
} }
#if 0
static void static void
set_log_level(int level) set_log_level(int level)
{ {
if (level < 0 || level >= 4) { if (level < 0 || level >= 4) {
return; return;
} }
mmc_log_debug(&log, "Setting verbosity level to %d\n", level); mmc_log_info(&log, "Setting verbosity level to %d\n", level);
log.log_level = level; log.log_level = level;
if (host.set_log_level) { if (host.set_log_level) {
host.set_log_level(level); host.set_log_level(level);
} }
} }
#endif
int int
main(int argc, char **argv) main(int argc, char **argv)
@ -657,7 +654,6 @@ main(int argc, char **argv)
/* Set and apply the environment */ /* Set and apply the environment */
env_setargs(argc, argv); env_setargs(argc, argv);
sef_local_startup(); sef_local_startup();
blockdriver_task(&mmc_driver); blockdriver_task(&mmc_driver);
return EXIT_SUCCESS; return EXIT_SUCCESS;

View file

@ -117,6 +117,9 @@ struct mmc_host
/* Release the card */ /* Release the card */
int (*card_release) (struct sd_card * card); int (*card_release) (struct sd_card * card);
/* Additional hardware interrupts */
void (*hw_intr) (unsigned int irqs);
/* read count blocks into existing buf */ /* read count blocks into existing buf */
int (*read) (struct sd_card * card, int (*read) (struct sd_card * card,
uint32_t blknr, uint32_t count, unsigned char *buf); uint32_t blknr, uint32_t count, unsigned char *buf);

View file

@ -1,11 +1,13 @@
/* kernel headers */ /* kernel headers */
#include <minix/blockdriver.h> #include <minix/blockdriver.h>
#include <minix/minlib.h>
/* usr headers */ /* usr headers */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <assert.h> #include <assert.h>
#include <unistd.h>
/* local headers */ /* local headers */
#include "mmclog.h" #include "mmclog.h"
@ -17,14 +19,14 @@
*/ */
static struct mmclog log = { static struct mmclog log = {
.name = "mmc_host_memory", .name = "mmc_host_memory",
.log_level = LEVEL_TRACE, .log_level = LEVEL_INFO,
.log_func = default_log .log_func = default_log
}; };
/* This is currently a dummy driver using an in-memory structure */ /* This is currently a dummy driver using an in-memory structure */
#define DUMMY_SIZE_IN_BLOCKS 0xFFFu #define DUMMY_SIZE_IN_BLOCKS 0xFFFFFu
#define DUMMY_BLOCK_SIZE 512 #define DUMMY_BLOCK_SIZE 512
static char dummy_data[DUMMY_BLOCK_SIZE * DUMMY_SIZE_IN_BLOCKS]; static char *dummy_data = NULL;
static struct sd_card * static struct sd_card *
init_dummy_sdcard(struct sd_slot *slot) init_dummy_sdcard(struct sd_slot *slot)
@ -35,6 +37,13 @@ init_dummy_sdcard(struct sd_slot *slot)
assert(slot != NULL); assert(slot != NULL);
mmc_log_info(&log, "Using a dummy card \n"); mmc_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) {
panic
("Failed to allocate data for dummy mmc driver\n");
}
}
card = &slot->card; card = &slot->card;
memset(card, 0, sizeof(struct sd_card)); memset(card, 0, sizeof(struct sd_card));

View file

@ -2,7 +2,9 @@
#include <minix/blockdriver.h> #include <minix/blockdriver.h>
#include <minix/com.h> #include <minix/com.h>
#include <minix/vm.h> #include <minix/vm.h>
#include <minix/spin.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/time.h>
/* usr headers */ /* usr headers */
#include <assert.h> #include <assert.h>
@ -12,6 +14,7 @@
#include <string.h> #include <string.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <unistd.h>
/* local headers */ /* local headers */
#include "mmclog.h" #include "mmclog.h"
@ -25,6 +28,14 @@
/* omap /hardware related */ /* omap /hardware related */
#include "omap_mmc.h" #include "omap_mmc.h"
#define USE_INTR
#ifdef USE_INTR
static int hook_id = 1;
#define OMAP3_MMC1_IRQ 83 /* MMC/SD module 1 */
#endif
#define SANE_TIMEOUT 500000 /* 500 MS */
/* /*
* Define a structure to be used for logging * Define a structure to be used for logging
*/ */
@ -38,14 +49,14 @@ static struct mmclog log = {
#define BIT(x)(0x1 << x) #define BIT(x)(0x1 << x)
/* Write a uint32_t value to a memory address. */ /* Write a uint32_t value to a memory address. */
inline void static inline void
write32(uint32_t address, uint32_t value) write32(uint32_t address, uint32_t value)
{ {
REG(address) = value; REG(address) = value;
} }
/* Read an uint32_t from a memory address */ /* Read an uint32_t from a memory address */
inline uint32_t static inline uint32_t
read32(uint32_t address) read32(uint32_t address)
{ {
@ -53,7 +64,7 @@ read32(uint32_t address)
} }
/* Set a 32 bits value depending on a mask */ /* Set a 32 bits value depending on a mask */
inline void static inline void
set32(uint32_t address, uint32_t mask, uint32_t value) set32(uint32_t address, uint32_t mask, uint32_t value)
{ {
uint32_t val; uint32_t val;
@ -75,13 +86,11 @@ static uint32_t base_address;
int int
mmchs_init(uint32_t instance) mmchs_init(uint32_t instance)
{ {
int counter;
uint32_t value; uint32_t value;
counter = 0;
value = 0; value = 0;
struct minix_mem_range mr; struct minix_mem_range mr;
spin_t spin;
mr.mr_base = MMCHS1_REG_BASE; mr.mr_base = MMCHS1_REG_BASE;
mr.mr_limit = MMCHS1_REG_BASE + 0x400; mr.mr_limit = MMCHS1_REG_BASE + 0x400;
@ -106,11 +115,14 @@ mmchs_init(uint32_t instance)
MMCHS_SD_SYSCONFIG_SOFTRESET); MMCHS_SD_SYSCONFIG_SOFTRESET);
/* Read sysstatus to know when it's done */ /* Read sysstatus to know when it's done */
spin_init(&spin, SANE_TIMEOUT);
while (!(read32(base_address + MMCHS_SD_SYSSTATUS) while (!(read32(base_address + MMCHS_SD_SYSSTATUS)
& MMCHS_SD_SYSSTATUS_RESETDONE)) { & MMCHS_SD_SYSSTATUS_RESETDONE)) {
/* TODO:Add proper delay and escape route */ if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log, "mmc init timeout\n");
counter++; return 1;
}
} }
/* Set SD default capabilities */ /* Set SD default capabilities */
@ -164,12 +176,14 @@ mmchs_init(uint32_t instance)
set32(base_address + MMCHS_SD_HCTL, MMCHS_SD_HCTL_SDBP, set32(base_address + MMCHS_SD_HCTL, MMCHS_SD_HCTL_SDBP,
MMCHS_SD_HCTL_SDBP_ON); MMCHS_SD_HCTL_SDBP_ON);
/* TODO: Add padconf stuff here as documented in the TRM */ // /* TODO: Add padconf/pinmux stuff here as documented in the TRM */
spin_init(&spin, SANE_TIMEOUT);
while ((read32(base_address + MMCHS_SD_HCTL) & MMCHS_SD_HCTL_SDBP) while ((read32(base_address + MMCHS_SD_HCTL) & MMCHS_SD_HCTL_SDBP)
!= MMCHS_SD_HCTL_SDBP_ON) { != MMCHS_SD_HCTL_SDBP_ON) {
/* TODO:Add proper delay and escape route */ if (spin_check(&spin) == FALSE) {
counter++; mmc_log_warn(&log, "mmc init timeout SDBP not set\n");
return 1;
}
} }
/* Enable internal clock and clock to the card */ /* Enable internal clock and clock to the card */
@ -177,16 +191,23 @@ mmchs_init(uint32_t instance)
MMCHS_SD_SYSCTL_ICE_EN); MMCHS_SD_SYSCTL_ICE_EN);
// @TODO Fix external clock enable , this one is very slow // @TODO Fix external clock enable , this one is very slow
// but we first need faster context switching
//set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_CLKD,
// (0x20 << 6));
set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_CLKD, set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_CLKD,
(0x3ff << 6)); (0x5 << 6));
set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_CEN, set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_CEN,
MMCHS_SD_SYSCTL_CEN_EN); MMCHS_SD_SYSCTL_CEN_EN);
counter = 0;
spin_init(&spin, SANE_TIMEOUT);
while ((read32(base_address + MMCHS_SD_SYSCTL) & MMCHS_SD_SYSCTL_ICS) while ((read32(base_address + MMCHS_SD_SYSCTL) & MMCHS_SD_SYSCTL_ICS)
!= MMCHS_SD_SYSCTL_ICS_STABLE) { != MMCHS_SD_SYSCTL_ICS_STABLE) {
/* TODO:Add proper delay and escape route */
counter++; if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log, "clock not stable\n");
return 1;
}
} }
/* /*
@ -216,7 +237,7 @@ mmchs_init(uint32_t instance)
/* command 0 , type other commands not response etc) */ /* command 0 , type other commands not response etc) */
write32(base_address + MMCHS_SD_CMD, 0x00); write32(base_address + MMCHS_SD_CMD, 0x00);
counter = 0; spin_init(&spin, SANE_TIMEOUT);
while ((read32(base_address + MMCHS_SD_STAT) & MMCHS_SD_STAT_CC) while ((read32(base_address + MMCHS_SD_STAT) & MMCHS_SD_STAT_CC)
!= MMCHS_SD_STAT_CC_RAISED) { != MMCHS_SD_STAT_CC_RAISED) {
if (read32(base_address + MMCHS_SD_STAT) & 0x8000) { if (read32(base_address + MMCHS_SD_STAT) & 0x8000) {
@ -225,7 +246,12 @@ mmchs_init(uint32_t instance)
read32(base_address + MMCHS_SD_STAT)); read32(base_address + MMCHS_SD_STAT));
return 1; return 1;
} }
counter++;
if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log,
"Interrupt not raised during init\n");
return 1;
}
} }
/* clear the cc interrupt status */ /* clear the cc interrupt status */
@ -238,41 +264,151 @@ mmchs_init(uint32_t instance)
set32(base_address + MMCHS_SD_CON, MMCHS_SD_CON_INIT, set32(base_address + MMCHS_SD_CON, MMCHS_SD_CON_INIT,
MMCHS_SD_CON_INIT_NOINIT); MMCHS_SD_CON_INIT_NOINIT);
/* Set timeout */
set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_DTO,
MMCHS_SD_SYSCTL_DTO_2POW27);
/* Clean the MMCHS_SD_STAT register */ /* Clean the MMCHS_SD_STAT register */
write32(base_address + MMCHS_SD_STAT, 0xffffffffu); write32(base_address + MMCHS_SD_STAT, 0xffffffffu);
#ifdef USE_INTR
hook_id = 1;
if (sys_irqsetpolicy(OMAP3_MMC1_IRQ, 0, &hook_id) != OK) {
printf("mmc: couldn't set IRQ policy %d\n", OMAP3_MMC1_IRQ);
return 1;
}
/* enable signaling from MMC controller towards interrupt controller */
write32(base_address + MMCHS_SD_ISE, 0xffffffffu);
#endif
return 0; return 0;
} }
static void
mmchs_hw_intr(unsigned int irqs)
{
mmc_log_warn(&log, "Hardware interrupt left over\n");
#ifdef USE_INTR
if (sys_irqenable(&hook_id) != OK)
printf("couldn't re-enable interrupt \n");
#endif
/* Leftover interrupt(s) received; ack it/them. */
}
/*===========================================================================*
* w_intr_wait *
*===========================================================================*/
static int
intr_wait(int mask)
{
long v;
#ifdef USE_INTR
if (sys_irqenable(&hook_id) != OK)
printf("Failed to enable irqenable irq\n");
/* Wait for a task completion interrupt. */
message m;
int ipc_status;
int ticks = SANE_TIMEOUT * sys_hz() / 1000000;
if (ticks <= 0) ticks =1;
while (1) {
int rr;
sys_setalarm(ticks, 0);
if ((rr = driver_receive(ANY, &m, &ipc_status)) != OK) {
panic("driver_receive failed: %d", rr);
};
if (is_ipc_notify(ipc_status)) {
switch (_ENDPOINT_P(m.m_source)) {
case CLOCK:
/* Timeout. */
// w_timeout(); /* a.o. set w_status */
mmc_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, "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;
}
default:
/*
* unhandled message. queue it and
* handle it in the blockdriver loop.
*/
blockdriver_mq_queue(&m, ipc_status);
}
} else {
mmc_log_debug(&log, "Other\n");
/*
* unhandled message. queue it and handle it in the
* blockdriver loop.
*/
blockdriver_mq_queue(&m, ipc_status);
}
}
sys_setalarm(0, 0); /* cancel the alarm */
#else
spin_t spin;
spin_init(&spin, SANE_TIMEOUT);
/* Wait for completion */
int counter =0;
while (1 == 1) {
counter ++;
v = read32(base_address + MMCHS_SD_STAT);
if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log, "Timeout waiting for interrupt (%d) value 0x%08x mask 0x%08x\n",counter, v,mask);
return 1;
}
if (v & mask) {
return 0;
} else if (v & 0xFF00) {
mmc_log_debug(&log, "unexpected HW interrupt (%d) 0x%08x mask 0x%08x\n", v, mask);
return 1;
}
}
return 1; /* unreached */
#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 int
mmchs_send_cmd(uint32_t command, uint32_t arg) mmchs_send_cmd(uint32_t command, uint32_t arg)
{ {
int count = 0;
/* Read current interrupt status and fail it an interrupt is already /* Read current interrupt status and fail it an interrupt is already
* asserted */ * asserted */
if ((read32(base_address + MMCHS_SD_STAT) & 0xffffu)) {
mmc_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);
// return 1;
}
/* Set arguments */ /* Set arguments */
write32(base_address + MMCHS_SD_ARG, arg); write32(base_address + MMCHS_SD_ARG, arg);
/* Set command */ /* Set command */
set32(base_address + MMCHS_SD_CMD, MMCHS_SD_CMD_MASK, command); set32(base_address + MMCHS_SD_CMD, MMCHS_SD_CMD_MASK, command);
/* Wait for completion */ if (intr_wait(MMCHS_SD_STAT_CC | MMCHS_SD_IE_TC_ENABLE_CLEAR)) {
while ((read32(base_address + MMCHS_SD_STAT) & 0xffffu) == 0x0) { intr_assert(MMCHS_SD_STAT_CC);
count++; mmc_log_warn(&log, "Failure waiting for interrupt\n");
}
if (read32(base_address + MMCHS_SD_STAT) & 0x8000) {
mmc_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);
return 1; return 1;
} }
@ -281,16 +417,20 @@ mmchs_send_cmd(uint32_t command, uint32_t arg)
/* /*
* Command with busy response *CAN* also set the TC bit if they exit busy * Command with busy response *CAN* also set the TC bit if they exit busy
*/ */
while ((read32(base_address + MMCHS_SD_STAT) if ((read32(base_address + MMCHS_SD_STAT)
& MMCHS_SD_IE_TC_ENABLE_ENABLE) == 0) { & MMCHS_SD_IE_TC_ENABLE_ENABLE) == 0) {
count++; mmc_log_warn(&log, "TC should be raised\n");
} }
write32(base_address + MMCHS_SD_STAT, write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_TC_ENABLE_CLEAR); MMCHS_SD_IE_TC_ENABLE_CLEAR);
}
/* clear the cc status */ if (intr_wait(MMCHS_SD_STAT_CC | MMCHS_SD_IE_TC_ENABLE_CLEAR)) {
write32(base_address + MMCHS_SD_STAT, MMCHS_SD_IE_CC_ENABLE_CLEAR); intr_assert(MMCHS_SD_STAT_CC);
mmc_log_warn(&log, "Failure waiting for clear\n");
return 1;
}
}
intr_assert(MMCHS_SD_STAT_CC);
return 0; return 0;
} }
@ -383,10 +523,11 @@ card_identification()
int int
card_query_voltage_and_type(struct sd_card_regs *card) card_query_voltage_and_type(struct sd_card_regs *card)
{ {
spin_t spin;
command.cmd = MMC_APP_CMD; command.cmd = MMC_APP_CMD;
command.resp_type = RESP_LEN_48; command.resp_type = RESP_LEN_48;
command.args = MMC_ARG_RCA(0x0); /* RCA=0000 */ command.args = MMC_ARG_RCA(0x0); /* RCA=0000 */
if (mmc_send_cmd(&command)) { if (mmc_send_cmd(&command)) {
return 1; return 1;
} }
@ -405,6 +546,7 @@ card_query_voltage_and_type(struct sd_card_regs *card)
return 1; return 1;
} }
/* @todo wait for max 1 ms */ /* @todo wait for max 1 ms */
spin_init(&spin, SANE_TIMEOUT);
while (!(command.resp[0] & MMC_OCR_MEM_READY)) { while (!(command.resp[0] & MMC_OCR_MEM_READY)) {
command.cmd = MMC_APP_CMD; command.cmd = MMC_APP_CMD;
command.resp_type = RESP_LEN_48; command.resp_type = RESP_LEN_48;
@ -432,6 +574,10 @@ card_query_voltage_and_type(struct sd_card_regs *card)
if ((command.resp[0] & MMC_OCR_MEM_READY)) { if ((command.resp[0] & MMC_OCR_MEM_READY)) {
break; break;
} }
if (spin_check(&spin) == FALSE) {
mmc_log_warn(&log,
"TIMEOUT waiting for the SD card\n");
}
} }
card->ocr = command.resp[3]; card->ocr = command.resp[3];
@ -536,15 +682,18 @@ read_single_block(struct sd_card_regs *card,
| MMCHS_SD_CMD_MSBS_SINGLE /* single block */ | MMCHS_SD_CMD_MSBS_SINGLE /* single block */
| MMCHS_SD_CMD_DDIR_READ /* read data from card */ | MMCHS_SD_CMD_DDIR_READ /* read data from card */
, blknr)) { , blknr)) {
mmc_log_warn(&log, "Error sending command\n");
return 1; return 1;
} }
while ((read32(base_address + MMCHS_SD_STAT) if (intr_wait(MMCHS_SD_IE_BRR_ENABLE_ENABLE)) {
& MMCHS_SD_IE_BRR_ENABLE_ENABLE) == 0) { intr_assert(MMCHS_SD_IE_BRR_ENABLE_ENABLE);
count++; mmc_log_warn(&log, "Timeout waiting for interrupt\n");
return 1;
} }
if (!(read32(base_address + MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BRE_EN)) { 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 return 1; /* We are not allowed to read data from the
* data buffer */ * data buffer */
} }
@ -558,11 +707,12 @@ read_single_block(struct sd_card_regs *card,
} }
/* Wait for TC */ /* Wait for TC */
while ((read32(base_address + if (intr_wait(MMCHS_SD_IE_TC_ENABLE_ENABLE)) {
MMCHS_SD_STAT) & MMCHS_SD_IE_TC_ENABLE_ENABLE) intr_assert(MMCHS_SD_IE_TC_ENABLE_ENABLE);
== 0) { mmc_log_warn(&log, "Timeout waiting for interrupt\n");
count++; return 1;
} }
write32(base_address + MMCHS_SD_STAT, MMCHS_SD_IE_TC_ENABLE_CLEAR); write32(base_address + MMCHS_SD_STAT, MMCHS_SD_IE_TC_ENABLE_CLEAR);
/* clear and disable the bbr interrupt */ /* clear and disable the bbr interrupt */
@ -579,17 +729,21 @@ write_single_block(struct sd_card_regs *card,
uint32_t count; uint32_t count;
uint32_t value; uint32_t value;
count = 0; if ((read32(base_address + MMCHS_SD_STAT) & 0xffffu)) {
mmc_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);
// return 1;
}
set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_BWR_ENABLE, set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_BWR_ENABLE,
MMCHS_SD_IE_BWR_ENABLE_ENABLE); MMCHS_SD_IE_BWR_ENABLE_ENABLE);
count = 0;
// set32(base_address + MMCHS_SD_IE, 0xfff , 0xfff); // set32(base_address + MMCHS_SD_IE, 0xfff , 0xfff);
set32(base_address + MMCHS_SD_BLK, MMCHS_SD_BLK_BLEN, 512); set32(base_address + MMCHS_SD_BLK, MMCHS_SD_BLK_BLEN, 512);
/* Set timeout */
set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_DTO,
MMCHS_SD_SYSCTL_DTO_2POW27);
/* write single block */ /* write single block */
if (mmchs_send_cmd(MMCHS_SD_CMD_INDX_CMD(MMC_WRITE_BLOCK_SINGLE) if (mmchs_send_cmd(MMCHS_SD_CMD_INDX_CMD(MMC_WRITE_BLOCK_SINGLE)
| MMCHS_SD_CMD_DP_DATA /* Command with data transfer */ | MMCHS_SD_CMD_DP_DATA /* Command with data transfer */
@ -597,19 +751,32 @@ write_single_block(struct sd_card_regs *card,
| MMCHS_SD_CMD_MSBS_SINGLE /* single block */ | MMCHS_SD_CMD_MSBS_SINGLE /* single block */
| MMCHS_SD_CMD_DDIR_WRITE /* write to the card */ | MMCHS_SD_CMD_DDIR_WRITE /* write to the card */
, blknr)) { , blknr)) {
mmc_log_warn(&log, "Write single block command failed\n");
mmc_log_trace(&log, "STAT=(0x%08x)\n",
read32(base_address + MMCHS_SD_STAT));
return 1; return 1;
} }
/* Wait for the MMCHS_SD_IE_BWR_ENABLE interrupt */ /* Wait for the MMCHS_SD_IE_BWR_ENABLE interrupt */
while ((read32(base_address + if (intr_wait(MMCHS_SD_IE_BWR_ENABLE)) {
MMCHS_SD_STAT) & MMCHS_SD_IE_BWR_ENABLE) == 0) { intr_assert(MMCHS_SD_IE_BWR_ENABLE);
count++; 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)) { 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 */ return 1; /* not ready to write data */
} }
for (count = 0; count < 512; count += 4) { 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) = buf[count]; *((char *) &value) = buf[count];
*((char *) &value + 1) = buf[count + 1]; *((char *) &value + 1) = buf[count + 1];
*((char *) &value + 2) = buf[count + 2]; *((char *) &value + 2) = buf[count + 2];
@ -617,17 +784,14 @@ write_single_block(struct sd_card_regs *card,
write32(base_address + MMCHS_SD_DATA, value); write32(base_address + MMCHS_SD_DATA, value);
} }
/* Wait for TC */ /* Wait for TC */
while ((read32(base_address + if (intr_wait(MMCHS_SD_IE_TC_ENABLE_CLEAR)) {
MMCHS_SD_STAT) & MMCHS_SD_IE_TC_ENABLE_ENABLE) intr_assert(MMCHS_SD_IE_TC_ENABLE_CLEAR);
== 0) { mmc_log_warn(&log, "(Write) Timeout waiting for transfer complete\n");
count++; return 1;
} }
write32(base_address + MMCHS_SD_STAT, MMCHS_SD_IE_TC_ENABLE_CLEAR); intr_assert(MMCHS_SD_IE_TC_ENABLE_CLEAR);
write32(base_address + MMCHS_SD_STAT, MMCHS_SD_IE_CC_ENABLE_CLEAR); /* finished.
*/
/* clear the bwr interrupt FIXME is this right when writing? */
write32(base_address + MMCHS_SD_STAT, MMCHS_SD_IE_BWR_ENABLE_CLEAR);
set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_BWR_ENABLE, set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_BWR_ENABLE,
MMCHS_SD_IE_BWR_ENABLE_DISABLE); MMCHS_SD_IE_BWR_ENABLE_DISABLE);
return 0; return 0;
@ -661,7 +825,7 @@ mmchs_host_set_instance(struct mmc_host *host, int instance)
int int
mmchs_host_reset(struct mmc_host *host) mmchs_host_reset(struct mmc_host *host)
{ {
mmchs_init(1); // mmchs_init(1);
return 0; return 0;
} }
@ -675,7 +839,7 @@ mmchs_card_detect(struct sd_slot *slot)
struct sd_card * struct sd_card *
mmchs_card_initialize(struct sd_slot *slot) mmchs_card_initialize(struct sd_slot *slot)
{ {
mmchs_init(1); // mmchs_init(1);
struct sd_card *card; struct sd_card *card;
card = &slot->card; card = &slot->card;
@ -785,6 +949,7 @@ host_initialize_host_structure_mmchs(struct mmc_host *host)
host->card_detect = mmchs_card_detect; host->card_detect = mmchs_card_detect;
host->card_initialize = mmchs_card_initialize; host->card_initialize = mmchs_card_initialize;
host->card_release = mmchs_card_release; host->card_release = mmchs_card_release;
host->hw_intr = mmchs_hw_intr;
host->read = mmchs_host_read; host->read = mmchs_host_read;
host->write = mmchs_host_write; host->write = mmchs_host_write;

View file

@ -23,6 +23,7 @@
#define MMCHS_SD_SYSCTL 0x22c /* SD System control (reset,clocks and timeout) */ #define MMCHS_SD_SYSCTL 0x22c /* SD System control (reset,clocks and timeout) */
#define MMCHS_SD_STAT 0x230 /* SD Interrupt status */ #define MMCHS_SD_STAT 0x230 /* SD Interrupt status */
#define MMCHS_SD_IE 0x234 /* SD Interrupt Enable register */ #define MMCHS_SD_IE 0x234 /* SD Interrupt Enable register */
#define MMCHS_SD_ISE 0x238 /* SD Interrupt Signal Enable register */
#define MMCHS_SD_CAPA 0x240 /* Capabilities of the host controller */ #define MMCHS_SD_CAPA 0x240 /* Capabilities of the host controller */
#define MMCHS_SD_CUR_CAPA 0x248 /* Current capabilities of the host controller */ #define MMCHS_SD_CUR_CAPA 0x248 /* Current capabilities of the host controller */
@ -146,7 +147,7 @@
#define MMCHS_SD_SYSCTL_DTO (0xf << 16) /* Data timeout counter */ #define MMCHS_SD_SYSCTL_DTO (0xf << 16) /* Data timeout counter */
#define MMCHS_SD_SYSCTL_DTO_2POW13 (0x0 << 16) /* TCF x 2^13 */ #define MMCHS_SD_SYSCTL_DTO_2POW13 (0x0 << 16) /* TCF x 2^13 */
#define MMCHS_SD_SYSCTL_DTO_2POW14 (0x1 << 16) /* TCF x 2^14 */ #define MMCHS_SD_SYSCTL_DTO_2POW14 (0x1 << 16) /* TCF x 2^14 */
#define MMCHS_SD_SYSCTL_DTO_2POW27 (0x3 << 16) /* TCF x 2^27 */ #define MMCHS_SD_SYSCTL_DTO_2POW27 (0xe << 16) /* TCF x 2^27 */
#define MMCHS_SD_STAT_ERRI (0x01 << 15) /* Error interrupt */ #define MMCHS_SD_STAT_ERRI (0x01 << 15) /* Error interrupt */
#define MMCHS_SD_STAT_ERROR_MASK (0xff << 15 | 0x3 << 24 | 0x03 << 28) #define MMCHS_SD_STAT_ERROR_MASK (0xff << 15 | 0x3 << 24 | 0x03 << 28)

View file

@ -549,6 +549,9 @@ service mmc
{ {
system system
PRIVCTL # 4 PRIVCTL # 4
IRQCTL # 19
;
irq 83; # IRQ 83 allowed
; ;
}; };