MMC driver.
Change-Id: I0d460d63070855df9b11eaf3d33eb7bb89c570f1
This commit is contained in:
parent
4fda063232
commit
49246fcdd5
8 changed files with 1965 additions and 0 deletions
17
drivers/mmc/Makefile
Normal file
17
drivers/mmc/Makefile
Normal file
|
@ -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 <minix.service.mk>
|
56
drivers/mmc/README.txt
Normal file
56
drivers/mmc/README.txt
Normal file
|
@ -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.
|
655
drivers/mmc/mmcblk.c
Normal file
655
drivers/mmc/mmcblk.c
Normal file
|
@ -0,0 +1,655 @@
|
|||
/*
|
||||
* Block driver for Multi Media Cards (MMC).
|
||||
*/
|
||||
/* kernel headers */
|
||||
#include <minix/syslib.h>
|
||||
#include <minix/driver.h>
|
||||
#include <minix/blockdriver.h>
|
||||
#include <minix/drvlib.h>
|
||||
|
||||
/* system headers */
|
||||
#include <sys/ioc_disk.h> /* disk IOCTL's */
|
||||
|
||||
/* usr headers */
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
|
||||
/* 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;
|
||||
}
|
149
drivers/mmc/mmchost.h
Normal file
149
drivers/mmc/mmchost.h
Normal file
|
@ -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);
|
788
drivers/mmc/mmchost_mmchs.c
Normal file
788
drivers/mmc/mmchost_mmchs.c
Normal file
|
@ -0,0 +1,788 @@
|
|||
/* kernel headers */
|
||||
#include <minix/blockdriver.h>
|
||||
#include <minix/com.h>
|
||||
#include <minix/vm.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
/* usr headers */
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* 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];
|
||||
}
|
||||
}
|
112
drivers/mmc/mmclog.h
Normal file
112
drivers/mmc/mmclog.h
Normal file
|
@ -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
|
181
drivers/mmc/omap_mmc.h
Normal file
181
drivers/mmc/omap_mmc.h
Normal file
|
@ -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 */
|
||||
|
|
@ -532,6 +532,13 @@ service devman
|
|||
uid 0;
|
||||
};
|
||||
|
||||
service mmc
|
||||
{
|
||||
system
|
||||
PRIVCTL # 4
|
||||
;
|
||||
};
|
||||
|
||||
service vbox
|
||||
{
|
||||
system
|
||||
|
|
Loading…
Reference in a new issue