#include <minix/driver.h>
#include <minix/blockdriver.h>
#include <minix/drvlib.h>
+#include <minix/minlib.h>
+
/* system headers */
#include <sys/ioc_disk.h> /* disk IOCTL's */
/* used for logging */
static struct mmclog log = {
.name = "mmc_block",
- .log_level = LEVEL_DEBUG,
+ .log_level = LEVEL_INFO,
.log_func = default_log
};
/* holding the current host controller */
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 NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE)
static int block_system_event_cb(int type, sef_init_info_t * info);
static void block_signal_handler_cb(int signo);
+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 */
static void set_log_level(int level);
-#endif
/* Entry points for the BLOCK driver. */
static struct blockdriver mmc_driver = {
NULL, /* no need to clean up (yet) */
block_part, /* return partition information */
NULL, /* no geometry */
- NULL, /* no interrupt processing */
- NULL, /* no alarm processing */
+ hw_intr, /* left over interrupts */
+ bdr_alarm, /* no alarm processing */
NULL, /* no processing of other messages */
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
apply_env()
{
+ long v;
/* 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
* 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 */
char driver[16];
memset(driver, '\0', 16);
} else {
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. */
v = 0;
if (env_parse("log_level", "d", 0, &v, LEVEL_NONE,
mmc_log_warn(&log, "Failed to set mmc instance to %d\n", v);
return -1; /* NOT OK */
}
-#endif
return OK;
}
partition(&mmc_driver, 0 /* first card on bus */ , P_PRIMARY,
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);
for (i = 1; i < 5; i++) {
if (slot->card.part[i].dv_size == 0)
continue;
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);
for (j = 0; j < 4; j++) {
if (slot->card.subpart[(i - 1) * 4 + j].dv_size == 0)
continue;
sub_part_count++;
- mmc_log_debug(&log,
+ mmc_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_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);
slot->card.open_ct++;
assert(slot->card.open_ct == 1);
/* Unknown device */
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);
slot = get_slot(minor);
}
}
-#if 0
static void
set_log_level(int level)
{
if (level < 0 || level >= 4) {
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;
if (host.set_log_level) {
host.set_log_level(level);
}
}
-#endif
int
main(int argc, char **argv)
/* Set and apply the environment */
env_setargs(argc, argv);
-
sef_local_startup();
blockdriver_task(&mmc_driver);
return EXIT_SUCCESS;
#include <minix/blockdriver.h>
#include <minix/com.h>
#include <minix/vm.h>
+#include <minix/spin.h>
#include <sys/mman.h>
+#include <sys/time.h>
/* usr headers */
#include <assert.h>
#include <string.h>
#include <inttypes.h>
#include <limits.h>
+#include <unistd.h>
/* local headers */
#include "mmclog.h"
/* omap /hardware related */
#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 BIT(x)(0x1 << x)
/* Write a uint32_t value to a memory address. */
-inline void
+static inline void
write32(uint32_t address, uint32_t value)
{
REG(address) = value;
}
/* Read an uint32_t from a memory address */
-inline uint32_t
+static inline uint32_t
read32(uint32_t address)
{
}
/* Set a 32 bits value depending on a mask */
-inline void
+static inline void
set32(uint32_t address, uint32_t mask, uint32_t value)
{
uint32_t val;
int
mmchs_init(uint32_t instance)
{
- int counter;
- uint32_t value;
- counter = 0;
+ uint32_t value;
value = 0;
-
struct minix_mem_range mr;
+ spin_t spin;
mr.mr_base = MMCHS1_REG_BASE;
mr.mr_limit = MMCHS1_REG_BASE + 0x400;
MMCHS_SD_SYSCONFIG_SOFTRESET);
/* Read sysstatus to know when it's done */
+
+ spin_init(&spin, SANE_TIMEOUT);
while (!(read32(base_address + MMCHS_SD_SYSSTATUS)
& MMCHS_SD_SYSSTATUS_RESETDONE)) {
- /* TODO:Add proper delay and escape route */
-
- counter++;
+ if (spin_check(&spin) == FALSE) {
+ mmc_log_warn(&log, "mmc init timeout\n");
+ return 1;
+ }
}
/* Set SD default capabilities */
set32(base_address + MMCHS_SD_HCTL, MMCHS_SD_HCTL_SDBP,
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)
!= MMCHS_SD_HCTL_SDBP_ON) {
- /* TODO:Add proper delay and escape route */
- counter++;
+ if (spin_check(&spin) == FALSE) {
+ mmc_log_warn(&log, "mmc init timeout SDBP not set\n");
+ return 1;
+ }
}
/* Enable internal clock and clock to the card */
MMCHS_SD_SYSCTL_ICE_EN);
// @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,
- (0x3ff << 6));
+ (0x5 << 6));
+
set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_CEN,
MMCHS_SD_SYSCTL_CEN_EN);
- counter = 0;
+ spin_init(&spin, SANE_TIMEOUT);
while ((read32(base_address + MMCHS_SD_SYSCTL) & MMCHS_SD_SYSCTL_ICS)
!= 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;
+ }
}
/*
/* command 0 , type other commands not response etc) */
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)
!= MMCHS_SD_STAT_CC_RAISED) {
if (read32(base_address + MMCHS_SD_STAT) & 0x8000) {
read32(base_address + MMCHS_SD_STAT));
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 */
set32(base_address + MMCHS_SD_CON, MMCHS_SD_CON_INIT,
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 */
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;
}
+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
mmchs_send_cmd(uint32_t command, uint32_t arg)
{
- int count = 0;
/* Read current interrupt status and fail it an interrupt is already
* 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 */
write32(base_address + MMCHS_SD_ARG, arg);
/* Set command */
set32(base_address + MMCHS_SD_CMD, MMCHS_SD_CMD_MASK, command);
- /* Wait for completion */
- while ((read32(base_address + MMCHS_SD_STAT) & 0xffffu) == 0x0) {
- count++;
- }
-
- 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);
+ 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");
return 1;
}
/*
* 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) {
- count++;
+ mmc_log_warn(&log, "TC should be raised\n");
}
write32(base_address + MMCHS_SD_STAT,
MMCHS_SD_IE_TC_ENABLE_CLEAR);
- }
- /* clear the cc status */
- write32(base_address + MMCHS_SD_STAT, MMCHS_SD_IE_CC_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;
+ }
+ }
+ intr_assert(MMCHS_SD_STAT_CC);
return 0;
}
int
card_query_voltage_and_type(struct sd_card_regs *card)
{
-
+ 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;
}
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;
if ((command.resp[0] & MMC_OCR_MEM_READY)) {
break;
}
+ if (spin_check(&spin) == FALSE) {
+ mmc_log_warn(&log,
+ "TIMEOUT waiting for the SD card\n");
+ }
}
card->ocr = command.resp[3];
| MMCHS_SD_CMD_MSBS_SINGLE /* single block */
| MMCHS_SD_CMD_DDIR_READ /* read data from card */
, blknr)) {
+ mmc_log_warn(&log, "Error sending command\n");
return 1;
}
- while ((read32(base_address + MMCHS_SD_STAT)
- & MMCHS_SD_IE_BRR_ENABLE_ENABLE) == 0) {
- count++;
+ 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 */
}
}
/* Wait for TC */
- while ((read32(base_address +
- MMCHS_SD_STAT) & MMCHS_SD_IE_TC_ENABLE_ENABLE)
- == 0) {
- count++;
+ 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 */
uint32_t count;
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,
MMCHS_SD_IE_BWR_ENABLE_ENABLE);
+ count = 0;
+
// set32(base_address + MMCHS_SD_IE, 0xfff , 0xfff);
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 */
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_MSBS_SINGLE /* single block */
| MMCHS_SD_CMD_DDIR_WRITE /* write to the card */
, 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;
}
/* Wait for the MMCHS_SD_IE_BWR_ENABLE interrupt */
- while ((read32(base_address +
- MMCHS_SD_STAT) & MMCHS_SD_IE_BWR_ENABLE) == 0) {
- count++;
+ 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) = buf[count];
*((char *) &value + 1) = buf[count + 1];
*((char *) &value + 2) = buf[count + 2];
write32(base_address + MMCHS_SD_DATA, value);
}
+
/* Wait for TC */
- while ((read32(base_address +
- MMCHS_SD_STAT) & MMCHS_SD_IE_TC_ENABLE_ENABLE)
- == 0) {
- count++;
+ 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;
}
- write32(base_address + MMCHS_SD_STAT, 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);
+ 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 0;
int
mmchs_host_reset(struct mmc_host *host)
{
- mmchs_init(1);
+ // mmchs_init(1);
return 0;
}
struct sd_card *
mmchs_card_initialize(struct sd_slot *slot)
{
- mmchs_init(1);
+ // mmchs_init(1);
struct sd_card *card;
card = &slot->card;
host->card_detect = mmchs_card_detect;
host->card_initialize = mmchs_card_initialize;
host->card_release = mmchs_card_release;
+ host->hw_intr = mmchs_hw_intr;
host->read = mmchs_host_read;
host->write = mmchs_host_write;