From: Kees Jongenburger Date: Wed, 3 Oct 2012 08:55:40 +0000 (+0200) Subject: MMC driver. X-Git-Tag: v3.2.1~294 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/migration?a=commitdiff_plain;h=49246fcdd50a585ff9e049f0ab9dc352202e31d4;p=minix.git MMC driver. Change-Id: I0d460d63070855df9b11eaf3d33eb7bb89c570f1 --- diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile new file mode 100644 index 000000000..52afdb272 --- /dev/null +++ b/drivers/mmc/Makefile @@ -0,0 +1,17 @@ +# Makefile for the mmc driver. +PROG= mmc +SRCS= mmcblk.c mmchost_mmchs.c mmclog.h sdhcreg.h sdmmcreg.h + +DPADD+= ${LIBBLOCKDRIVER} ${LIBSYS} +LDADD+= -lblockdriver -lsys +CLEANFILES+=.depend mmcblk.d + +# +# This is a system driver. +CPPFLAGS+= -D_SYSTEM=1 + +MAN= + +BINDIR?= /usr/sbin + +.include diff --git a/drivers/mmc/README.txt b/drivers/mmc/README.txt new file mode 100644 index 000000000..cd353ed95 --- /dev/null +++ b/drivers/mmc/README.txt @@ -0,0 +1,56 @@ +This directory contains code to access MMC based devices. + +It was created during the initial port of MINIX to the ARM platform. mmbclk +implements a normal MINIX block device. It uses the interfaces defined in +mmchost.h to perform it's operations. + +mmchost_mmchs is the MMC host controller driver for the TI omap device range. +It contains the logic to access SD cards on that device. + + drivers/mmc + |-- Makefile (The makefile) + |-- mmclog.h (A simpel logging system) + |-- omap_mmc.h (TI Omap register definitions) + |-- sdhcreg.h (BSD headers for the MMC layer) + |-- sdmmcreg.h (BSD headers for the MMC layer) + |-- mmcblk.c (MINIX 3 block device) + |-- mmchost.h (interface between the block device and the MMC layer) + |-- mmchost_mmchs.c (Driver to use on the ARM port/beagle) + '-- README.txt (This file) + + +Future work: +============ + +* Creating a more generic MMC layer +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 +for 4 or 8 bits(for movinands) mode is very welcome. + +* DMA. +The OMAP driver allows the usage of DMA. Adding DMA support will increase the +performance of the driver + +* Removal of the PRIVCTL call. +The MMC driver uses memory mapped IO to perform it's operations. On i386 it is +the pci driver that will grant access to certain piece of memory to a driver. +On the ARM port we lack pci and other self describing busses. In the current +driver the driver will itself grant itself access to the correct piece of +memory but this is unwanted behavior. We currently as thinking about two +possible solutions. The first would be to add the memory ranges in system.conf. +The second would be to modify the PCI driver to grant access to memory based on +a fixed configuration. For example we could use the driver tree to perform +device discovery and granting access to devices. + + +* TODO removal +The driver contains (quite a few) TODO's where the code need to be improved. diff --git a/drivers/mmc/mmcblk.c b/drivers/mmc/mmcblk.c new file mode 100644 index 000000000..c5a15506e --- /dev/null +++ b/drivers/mmc/mmcblk.c @@ -0,0 +1,655 @@ +/* + * Block driver for Multi Media Cards (MMC). + */ +/* kernel headers */ +#include +#include +#include +#include + +/* system headers */ +#include /* disk IOCTL's */ + +/* usr headers */ +#include +#include +#include +#include +#include + +/* local headers */ +#include "mmchost.h" +#include "mmclog.h" + +/* used for logging */ +static struct mmclog log = { + .name = "mmc_block", + .log_level = LEVEL_DEBUG, + .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) + +/* When passing data over a grant one needs to pass + * a buffer to sys_safecopy copybuff is used for that*/ +#define COPYBUFF_SIZE 0x1000 /* 4k buff */ +static unsigned char copybuff[COPYBUFF_SIZE]; + +static struct sd_slot *get_slot(dev_t minor); + +/* Prototypes for the block device */ +static int block_open(dev_t minor, int access); +static int block_close(dev_t minor); +static int block_transfer(dev_t minor, + int do_write, + u64_t position, + endpoint_t endpt, iovec_t * iov, unsigned int nr_req, int flags); + +static int block_ioctl(dev_t minor, + unsigned int request, endpoint_t endpt, cp_grant_id_t grant); +static struct device *block_part(dev_t minor); + +/* System even handling */ +static void sef_local_startup(); +static int block_system_event_cb(int type, sef_init_info_t * info); +static void block_signal_handler_cb(int signo); + +static int apply_env(); + +#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 = { + BLOCKDRIVER_TYPE_DISK, /* handle partition requests */ + block_open, /* open or mount */ + block_close, /* on a close */ + block_transfer, /* does the I/O */ + block_ioctl, /* ioclt's */ + NULL, /* no need to clean up (yet) */ + block_part, /* return partition information */ + NULL, /* no geometry */ + NULL, /* no interrupt processing */ + NULL, /* no alarm processing */ + NULL, /* no processing of other messages */ + NULL /* no threading support */ +}; + +static int +apply_env() +{ +#if 0 + /* @TODO: re-enable this function when __aeabi_idiv will be present + * The following code(env_parse) uses strtol.c and needs __aeabi_idiv */ + /* apply the env setting passed to this driver parameters accepted + * log_level=[0-4] (NONE,WARNING,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/mmcblk -args "log_level=2 + * instance=1" */ + long v; + + /* Initialize the verbosity level. */ + v = 0; + if (env_parse("log_level", "d", 0, &v, LEVEL_NONE, + LEVEL_TRACE) == EP_SET) { + set_log_level(v); + } + + /* Find out which driver instance we are. */ + 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); + return -1; /* NOT OK */ + } +#endif + return OK; +} + +; + +/*===========================================================================* + * block_open * + *===========================================================================*/ +static int +block_open(dev_t minor, int access) +{ + struct sd_slot *slot; + slot = get_slot(minor); + int i, j; + int part_count, sub_part_count; + + i = j = part_count = sub_part_count = 0; + + if (!slot) { + mmc_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"); + return EIO; + } + + /* If we are already open just increase the open count and return */ + 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", + 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); + if (!host.card_initialize(slot)) { + // * TODO: set card state to INVALID until removed? */ + return EIO; + } + + 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_debug(&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, + slot->card.part[i].dv_base, slot->card.part[i].dv_size); + for (j = 0; j < 4; j++) { + if (slot->card.part[i * 4 + j].dv_size == 0) + continue; + sub_part_count++; + mmc_log_debug(&log, + " sub %d/%d\t0x%016llx 0x%016llx\n", i, j, + slot->card.part[i * 4 + j].dv_base, + slot->card.part[i * 4 + j].dv_size); + } + } + mmc_log_info(&log, "Found %d partitions and %d sub partitions\n", + part_count, sub_part_count); + slot->card.open_ct++; + assert(slot->card.open_ct == 1); + return OK; +} + +/*===========================================================================* + * block_close * + *===========================================================================*/ +static int +block_close(dev_t minor) +{ + struct sd_slot *slot; + + slot = get_slot(minor); + if (!slot) { + mmc_log_debug(&log, + "Not handling open on non existing slot\n"); + return EIO; + } + + /* if we arrived here we expect a card to be present, we will need do + * deal with removal later */ + assert(slot->host != NULL); + assert(slot->card.open_ct >= 1); + + /* If this is not the last open count simply decrease the counter and + * return */ + if (slot->card.open_ct > 1) { + slot->card.open_ct--; + mmc_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"); + + /* release the card as check the open_ct should be 0 */ + slot->host->card_release(&slot->card); + assert(slot->card.open_ct == 0); + return OK; +} + +static int +copyto(endpoint_t dst_e, + cp_grant_id_t gr_id, vir_bytes offset, vir_bytes address, size_t bytes) +{ + /* Helper function that used memcpy to copy data when the endpoint == + * SELF */ + if (dst_e == SELF) { + memcpy((char *) gr_id + offset, (char *) address, bytes); + return OK; + } else { + /* Read io_size bytes from our data at the correct * offset + * and write it to the output buffer at 0 */ + return sys_safecopyto(dst_e, gr_id, offset, address, bytes); + } +} + +static int +copyfrom(endpoint_t src_e, + cp_grant_id_t gr_id, vir_bytes offset, vir_bytes address, size_t bytes) +{ + /* Helper function that used memcpy to copy data when the endpoint == + * SELF */ + if (src_e == SELF) { + memcpy((char *) address, (char *) gr_id + offset, bytes); + return OK; + } else { + return sys_safecopyfrom(src_e, gr_id, offset, address, bytes); + } +} + +/*===========================================================================* + * block_transfer * + *===========================================================================*/ +static int +block_transfer(dev_t minor, /* minor device number */ + int do_write, /* read or write? */ + u64_t position, /* offset on device to read or write */ + endpoint_t endpt, /* process doing the request */ + iovec_t * iov, /* pointer to read or write request vector */ + unsigned int nr_req, /* length of request vector */ + int flags /* transfer flags */ + ) +{ + unsigned long counter; + iovec_t *ciov; /* Current IO Vector */ + struct device *dev; /* The device used */ + struct sd_slot *slot; /* The sd slot the requests is pointed to */ + vir_bytes io_size; /* Size to read/write to/from the iov */ + vir_bytes io_offset; /* Size to read/write to/from the iov */ + vir_bytes bytes_written; + + int r, blk_size, i; + + /* Get the current "device" geometry */ + dev = block_part(minor); + if (dev == NULL) { + mmc_log_warn(&log, + "Transfer requested on unknown device minor(%d)\n", minor); + /* Unknown device */ + return ENXIO; + } + mmc_log_trace(&log, "I/O %d %s 0x%llx\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"); + return EINVAL; + } + if (slot->card.blk_size > COPYBUFF_SIZE) { + mmc_log_warn(&log, + "Card block size (%d) exceeds internal buffer size %d\n", + slot->card.blk_size, COPYBUFF_SIZE); + return EINVAL; + } + + /* It is fully up to the driver to decide on restrictions for the + * 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, + "Requests must start at a block boundary" + "(start,block size)=(%016llx,%08x)\n", position, + slot->card.blk_size); + return EINVAL; + } + + blk_size = slot->card.blk_size; + + bytes_written = 0; + + /* Are we trying to start reading past the end */ + if (position >= dev->dv_size) { + mmc_log_warn(&log, "start reading past drive size\n"); + return 0; + }; + + ciov = iov; + /* do some more validation */ + for (counter = 0; counter < nr_req; counter++) { + assert(ciov != NULL); + if (ciov->iov_size % blk_size != 0) { + /* transfer a multiple of blk_size */ + mmc_log_warn(&log, + "Requests must start at a block boundary " + "(start,block size)=(%016llx,%08x)\n", position, + slot->card.blk_size); + return EINVAL; + } + + if (ciov->iov_size == 0 || ciov->iov_size < 0) { + mmc_log_warn(&log, + "Invalid iov size for iov %d of %d size\n", + counter, nr_req, ciov->iov_size); + return EINVAL; + } + ciov++; + } + + ciov = iov; + for (counter = 0; counter < nr_req; counter++) { + /* Assume we are to transfer the amount of data given in the + * input/output vector but ensure we are not doing i/o past + * our own boundaries */ + io_size = ciov->iov_size; + io_offset = position + bytes_written; + + /* Check we are not reading/writing past the end */ + if (position + bytes_written + io_size > dev->dv_size) { + io_size = dev->dv_size - (position + bytes_written); + }; + + mmc_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, + ciov->iov_addr, ciov->iov_size, io_size, io_offset); + /* transfer max one block at the time */ + for (i = 0; i < io_size / blk_size; i++) { + if (do_write) { + /* Read io_size bytes from i/o vector starting + * at 0 and write it to out buffer at the + * correct offset */ + r = copyfrom(endpt, ciov->iov_addr, + i * blk_size, (vir_bytes) copybuff, + blk_size); + if (r != OK) { + mmc_log_warn(&log, + "I/O write error: %s iov(base,size)=(%d,%d)" + " at offset=%d\n", + strerror(_SIGN r), ciov->iov_addr, + ciov->iov_size, io_offset); + return EINVAL; + } + + /* write a single block */ + slot->host->write(&slot->card, + (dev->dv_base / blk_size) + + (io_offset / blk_size) + i, 1, copybuff); + bytes_written += blk_size; + } else { + /* read a single block info copybuff */ + slot->host->read(&slot->card, + (dev->dv_base / blk_size) + + (io_offset / blk_size) + i, 1, copybuff); + /* Read io_size bytes from our data at the + * correct offset and write it to the output + * buffer at 0 */ + r = copyto(endpt, ciov->iov_addr, i * blk_size, + (vir_bytes) copybuff, blk_size); + if (r != OK) { + mmc_log_warn(&log, + "I/O read error: %s iov(base,size)=(%d,%d)" + " at offset=%d\n", + strerror(_SIGN r), ciov->iov_addr, + ciov->iov_size, io_offset); + return EINVAL; + } + bytes_written += blk_size; + } + } + ciov++; + } + return bytes_written; +} + +/*===========================================================================* + * block_ioctl * + *===========================================================================*/ +static int +block_ioctl(dev_t minor, + unsigned int request, endpoint_t endpt, cp_grant_id_t grant) +{ + /* IOCTL handling */ + struct sd_slot *slot; + mmc_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, + "Doing ioctl on non existing block device(%d)\n", minor); + return EINVAL; + } + + switch (request) { + case DIOCOPENCT: + // TODO: add a check for card validity */ + mmc_log_trace(&log, "returning open count %d\n", + slot->card.open_ct); + /* return the current open count */ + return sys_safecopyto(endpt, grant, 0, + (vir_bytes) & slot->card.open_ct, + sizeof(slot->card.open_ct)); + case DIOCFLUSH: + /* No need to flush but some devices like movinands require + * 500 ms inactivity */ + return OK; + } + + return EINVAL; +} + +/*===========================================================================* + * block_part * + *===========================================================================*/ +static struct device * +block_part(dev_t minor) +{ + /* + * Reuse the existing MINIX major/minor partitioning scheme. + * - 8 drives + * - 5 devices per drive allowing direct access to the disk and up to 4 + * partitions (IBM style partitioning without extended partitions) + * - 4 Minix style sub partitions per partitions + */ + struct device *dev; + struct sd_slot *slot; + + dev = NULL; + slot = get_slot(minor); + if (!slot) { + mmc_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, + "Device information requested from empty slot(%d)\n", + minor); + return NULL; + } + + if (minor < 5) { + /* we are talking about the first disk */ + dev = &slot->card.part[minor]; + mmc_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, + "returning sub partition(%d) (base,size)=(0x%016llx,0x%016llx)\n", + minor - 128, dev->dv_base, dev->dv_size); + + } else { + mmc_log_warn(&log, + "Device information requested for non existing " + "partition minor(%d)\n", minor); + } + return dev; +} + +/*===========================================================================* + * sef_local_startup * + *===========================================================================*/ +static void +sef_local_startup() +{ + mmc_log_info(&log, "Initializing the MMC block device\n"); + if (apply_env()) { + mmc_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"); + exit(EXIT_FAILURE); + } + /* + * Register callbacks for fresh start, live update and restart. + * Use the same function for all event types + */ + sef_setcb_init_fresh(block_system_event_cb); + sef_setcb_init_lu(block_system_event_cb); + sef_setcb_init_restart(block_system_event_cb); + + /* Register a signal handler */ + sef_setcb_signal_handler(block_signal_handler_cb); + + /* SEF startup */ + sef_startup(); +} + +/*===========================================================================* + * block_system_event_cb * + *===========================================================================*/ +static int +block_system_event_cb(int type, sef_init_info_t * info) +{ + /* + * Callbacks for the System event framework as registered in + * sef_local_startup */ + switch (type) { + case SEF_INIT_FRESH: + mmc_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"); + break; + + case SEF_INIT_RESTART: + mmc_log_info(&log, "System event framework post restart\n"); + break; + } + return OK; +} + +/*===========================================================================* + * block_signal_handler_cb * + *===========================================================================*/ +static void +block_signal_handler_cb(int signo) +{ + mmc_log_debug(&log, "System event framework signal handler sig(%d)\n", + signo); + /* Only check for termination signal, ignore anything else. */ + if (signo != SIGTERM) + return; + // FIXME shutdown + exit(0); +} + +#define IS_MINIX_SUB_PARTITION_MINOR(minor) (minor >= MINOR_d0p0s0 ) + +static struct sd_slot * +get_slot(dev_t minor) +{ + /* + * Get an sd_slot based on the minor number. + * + * This driver only supports a single card at at time. Also as + * we are following the major/minor scheme of other driver we + * must return a slot for all minors on disk 0 these are 0-5 + * for the disk and 4 main partitions and + * number 128 till 144 for sub partitions. + */ + /* If this is a minor for the first disk (e.g. minor 0 till 5) */ + if (minor / DEV_PER_DRIVE == 0) { + /* we are talking about the first disk and that is all we + * support */ + return &host.slot[0]; + } else if ( IS_MINIX_SUB_PARTITION_MINOR(minor) + && (((minor - MINOR_d0p0s0) / SUB_PER_DRIVE) == 0)) { + /* a minor from the first disk */ + return &host.slot[0]; + } else { + mmc_log_trace(&log, + "Device information requested for non existing partition " + "minor(%d)\n", minor); + return NULL; + } +} + +#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); + log.log_level = level; + if (host.set_log_level) { + host.set_log_level(level); + } +} +#endif + +int +main(int argc, char **argv) +{ + /* early init of host mmc host controller. This code should depend on + * knowing the hardware that is running bellow. */ + host_initialize_host_structure(&host); + + /* Set and apply the environment */ + env_setargs(argc, argv); + + sef_local_startup(); + blockdriver_task(&mmc_driver); + return EXIT_SUCCESS; +} diff --git a/drivers/mmc/mmchost.h b/drivers/mmc/mmchost.h new file mode 100644 index 000000000..b4827fa53 --- /dev/null +++ b/drivers/mmc/mmchost.h @@ -0,0 +1,149 @@ + +#define SUBPARTITION_PER_PARTITION 4 /* 4 sub partitions per partition */ +#define PARTITONS_PER_DISK 4 /* 4 partitions per disk */ +#define MINOR_PER_DISK 1 /* one additional minor to point to */ + +/** + * We can have multiple MMC host controller present on the hardware. The MINIX + * approach to handle this is to run a driver for each instance. Every driver + * will therefore be stated with an "instance" id and the rest of the code here + * will assume a single host controller to be present. + * + * The SD specification allows multiple cards to be attached to a single host + * controller using the same lines. I recommend reading SD Specifications Part 1 + * Physical layer Simplified Specification chapter 3 about SD Memory Card system + * concepts if you want to get a better understanding of this. + * + * In practice an MMC host will usually have a single slot attached to it and that + * Slot may or may not contain a card. On sudden card removal we might want to + * keep track of the last inserted card and we might therefore at later stage + * add an additional "last_card" attribute to the card. + * + * The following diagram shows the structure that will be used to modulate the + * hardware written in umlwiki syntax. + * + * [/host/ + * +instance:int] 1 --- 0..4 [ /slot/ + * +card_detect:func ] 1 --- 0..1 [ /card/ ] + * ` + */ + +#define MAX_SD_SLOTS 4 + +struct mmc_host; + +//TODO Add more modes like INACTIVE STATE and such +#define SD_MODE_UNINITIALIZED 0 +#define SD_MODE_CARD_IDENTIFICATION 1 +#define SD_MODE_DATA_TRANSFER_MODE 2 + + +struct sd_card_regs +{ + uint32_t cid[4]; /* Card Identification */ + uint32_t rca; /* Relative card address */ + uint32_t dsr; /* Driver stage register */ + uint32_t csd[4]; /* Card specific data */ + uint32_t scr[2]; /* SD configuration */ + uint32_t ocr; /* Operation conditions */ + uint32_t ssr[5]; /* SD Status */ + uint32_t csr; /* Card status */ +}; + +/* struct representing an mmc command */ +struct mmc_command +{ + uint32_t cmd; + uint32_t args; + uint32_t resp_type; + +#define RESP_LEN_48_CHK_BUSY (3<<0) +#define RESP_LEN_48 (2<<0) +#define RESP_LEN_136 (1<<0) +#define NO_RESPONSE (0<<0) + + uint32_t resp[4]; + unsigned char* data; + uint32_t data_len; +}; + +/* structure representing an SD card */ +struct sd_card +{ + /* pointer back to the SD slot for convenience */ + struct sd_slot *slot; + + struct sd_card_regs regs; + + /* some helpers (data comming from the csd) */ + uint32_t blk_size; + uint32_t blk_count; + + /* drive state: deaf, initialized, dead */ + unsigned state; + + /* MINIX/block driver related things */ + int open_ct; /* in-use count */ + + /* 1 disks + 4 partitions and 16 possible sub partitions */ + struct device part[MINOR_PER_DISK + PARTITONS_PER_DISK]; + struct device subpart[PARTITONS_PER_DISK * SUBPARTITION_PER_PARTITION]; +}; + +/* structure representing an SD slot */ +struct sd_slot +{ + /* pointer back to the host for convenience */ + struct mmc_host *host; + + unsigned state; + struct sd_card card; +}; + +/* structure for the host controller */ +struct mmc_host +{ + /* MMC host configuration */ + int (*host_set_instance) (struct mmc_host * host, int instance); + /* MMC host configuration */ + int (*host_init) (struct mmc_host * host); + /* Set log level */ + void(*set_log_level) (int level); + /* Host controller reset */ + int (*host_reset) (struct mmc_host * host); + /* Card detection (binary yes/no) */ + int (*card_detect) (struct sd_slot * slot); + /* Perform card detection e.g. card type */ + struct sd_card *(*card_initialize) (struct sd_slot * slot); + /* Release the card */ + int (*card_release) (struct sd_card * card); + + /* read count blocks into existing buf */ + int (*read) (struct sd_card * card, + uint32_t blknr, uint32_t count, unsigned char *buf); + + /* write count blocks */ + int (*write) (struct sd_card * card, + uint32_t blknr, uint32_t count, unsigned char *buf); + + /* up to 4 slots with 4 SD cards */ + struct sd_slot slot[MAX_SD_SLOTS]; +}; + +#if 0 +/* Command execution */ +int (*send_cmd) (struct sd_card * card, struct mmc_command *); + +/* struct representing an mmc command */ +struct mmc_command +{ + uint32_t cmd; + uint32_t args; + uint32_t resp[4]; + unsigned char *data; + uint32_t data_len; +}; +#endif + +/* Hack done for driver registration */ +void host_initialize_host_structure(struct mmc_host *host); diff --git a/drivers/mmc/mmchost_mmchs.c b/drivers/mmc/mmchost_mmchs.c new file mode 100644 index 000000000..2ee0425af --- /dev/null +++ b/drivers/mmc/mmchost_mmchs.c @@ -0,0 +1,788 @@ +/* kernel headers */ +#include +#include +#include +#include + +/* usr headers */ +#include +#include +#include +#include +#include +#include +#include + +/* local headers */ +#include "mmclog.h" +#include "mmchost.h" + +/* header imported from netbsd */ +#include "sdmmcreg.h" +#include "sdmmcreg.h" +#include "sdhcreg.h" + +/* omap /hardware related */ +#include "omap_mmc.h" + +#define REG(x)(*((volatile uint32_t *)(x))) +#define BIT(x)(0x1 << x) + +/* Write a uint32_t value to a memory address. */ +inline void +write32(uint32_t address, uint32_t value) +{ + REG(address) = value; +} + +/* Read an uint32_t from a memory address */ +inline uint32_t +read32(uint32_t address) +{ + + return REG(address); +} + +/* Set a 32 bits value depending on a mask */ +inline void +set32(uint32_t address, uint32_t mask, uint32_t value) +{ + uint32_t val; + val = read32(address); + /* clear the bits */ + val &= ~(mask); + /* apply the value using the mask */ + val |= (value & mask); + write32(address, val); +} + +static uint32_t base_address; + +/* + * Initialize the MMC controller given a certain + * instance. this driver only handles a single + * mmchs controller at a given time. + */ +int +mmchs_init(uint32_t instance) +{ + int counter; + uint32_t value; + + counter = 0; + value = 0; + + struct minix_mem_range mr; + + mr.mr_base = MMCHS1_REG_BASE; + mr.mr_limit = MMCHS1_REG_BASE + 0x400; + + if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != 0) { + panic("Unable to request permission to map memory"); + } + + /* Set the base address to use */ + base_address = + (uint32_t) vm_map_phys(SELF, (void *) MMCHS1_REG_BASE, 0x400); + if (base_address == (uint32_t) MAP_FAILED) + panic("Unable to map MMC memory"); + + base_address = (unsigned long) base_address - 0x100; + + /* Soft reset of the controller. This section is documented in the TRM */ + + /* Write 1 to sysconfig[0] to trigger a reset */ + set32(base_address + MMCHS_SD_SYSCONFIG, MMCHS_SD_SYSCONFIG_SOFTRESET, + MMCHS_SD_SYSCONFIG_SOFTRESET); + + /* Read sysstatus to know when it's done */ + while (!(read32(base_address + MMCHS_SD_SYSSTATUS) + & MMCHS_SD_SYSSTATUS_RESETDONE)) { + /* TODO:Add proper delay and escape route */ + + counter++; + } + + /* Set SD default capabilities */ + set32(base_address + MMCHS_SD_CAPA, MMCHS_SD_CAPA_VS_MASK, + MMCHS_SD_CAPA_VS18 | MMCHS_SD_CAPA_VS30); + + /* TRM mentions MMCHS_SD_CUR_CAPA but does not describe how to limit + * the current */ + + uint32_t mask = + MMCHS_SD_SYSCONFIG_AUTOIDLE | MMCHS_SD_SYSCONFIG_ENAWAKEUP | + MMCHS_SD_SYSCONFIG_STANDBYMODE | MMCHS_SD_SYSCONFIG_CLOCKACTIVITY | + MMCHS_SD_SYSCONFIG_SIDLEMODE; + + /* Automatic clock gating strategy */ + value = MMCHS_SD_SYSCONFIG_AUTOIDLE_EN; + /* Enable wake-up capability */ + value |= MMCHS_SD_SYSCONFIG_ENAWAKEUP_EN; + /* Smart-idle */ + value |= MMCHS_SD_SYSCONFIG_SIDLEMODE_IDLE; + /* Booth the interface and functional can be switched off */ + value |= MMCHS_SD_SYSCONFIG_CLOCKACTIVITY_OFF; + /* Go into wake-up mode when possible */ + value |= MMCHS_SD_SYSCONFIG_STANDBYMODE_WAKEUP_INTERNAL; + + /* + * wake-up configuration + */ + set32(base_address + MMCHS_SD_SYSCONFIG, mask, value); + + /* Wake-up on sd interrupt for SDIO */ + set32(base_address + MMCHS_SD_HCTL, MMCHS_SD_HCTL_IWE, + MMCHS_SD_HCTL_IWE_EN); + + /* + * MMC host and bus configuration + */ + + /* Configure data and command transfer (1 bit mode) */ + set32(base_address + MMCHS_SD_CON, MMCHS_SD_CON_DW8, + MMCHS_SD_CON_DW8_1BIT); + set32(base_address + MMCHS_SD_HCTL, MMCHS_SD_HCTL_DTW, + MMCHS_SD_HCTL_DTW_1BIT); + + /* Configure card voltage to 3.0 volt */ + set32(base_address + MMCHS_SD_HCTL, MMCHS_SD_HCTL_SDVS, + MMCHS_SD_HCTL_SDVS_VS30); + + /* Power on the host controller and wait for the + * MMCHS_SD_HCTL_SDBP_POWER_ON to be set */ + 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*/ + + while ((read32(base_address + MMCHS_SD_HCTL) & MMCHS_SD_HCTL_SDBP) + != MMCHS_SD_HCTL_SDBP_ON) { + /* TODO:Add proper delay and escape route */ + counter++; + } + + /* Enable internal clock and clock to the card */ + set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_ICE, + MMCHS_SD_SYSCTL_ICE_EN); + + // @TODO Fix external clock enable , this one is very slow + set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_CLKD, + (0x3ff << 6)); + set32(base_address + MMCHS_SD_SYSCTL, MMCHS_SD_SYSCTL_CEN, + MMCHS_SD_SYSCTL_CEN_EN); + counter = 0; + + while ((read32(base_address + MMCHS_SD_SYSCTL) & MMCHS_SD_SYSCTL_ICS) + != MMCHS_SD_SYSCTL_ICS_STABLE) { + /* TODO:Add proper delay and escape route */ + counter++; + } + + /* + * See spruh73e page 3576 Card Detection, Identification, and Selection + */ + + /* Enable command interrupt */ + set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_CC_ENABLE, + MMCHS_SD_IE_CC_ENABLE_ENABLE); + /* Enable transfer complete interrupt */ + set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_TC_ENABLE, + 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); + + /* clean the error interrupts */ + set32(base_address + MMCHS_SD_STAT, MMCHS_SD_STAT_ERROR_MASK, + 0xffffffffu); + + /* send a init signal to the host controller. This does not actually + * send a command to a card manner */ + set32(base_address + MMCHS_SD_CON, MMCHS_SD_CON_INIT, + MMCHS_SD_CON_INIT_INIT); + /* command 0 , type other commands not response etc) */ + write32(base_address + MMCHS_SD_CMD, 0x00); + + counter = 0; + while ((read32(base_address + MMCHS_SD_STAT) & MMCHS_SD_STAT_CC) + != MMCHS_SD_STAT_CC_RAISED) { + if (read32(base_address + MMCHS_SD_STAT) & 0x8000) { + printf("%s, error stat %x\n", __FUNCTION__, + read32(base_address + MMCHS_SD_STAT)); + return 1; + } + counter++; + } + + /* clear the cc interrupt status */ + set32(base_address + MMCHS_SD_STAT, MMCHS_SD_IE_CC_ENABLE, + MMCHS_SD_IE_CC_ENABLE_ENABLE); + + /* + * Set Set SD_CON[1] INIT bit to 0x0 to end the initialization sequence + */ + set32(base_address + MMCHS_SD_CON, MMCHS_SD_CON_INIT, + MMCHS_SD_CON_INIT_NOINIT); + + /* Clean the MMCHS_SD_STAT register */ + write32(base_address + MMCHS_SD_STAT, 0xffffffffu); + return 0; +} + +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)) { + printf("%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) { + printf("%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; + } + + if ((command & MMCHS_SD_CMD_RSP_TYPE) == + MMCHS_SD_CMD_RSP_TYPE_48B_BUSY) { + /* + * Command with busy response *CAN* also set the TC bit if they exit busy + */ + while ((read32(base_address + MMCHS_SD_STAT) + & MMCHS_SD_IE_TC_ENABLE_ENABLE) == 0) { + count++; + } + 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); + return 0; +} + +int +mmc_send_cmd(struct mmc_command *c) +{ + + /* convert the command to a hsmmc command */ + int ret; + uint32_t cmd, arg; + cmd = MMCHS_SD_CMD_INDX_CMD(c->cmd); + arg = c->args; + + switch (c->resp_type) { + case RESP_LEN_48_CHK_BUSY: + cmd |= MMCHS_SD_CMD_RSP_TYPE_48B_BUSY; + break; + case RESP_LEN_48: + cmd |= MMCHS_SD_CMD_RSP_TYPE_48B; + break; + case RESP_LEN_136: + cmd |= MMCHS_SD_CMD_RSP_TYPE_136B; + break; + case NO_RESPONSE: + cmd |= MMCHS_SD_CMD_RSP_TYPE_NO_RESP; + break; + default: + return 1; + } + + ret = mmchs_send_cmd(cmd, arg); + + /* copy response into cmd->resp */ + switch (c->resp_type) { + case RESP_LEN_48_CHK_BUSY: + case RESP_LEN_48: + c->resp[0] = read32(base_address + MMCHS_SD_RSP10); + break; + case RESP_LEN_136: + c->resp[0] = read32(base_address + MMCHS_SD_RSP10); + c->resp[1] = read32(base_address + MMCHS_SD_RSP32); + c->resp[2] = read32(base_address + MMCHS_SD_RSP54); + c->resp[3] = read32(base_address + MMCHS_SD_RSP76); + break; + case NO_RESPONSE: + break; + default: + return 1; + } + + return ret; +} + +static struct mmc_command command; + +int +card_goto_idle_state() +{ + command.cmd = MMC_GO_IDLE_STATE; + command.resp_type = NO_RESPONSE; + command.args = 0x00; + if (mmc_send_cmd(&command)) { + // Failure + return 1; + } + return 0; +} + +int +card_identification() +{ + command.cmd = MMC_SEND_EXT_CSD; + command.resp_type = RESP_LEN_48; + command.args = MMCHS_SD_ARG_CMD8_VHS | MMCHS_SD_ARG_CMD8_CHECK_PATTERN; + + if (mmc_send_cmd(&command)) { + // We currently only support 2.0, + return 1; + } + + if (!(command.resp[0] + == (MMCHS_SD_ARG_CMD8_VHS | MMCHS_SD_ARG_CMD8_CHECK_PATTERN))) { + printf("%s, check pattern check failed %08x\n", __FUNCTION__, + command.resp[0]); + return 1; + } + return 0; +} + +int +card_query_voltage_and_type(struct sd_card_regs *card) +{ + + 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; + + /* 0x1 << 30 == send HCS (Host capacity support) and get OCR register */ + command.args = + MMC_OCR_3_3V_3_4V | MMC_OCR_3_2V_3_3V | MMC_OCR_3_1V_3_2V | + MMC_OCR_3_0V_3_1V | MMC_OCR_2_9V_3_0V | MMC_OCR_2_8V_2_9V | + MMC_OCR_2_7V_2_8V; + command.args |= MMC_OCR_HCS; /* RCA=0000 */ + + if (mmc_send_cmd(&command)) { + return 1; + } + /* @todo wait for max 1 ms */ + 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 + * register */ + command.cmd = SD_APP_OP_COND; + command.resp_type = RESP_LEN_48; + /* 0x1 << 30 == send HCS (Host capacity support) */ + command.args = MMC_OCR_3_3V_3_4V | MMC_OCR_3_2V_3_3V + | MMC_OCR_3_1V_3_2V | MMC_OCR_3_0V_3_1V | MMC_OCR_2_9V_3_0V + | MMC_OCR_2_8V_2_9V | MMC_OCR_2_7V_2_8V; + command.args |= MMC_OCR_HCS; /* RCA=0000 */ + + if (mmc_send_cmd(&command)) { + return 1; + } + + /* if bit 31 is set the response is valid */ + if ((command.resp[0] & MMC_OCR_MEM_READY)) { + break; + } + + } + card->ocr = command.resp[3]; + return 0; +} + +int +card_identify(struct sd_card_regs *card) +{ + + /* Send cmd 2 (all_send_cid) and expect 136 bits response */ + command.cmd = MMC_ALL_SEND_CID; + command.resp_type = RESP_LEN_136; + command.args = MMC_ARG_RCA(0x0); /* RCA=0000 */ + + if (mmc_send_cmd(&command)) { + return 1; + } + + card->cid[0] = command.resp[0]; + card->cid[1] = command.resp[1]; + card->cid[2] = command.resp[2]; + card->cid[3] = command.resp[3]; + + command.cmd = MMC_SET_RELATIVE_ADDR; + command.resp_type = RESP_LEN_48; + command.args = 0x0; /* RCA=0000 */ + + /* R6 response */ + if (mmc_send_cmd(&command)) { + return 1; + } + + card->rca = SD_R6_RCA(command.resp); + /* MMHCS only supports a single card so sending MMCHS_SD_CMD_CMD2 is + * useless Still we should make it possible in the API to support + * multiple cards */ + + return 0; +} + +int +card_csd(struct sd_card_regs *card) +{ + /* send_csd -> r2 response */ + command.cmd = MMC_SEND_CSD; + command.resp_type = RESP_LEN_136; + command.args = MMC_ARG_RCA(card->rca); /* card rca */ + + if (mmc_send_cmd(&command)) { + return 1; + } + + card->csd[0] = command.resp[0]; + card->csd[1] = command.resp[1]; + card->csd[2] = command.resp[2]; + card->csd[3] = command.resp[3]; + + if (SD_CSD_CSDVER(card->csd) != SD_CSD_CSDVER_2_0) { + printf("Version 2.0 of CSD register expected\n"); + return 1; + } + + /* sanity check */ + // printf("size = %llu bytes\n", (long long + // unsigned)SD_CSD_V2_CAPACITY( card->csd) * 512); + return 0; +} + +int +select_card(struct sd_card_regs *card) +{ + + command.cmd = MMC_SELECT_CARD; + command.resp_type = RESP_LEN_48_CHK_BUSY; + command.args = MMC_ARG_RCA(card->rca); /* card rca */ + + if (mmc_send_cmd(&command)) { + return 1; + } + return 0; +} + +int +read_single_block(struct sd_card_regs *card, + uint32_t blknr, unsigned char *buf) +{ + uint32_t count; + uint32_t value; + + count = 0; + + set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_BRR_ENABLE, + MMCHS_SD_IE_BRR_ENABLE_ENABLE); + + set32(base_address + MMCHS_SD_BLK, MMCHS_SD_BLK_BLEN, 512); + + /* read single block */ + if (mmchs_send_cmd(MMCHS_SD_CMD_INDX_CMD(MMC_READ_BLOCK_SINGLE) + | MMCHS_SD_CMD_DP_DATA /* Command with data transfer */ + | MMCHS_SD_CMD_RSP_TYPE_48B /* type (R1) */ + | MMCHS_SD_CMD_MSBS_SINGLE /* single block */ + | MMCHS_SD_CMD_DDIR_READ /* read data from card */ + , blknr)) { + return 1; + } + + while ((read32(base_address + MMCHS_SD_STAT) + & MMCHS_SD_IE_BRR_ENABLE_ENABLE) == 0) { + count++; + } + + if (!(read32(base_address + MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BRE_EN)) { + return 1; /* We are not allowed to read data from the + * data buffer */ + } + + for (count = 0; count < 512; count += 4) { + value = read32(base_address + MMCHS_SD_DATA); + buf[count] = *((char *) &value); + buf[count + 1] = *((char *) &value + 1); + buf[count + 2] = *((char *) &value + 2); + buf[count + 3] = *((char *) &value + 3); + } + + /* Wait for TC */ + while ((read32(base_address + + MMCHS_SD_STAT) & MMCHS_SD_IE_TC_ENABLE_ENABLE) + == 0) { + count++; + } + 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); + return 0; +} + +int +write_single_block(struct sd_card_regs *card, + uint32_t blknr, unsigned char *buf) +{ + uint32_t count; + uint32_t value; + + count = 0; + + set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_BWR_ENABLE, + MMCHS_SD_IE_BWR_ENABLE_ENABLE); + // 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_RSP_TYPE_48B /* type (R1b) */ + | MMCHS_SD_CMD_MSBS_SINGLE /* single block */ + | MMCHS_SD_CMD_DDIR_WRITE /* write to the card */ + , blknr)) { + 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 (!(read32(base_address + MMCHS_SD_PSTATE) & MMCHS_SD_PSTATE_BWE_EN)) { + return 1; /* not ready to write data */ + } + for (count = 0; count < 512; count += 4) { + *((char *) &value) = buf[count]; + *((char *) &value + 1) = buf[count + 1]; + *((char *) &value + 2) = buf[count + 2]; + *((char *) &value + 3) = buf[count + 3]; + 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++; + } + 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); + set32(base_address + MMCHS_SD_IE, MMCHS_SD_IE_BWR_ENABLE, + MMCHS_SD_IE_BWR_ENABLE_DISABLE); + return 0; +} + +/* + * Define a structure to be used for logging + */ +static struct mmclog log = { + .name = "mmc_host_mmchs", + .log_level = LEVEL_INFO, + .log_func = default_log +}; + +int +mmchs_host_init(struct mmc_host *host) +{ + mmchs_init(1); + return 0; +} + +void +mmchs_set_log_level(int level) +{ + if (level >= 0 && level <= 4) { + log.log_level = level; + } +} + +int +mmchs_host_set_instance(struct mmc_host *host, int instance) +{ + mmc_log_info(&log, "Using instance number %d\n", instance); + if (instance != 0) { + return EIO; + } + return OK; +} + +int +mmchs_host_reset(struct mmc_host *host) +{ + mmchs_init(1); + return 0; +} + +int +mmchs_card_detect(struct sd_slot *slot) +{ + /* @TODO implement proper card detect */ + return 1; +} + +struct sd_card * +mmchs_card_initialize(struct sd_slot *slot) +{ + mmchs_init(1); + + struct sd_card *card; + card = &slot->card; + memset(card, 0, sizeof(struct sd_card)); + card->slot = slot; + + if (card_identification()) { + printf("Failed to do card_identification\n"); + return NULL; + } + + if (card_query_voltage_and_type(&slot->card.regs)) { + printf("Failed to do card_query_voltage_and_type\n"); + return NULL; + } + if (card_identify(&slot->card.regs)) { + printf("Failed to identify card\n"); + return NULL; + } + /* We have now initialized the hardware identified the card */ + if (card_csd(&slot->card.regs)) { + printf("failed to read csd (card specific data)\n"); + return NULL; + } + + if (select_card(&slot->card.regs)) { + printf("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 */ + printf("Block size expect to be 512\n"); + return NULL; + } + + slot->card.blk_size = 512; /* HARDCODED value */ + slot->card.blk_count = SD_CSD_V2_CAPACITY(slot->card.regs.csd); + slot->card.state = SD_MODE_DATA_TRANSFER_MODE; + + memset(slot->card.part, 0, sizeof(slot->card.part)); + memset(slot->card.subpart, 0, sizeof(slot->card.subpart)); + slot->card.part[0].dv_base = 0; + slot->card.part[0].dv_size = + (unsigned long long) SD_CSD_V2_CAPACITY(slot->card.regs.csd) * 512; + return &slot->card; +} + +/* read count blocks into existing buf */ +static int +mmchs_host_read(struct sd_card *card, + uint32_t blknr, uint32_t count, unsigned char *buf) +{ + uint32_t i; + i = count; + for (i = 0; i < count; i++) { + read_single_block(&card->regs, blknr + i, + buf + (i * card->blk_size)); + } + return OK; +} + +/* write count blocks */ +static int +mmchs_host_write(struct sd_card *card, + uint32_t blknr, uint32_t count, unsigned char *buf) +{ + uint32_t i; + + i = count; + for (i = 0; i < count; i++) { + write_single_block(&card->regs, blknr + i, + buf + (i * card->blk_size)); + } + + return OK; +} + +int +mmchs_card_release(struct sd_card *card) +{ + assert(card->open_ct == 1); + card->open_ct--; + card->state = SD_MODE_UNINITIALIZED; + /* TODO:Set card state */ + return OK; +} + +void +host_initialize_host_structure(struct mmc_host *host) +{ + /* Initialize the basic data structures host slots and cards */ + int i; + + host->host_set_instance = mmchs_host_set_instance; + host->host_init = mmchs_host_init; + host->set_log_level = mmchs_set_log_level; + host->host_reset = mmchs_host_reset; + host->card_detect = mmchs_card_detect; + host->card_initialize = mmchs_card_initialize; + host->card_release = mmchs_card_release; + host->read = mmchs_host_read; + host->write = mmchs_host_write; + + /* initialize data structures */ + for (i = 0; i < sizeof(host->slot) / sizeof(host->slot[0]); i++) { + // @TODO set initial card and slot state + host->slot[i].host = host; + host->slot[i].card.slot = &host->slot[i]; + } +} diff --git a/drivers/mmc/mmclog.h b/drivers/mmc/mmclog.h new file mode 100644 index 000000000..872823a3e --- /dev/null +++ b/drivers/mmc/mmclog.h @@ -0,0 +1,112 @@ +#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 diff --git a/drivers/mmc/omap_mmc.h b/drivers/mmc/omap_mmc.h new file mode 100644 index 000000000..46011ebda --- /dev/null +++ b/drivers/mmc/omap_mmc.h @@ -0,0 +1,181 @@ +///* TODO: Rename to MMCH_0_REG_BASE and add the base address for the other items */ +//#define MMCHS1_REG_BASE 0x48060000 + +//#ifdef AM_DM37x_Multimedia_Device +#define MMCHS1_REG_BASE 0x4809C000 +//#define MMCHS2_REG_BASE 0x480B4000 +//#define MMCHS3_REG_BASE 0x480AD000 +//#endif + +#define MMCHS_SD_SYSCONFIG 0x110 /* SD system configuration */ +#define MMCHS_SD_SYSSTATUS 0x114 /* SD system status */ +#define MMCHS_SD_CON 0x12c /* Configuration (functional mode,card initialization etc) */ +#define MMCHS_SD_BLK 0x204 /* Transfer length configuration */ +#define MMCHS_SD_ARG 0x208 /* Command argument bit 38-8 of command format*/ +#define MMCHS_SD_CMD 0x20c /* Command and transfer mode */ +#define MMCHS_SD_RSP10 0x210 /* Command response 0 and 1 */ +#define MMCHS_SD_RSP32 0x214 /* Command response 2 and 3 */ +#define MMCHS_SD_RSP54 0x218 /* Command response 4 and 5 */ +#define MMCHS_SD_RSP76 0x21c /* Command response 6 and 7 */ +#define MMCHS_SD_DATA 0x220 /* Data register */ +#define MMCHS_SD_PSTATE 0x224 /* Present state */ +#define MMCHS_SD_HCTL 0x228 /* Host control(power ,wake-up and transfer) */ +#define MMCHS_SD_SYSCTL 0x22c /* SD System control (reset,clocks and timeout) */ +#define MMCHS_SD_STAT 0x230 /* SD Interrupt status */ +#define MMCHS_SD_IE 0x234 /* SD Interrupt Enable register */ +#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_SYSCONFIG_AUTOIDLE (0x1 << 0) /* Internal clock gating strategy */ +#define MMCHS_SD_SYSCONFIG_AUTOIDLE_DIS (0x0 << 0) /* Clocks are free running */ +#define MMCHS_SD_SYSCONFIG_AUTOIDLE_EN (0x1 << 0) /* Automatic clock gating strategy */ +#define MMCHS_SD_SYSCONFIG_SOFTRESET (0x1 << 1) /* Software reset bit writing */ +#define MMCHS_SD_SYSCONFIG_ENAWAKEUP (0x1 << 2) /* Wake-up feature control */ +#define MMCHS_SD_SYSCONFIG_ENAWAKEUP_DIS (0x0 << 2) /* Disable wake-up capability */ +#define MMCHS_SD_SYSCONFIG_ENAWAKEUP_EN (0x1 << 2) /* Enable wake-up capability */ +#define MMCHS_SD_SYSCONFIG_SIDLEMODE (0x3 << 3) /* Power management */ +#define MMCHS_SD_SYSCONFIG_SIDLEMODE_UNCONDITIONAL (0x0 << 3) /* Go into idle mode unconditionally upon request */ +#define MMCHS_SD_SYSCONFIG_SIDLEMODE_IGNORE (0x1 << 3) /* Ignore ILDE requests */ +#define MMCHS_SD_SYSCONFIG_SIDLEMODE_IDLE (0x2 << 3) /* Acknowledge IDLE request switch to wake-up mode */ +#define MMCHS_SD_SYSCONFIG_SIDLEMODE_SMART_IDLE (0x3 << 3) /* Smart-idle */ +#define MMCHS_SD_SYSCONFIG_CLOCKACTIVITY (0x3 << 8) /* Clock activity during wake-up */ +#define MMCHS_SD_SYSCONFIG_CLOCKACTIVITY_OFF (0x0 << 8) /* Interface and functional clock can be switched off */ +#define MMCHS_SD_SYSCONFIG_CLOCKACTIVITY_IF (0x1 << 8) /* Only Interface clock (functional can be switched off*/ +#define MMCHS_SD_SYSCONFIG_CLOCKACTIVITY_FUNC (0x2 << 8) /* Only Functional clock (interface clock can be switched off) */ +#define MMCHS_SD_SYSCONFIG_CLOCKACTIVITY_BOOTH (0x3 << 8) /* Booth the interface and functional clock are maintained */ +#define MMCHS_SD_SYSCONFIG_STANDBYMODE (0x3 << 12) /* Configuration for standby */ +#define MMCHS_SD_SYSCONFIG_STANDBYMODE_FORCE_STANDBY (0x0 << 12) /* Force standby mode upon idle request*/ +#define MMCHS_SD_SYSCONFIG_STANDBYMODE_NO_STANDBY (0x1 << 12) /* Never go into standby mode */ +#define MMCHS_SD_SYSCONFIG_STANDBYMODE_WAKEUP_INTERNAL (0x2 << 12) /* Go into wake-up mode based on internal knowledge */ +#define MMCHS_SD_SYSCONFIG_STANDBYMODE_WAKEUP_SMART (0x3 << 12) /* Go info wake-up mode when possible */ + +#define MMCHS_SD_SYSSTATUS_RESETDONE 0x01 + +#define MMCHS_SD_CON_DW8 (0x1 << 5) /* 8-bit mode MMC select , For SD clear this bit */ +#define MMCHS_SD_CON_DW8_1BIT (0x0 << 5) /* 1 or 4 bits data width configuration(also set SD_HCTL) */ +#define MMCHS_SD_CON_DW8_8BITS (0x1 << 5) /* 8 bits data width configuration */ +#define MMCHS_SD_CON_INIT (0x1 << 1) /* Send initialization stream (all cards) */ +#define MMCHS_SD_CON_INIT_NOINIT (0x0 << 1) /* Do nothing */ +#define MMCHS_SD_CON_INIT_INIT (0x1 << 1) /* Send initialization stream */ + +#define MMCHS_SD_BLK_NBLK (0xffffu << 16) /* Block count for the current transfer */ +#define MMCHS_SD_BLK_BLEN (0xfff << 0) /* Transfer block size */ +#define MMCHS_SD_BLK_BLEN_NOTRANSFER (0x0 << 0) /* No transfer */ + +#define MMCHS_SD_CMD_INDX (0x3f << 24) /* Command index */ +#define MMCHS_SD_CMD_INDX_CMD(x) (x << 24) /* MMC command index binary encoded values from 0 to 63 */ + +#define MMCHS_SD_ARG_MASK (0xffffffffu) /* Mask everything */ +#define MMCHS_SD_ARG_CMD8_VHS (0x1 << (16 - 8)) /* Voltage between 2.7 and 3.6 v*/ +#define MMCHS_SD_ARG_CMD8_CHECK_PATTERN (0xaa <<(8 - 8)) /* 10101010b pattern */ + +#define MMCHS_SD_CMD_TYPE (0x3 << 22) /* Command type. */ +#define MMCHS_SD_CMD_TYPE_OTHER (0x0 << 22) /* Other type of commands (like go idle) */ +#define MMCHS_SD_CMD_TYPE_BUS_SUSPEND (0x1 << 22) /* Upon CMD52 "Bus Suspend" operation */ +#define MMCHS_SD_CMD_TYPE_FUNCTION_SELECT (0x2 << 22) /* Upon CMD52 "Function Select" operation */ +#define MMCHS_SD_CMD_TYPE_IOABORT (0x3 << 22) /* Upon CMD12 and CMD21 "I/O Abort */ +#define MMCHS_SD_CMD_DP (0x1 << 21) /* Data present select */ +#define MMCHS_SD_CMD_DP_DATA (0x1 << 21) /* Additional data is present on the data lines */ +#define MMCHS_SD_CMD_DP_NODATA (0x0 << 21) /* No additional data is present on the data lines */ +#define MMCHS_SD_CMD_CICE (0x1 << 20) /* Command index response check enable */ +#define MMCHS_SD_CMD_CICE_ENABLE (0x1 << 20) /* Enable index check response */ +#define MMCHS_SD_CMD_CICE_DISABLE (0x0 << 20) /* Disable index check response */ +#define MMCHS_SD_CMD_CCCE (0x1 << 19) /* Command CRC7 Check enable on responses*/ +#define MMCHS_SD_CMD_CCCE_ENABLE (0x1 << 19) /* Enable CRC7 Check on response */ +#define MMCHS_SD_CMD_CCCE_DISABLE (0x0 << 19) /* Disable CRC7 Check on response */ +#define MMCHS_SD_CMD_RSP_TYPE (0x3 << 16) /* Response type */ +#define MMCHS_SD_CMD_RSP_TYPE_NO_RESP (0x0 << 16) /* No response */ +#define MMCHS_SD_CMD_RSP_TYPE_136B (0x1 << 16) /* Response length 136 bits */ +#define MMCHS_SD_CMD_RSP_TYPE_48B (0x2 << 16) /* Response length 48 bits */ +#define MMCHS_SD_CMD_RSP_TYPE_48B_BUSY (0x3 << 16) /* Response length 48 bits with busy after response */ +#define MMCHS_SD_CMD_MSBS (0x1 << 5) /* Multi/Single block select */ +#define MMCHS_SD_CMD_MSBS_SINGLE (0x0 << 5) /* Single block mode */ +#define MMCHS_SD_CMD_MSBS_MULTI (0x0 << 5) /* Multi block mode */ +#define MMCHS_SD_CMD_DDIR (0x1 << 4) /* Data transfer direction */ +#define MMCHS_SD_CMD_DDIR_READ (0x1 << 4) /* Data read (card to host) */ +#define MMCHS_SD_CMD_DDIR_WRITE (0x0 << 4) /* Data write (host to card) */ +#define MMCHS_SD_CMD_ACEN (0x1 << 2) /* Auto CMD12 Enable */ +#define MMCHS_SD_CMD_ACEN_DIS (0x0 << 2) /* Auto CMD12 Disable */ +#define MMCHS_SD_CMD_ACEN_EN (0x1 << 2) /* Auto CMD12 Enable */ +#define MMCHS_SD_CMD_BCE (0x1 << 1) /* Block Count Enable(for multi block transfer) */ +#define MMCHS_SD_CMD_BCE_DIS (0x0 << 1) /* Disabled block count for infinite transfer*/ +#define MMCHS_SD_CMD_BCE_EN (0x1 << 1) /* Enabled for multi block transfer with know amount of blocks */ +#define MMCHS_SD_CMD_DE (0x1 << 0) /* DMA enable */ +#define MMCHS_SD_CMD_DE_DIS (0x0 << 0) /* Disable DMA */ +#define MMCHS_SD_CMD_DE_EN (0x1 << 0) /* Enable DMA */ +#define MMCHS_SD_CMD_MASK ~(0x1 << 30 | 0x1 << 31 | 0x1 << 18 | 0x1 <<3) /* bits 30 , 31 and 18 are reserved */ + +#define MMCHS_SD_PSTATE_CI (0x1 << 16) /* Card Inserted */ +#define MMCHS_SD_PSTATE_CI_INSERTED (0x1 << 16) /* Card Inserted is inserted*/ +#define MMCHS_SD_PSTATE_BRE (0x0 << 11) /* Buffer read enable */ +#define MMCHS_SD_PSTATE_BRE_DIS (0x0 << 11) /* Read BLEN bytes disabled*/ +#define MMCHS_SD_PSTATE_BRE_EN (0x1 << 11) /* Read BLEN bytes enabled*/ +#define MMCHS_SD_PSTATE_BWE (0x0 << 10) /* Buffer Write enable */ +#define MMCHS_SD_PSTATE_BWE_DIS (0x0 << 10) /* There is no room left in the buffer to write BLEN bytes of data */ +#define MMCHS_SD_PSTATE_BWE_EN (0x1 << 10) /* There is enough space in the buffer to write BLEN bytes of data*/ + +#define MMCHS_SD_HCTL_DTW (0x1 << 1) /*Data transfer width.(must be set after a successful ACMD6) */ +#define MMCHS_SD_HCTL_DTW_1BIT (0x0 << 1) /*1 bit transfer with */ +#define MMCHS_SD_HCTL_DTW_4BIT (0x1 << 1) /*4 bit transfer with */ +#define MMCHS_SD_HCTL_SDBP (0x1 << 8) /*SD bus power */ +#define MMCHS_SD_HCTL_SDBP_OFF (0x0 << 8) /*SD Power off (start card detect?) */ +#define MMCHS_SD_HCTL_SDBP_ON (0x1 << 8) /*SD Power on (start card detect?) */ +#define MMCHS_SD_HCTL_SDVS (0x7 << 9) /*SD bus voltage select */ +#define MMCHS_SD_HCTL_SDVS_VS18 (0x5 << 9) /*1.8 V */ +#define MMCHS_SD_HCTL_SDVS_VS30 (0x6 << 9) /*3.0 V */ +#define MMCHS_SD_HCTL_SDVS_VS33 (0x7 << 9) /*3.3 V */ +#define MMCHS_SD_HCTL_IWE (0x1 << 24)/* wake-up event on SD interrupt */ +#define MMCHS_SD_HCTL_IWE_DIS (0x0 << 24)/* Disable wake-up on SD interrupt */ +#define MMCHS_SD_HCTL_IWE_EN (0x1 << 24)/* Enable wake-up on SD interrupt */ + +#define MMCHS_SD_SYSCTL_CLKD (0x3ff << 6) /* 10 bits clock frequency select */ +#define MMCHS_SD_SYSCTL_SRD (0x1 << 26) /* Soft reset for mmc_dat line */ +#define MMCHS_SD_SYSCTL_SRC (0x1 << 25) /* Soft reset for mmc_cmd line */ +#define MMCHS_SD_SYSCTL_SRA (0x1 << 24) /* Soft reset all (host controller) */ + +#define MMCHS_SD_SYSCTL_ICE (0x1 << 0) /* Internal clock enable register */ +#define MMCHS_SD_SYSCTL_ICE_DIS (0x0 << 0) /* Disable internal clock */ +#define MMCHS_SD_SYSCTL_ICE_EN (0x1 << 0) /* Enable internal clock */ +#define MMCHS_SD_SYSCTL_ICS (0x1 << 1) /* Internal clock stable register */ +#define MMCHS_SD_SYSCTL_ICS_UNSTABLE (0x0 << 1) /* Internal clock is unstable */ +#define MMCHS_SD_SYSCTL_ICS_STABLE (0x1 << 1) /* Internal clock is stable */ +#define MMCHS_SD_SYSCTL_CEN (0x1 << 2) /* Card lock enable provide clock to the card */ +#define MMCHS_SD_SYSCTL_CEN_DIS (0x0 << 2) /* Internal clock is unstable */ +#define MMCHS_SD_SYSCTL_CEN_EN (0x1 << 2) /* Internal clock is stable */ + +#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_2POW14 (0x1 << 16) /* TCF x 2^14 */ +#define MMCHS_SD_SYSCTL_DTO_2POW27 (0x3 << 16) /* TCF x 2^27 */ + +#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_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_IE_ERROR_MASK (0xff << 15 | 0x3 << 24 | 0x03 << 28) + +#define MMCHS_SD_IE_CC_ENABLE (0x1 << 0) /* Command complete interrupt enable */ +#define MMCHS_SD_IE_CC_ENABLE_ENABLE (0x1 << 0) /* Command complete Interrupts are enabled */ +#define MMCHS_SD_IE_CC_ENABLE_CLEAR (0x1 << 0) /* Clearing is done by writing a 0x1 */ + +#define MMCHS_SD_IE_TC_ENABLE (0x1 << 1) /* Transfer complete interrupt enable */ +#define MMCHS_SD_IE_TC_ENABLE_ENABLE (0x1 << 1) /* Transfer complete Interrupts are enabled */ +#define MMCHS_SD_IE_TC_ENABLE_CLEAR (0x1 << 1) /* Clearing TC is done by writing a 0x1 */ + +#define MMCHS_SD_IE_BRR_ENABLE (0x1 << 5) /* Buffer read ready interrupt */ +#define MMCHS_SD_IE_BRR_ENABLE_DISABLE (0x0 << 5) /* Buffer read ready interrupt disable */ +#define MMCHS_SD_IE_BRR_ENABLE_ENABLE (0x1 << 5) /* Buffer read ready interrupt enable */ +#define MMCHS_SD_IE_BRR_ENABLE_CLEAR (0x1 << 5) /* Buffer read ready interrupt clear */ + +#define MMCHS_SD_IE_BWR_ENABLE (0x1 << 4) /* Buffer write ready interrupt */ +#define MMCHS_SD_IE_BWR_ENABLE_DISABLE (0x0 << 4) /* Buffer write ready interrupt disable */ +#define MMCHS_SD_IE_BWR_ENABLE_ENABLE (0x1 << 4) /* Buffer write ready interrupt enable */ +#define MMCHS_SD_IE_BWR_ENABLE_CLEAR (0x1 << 4) /* Buffer write ready interrupt clear */ + +#define MMCHS_SD_CAPA_VS_MASK (0x7 << 24 ) /* voltage mask */ +#define MMCHS_SD_CAPA_VS18 (0x01 << 26 ) /* 1.8 volt */ +#define MMCHS_SD_CAPA_VS30 (0x01 << 25 ) /* 3.0 volt */ +#define MMCHS_SD_CAPA_VS33 (0x01 << 24 ) /* 3.3 volt */ + diff --git a/etc/system.conf b/etc/system.conf index cbe543db3..0bed9ddff 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -532,6 +532,13 @@ service devman uid 0; }; +service mmc +{ + system + PRIVCTL # 4 + ; +}; + service vbox { system