MMC driver.

Change-Id: I0d460d63070855df9b11eaf3d33eb7bb89c570f1
This commit is contained in:
Kees Jongenburger 2012-10-03 10:55:40 +02:00
parent 4fda063232
commit 49246fcdd5
8 changed files with 1965 additions and 0 deletions

17
drivers/mmc/Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */

View file

@ -532,6 +532,13 @@ service devman
uid 0; uid 0;
}; };
service mmc
{
system
PRIVCTL # 4
;
};
service vbox service vbox
{ {
system system