I've a lot of effort to make my DBox2 handle big cards, and to record a radio channel with 256kbps.
I have my issues with the building, so every time i made some changes, i had to build clean, and reflash an image to the box. (i would like to have a full tutorial to build a yadd, and setup the dboxII bootmanager)
So after a few dozen of trial and error finally some working code have been made. I'm not really interested in further maintaining the code (like active cvs updates once in a while) but i want to share what have i already.
Some info and history about the process: i've started dev-ing for recording the SSL mixmission over a year ago, but abandoned due to lack of patience and big enough card. Now, I revisited again, and now as i have a 32GB card, it seemed more justified.
The code based on the mmc2.c , due to optimized read, but later further optimized for writing based on mmccombo.c thus rearranged the pins to use with.
- Now only NOKIA boxes can be used for this, sorry! And check for the modified pins, not the same anymore -
The module is capable of initializating (int theory) all MMC and SC cards in SPI mode, but only legacy (<2GB) MMC cards, and SD cards with 512bytes/block (all SDHC/SDXC(?), and legacy cards except 2GB ones).
At first (default mmc2 i/o) the write from ftp was ~16kBytes/s, after the mmccombo's write code and pin arrange nothing really changed, still ~17kB/s. The initial i/o was 512byte block read/write, but now uses SET_WR_BLK_ERASE_COUNT prior to WRITE_MULTIPLE_BLOCK which is really improved the writing speed.
This is a really major point, as before writing, the card issues an erase of an AU (accessible unit), and then copy AU_SIZE-512byte to somewhere, and after that can the write fill that 512 bytes with own datas. Now the cards gets how many blocks waiting to be written, then sending them by WRITE_MULTIPLE_BLOCK requireing less erase and less copy by the card's internal management.
I have a plan for further upgrade: using hardware parallel->serial and serial->parallel circuits, and a 1/3 clock divider (66/3=22MHz) and some transaction management --- buuut i may let others to continue in this path
So the source is here for everyone: ( not sure if the wmb() is needed )
Code: Alles auswählen
// $Id: mmc2.c,v 2.0 2011/12/27 13:42:05 satsuse Exp $
//
// MMC2.c
// General MMC device driver for dbox Modem Connector
//---// Modem connector pins PA9,PA8,PB16,PB17 used -> MMC2 connection sheme
// Modem connector pins PA9,PA8,PB16,PA7 used -> MMC2 connection sheme
//
// This version of MMC is used to test various optimisation variants only
//
// 14 Aug 2006
// ---- Modem_CN ---
// SAGEM NOKIA PHIL
// PA9 = SD_DO = 0x0040 = SD_Card Pin 7 = 2 12 11
// PA8 = SD_DI = 0x0080 = SD_Card Pin 2 = 1 11 9
// PA7 = SD_CLK= 0x0100 = SD_Card Pin 5 = ? 14 ?
///----------------------------// PB17 = SD_CLK= 0x4000 = SD_Card Pin 5 = 10 2 7
// PB16 = SD_CS = 0x8000 = SD_Card Pin 1 = 6 6 5
// GND = = Masse = SD_Card Pin 3,6 = 3 10 2
// VCC = = 3,3V = SD_Card Pin 4 = 5 16 x
// (x philips: connect in series 3 Diodes (1N4007) from MODEM_CN 1 to SD/MMC card pin 4)
// Also connect a pullup resistor 100 KOhm from SD/MMC card pin 7 (SD_DO) to SD/MMC card pin 4 (VCC)
//
// written by Madsuk/Rohde/TaGana
// optimized by just_me/Guenther/DboxBar
//
// (partial) big card (2gb+ MMC / SDHC / SDXC) support by CodeKiller
//
//
// TODO: more write speed by ACMD23 (pre erase multiblocks) and CMD25 (write multiblocks)
// mutiblock headers: block header - 0xfc end of datas - 0xfd
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/blkpg.h>
#include <linux/hdreg.h>
#include <linux/major.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/8xx_immap.h>
#define DEVICE_NAME "mmc"
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#define MAJOR_NR 121
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
extern void m8xx_wdt_reset(void);
#endif
#include <linux/blk.h>
MODULE_AUTHOR("Madsuk/Rohde/TaGana/CK");
MODULE_DESCRIPTION("Driver MMC/SD-Cards");
MODULE_SUPPORTED_DEVICE("all dbox2 on com2 connector");
MODULE_LICENSE("GPL");
//#define CHECK_MEDIA_CHANGE // for developement ONLY, not working yet
#define SD_DO 0x0040 // on SD/MMC card pin 7
#define SD_DI 0x0080 // on SD/MMC card pin 2
#define SD_CLK 0x0100 // on SD/MMC card pin 5
#define SD_CS 0x8000 // on SD/MMC card pin 1
volatile immap_t *immap=(immap_t *)IMAP_ADDR ;
/* we have only one device */
static int hd_sizes[1<<6]; //1<<6=SILLY!! EQUAL 64! but why??
static int hd_blocksizes[1<<6];
static int hd_hardsectsizes[1<<6];
static int hd_maxsect[1<<6];
static struct hd_struct hd[1<<6];
static struct timer_list mmc_timer;
static int mmc_media_detect = 0;
static int mmc_media_changed = 1;
volatile int mmc_type=1; //4=MMC 2GB+; 3=SDHC/SDXC; 2=SDSCv2.0; 1=SDSC; 0=MMC
extern struct gendisk hd_gendisk;
/////////////////////
// prototypes
static int mmc_open(struct inode *inode, struct file *filp);
static int mmc_release(struct inode *inode, struct file *filp);
static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
static void mmc_request(request_queue_t *q);
static struct block_device_operations mmc_bdops =
{
open: mmc_open,
release: mmc_release,
ioctl: mmc_ioctl,
#ifdef CHECK_MEDIA_CHANGE
check_media_change: mmc_check_media_change,
revalidate: mmc_revalidate,
#endif
};
static struct gendisk hd_gendisk = {
major: MAJOR_NR,
major_name: DEVICE_NAME,
minor_shift: 6,
max_p: 1 << 6,
part: hd,
sizes: hd_sizes,
fops: &mmc_bdops,
};
///////////////////////////////////////////////////////////////////////////////////////////////
/////// Low-Level Driver
///////////////////////////////////////////////////////////////////////////////////////////////
static int mmc_hardware_init(void) {
volatile cpm8xx_t *cp = (cpm8xx_t *) &immap->im_cpm;
volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
printk("mmc2: Hardware init\n");
cp->cp_pbpar &= ~(SD_CS);
cp->cp_pbodr &= ~(SD_CS);
cp->cp_pbdir |= (SD_CS);
cpi->iop_papar &= ~(SD_DO | SD_DI | SD_CLK);
cpi->iop_paodr &= ~(SD_DO | SD_CLK);
cpi->iop_padir |= (SD_DI | SD_CLK);
cpi->iop_padir &= ~SD_DO;
// Clock + CS low
cpi->iop_padat &= ~SD_CLK;
cp->cp_pbdat &= ~SD_CS;
cpi->iop_padat &= ~SD_DI;
return 0;
}
static void mmc_spi_cs_low(void) {
volatile cpm8xx_t *cp = (cpm8xx_t *) &immap->im_cpm;
cp->cp_pbdat &= ~(SD_CS);
}
static void mmc_spi_cs_high(void) {
volatile cpm8xx_t *cp = (cpm8xx_t *) &immap->im_cpm;
cp->cp_pbdat |= SD_CS;
}
static unsigned char mmc_spi_io(unsigned char data_out) {
//volatile cpm8xx_t *cp = (cpm8xx_t *) &immap->im_cpm;
volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
unsigned char result = 0;
unsigned char i;
for(i = 0x80; i != 0; i >>= 1) {
if (data_out & i)
cpi->iop_padat |= SD_DI;
else
cpi->iop_padat &= ~SD_DI;
// latch data on SD_DI into card
cpi->iop_padat |= SD_CLK;
// shouldn't data be read after clk h->l?
if (cpi->iop_padat & SD_DO) {
result |= i;
}
cpi->iop_padat &= ~SD_CLK;
}
return result;
}
//////////////////////////////////
// optimized read/write functions
inline void mmc_spi_write(unsigned char data_out) {
//volatile cpm8xx_t *cp = (cpm8xx_t *) &immap->im_cpm;
volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
//unsigned long clk_high = cp->cp_pbdat | SD_CLK;
//unsigned long clk_low = cp->cp_pbdat & ~SD_CLK;
//unsigned long data_high = cpi->iop_padat | SD_DI;
//unsigned long data_low = cpi->iop_padat & ~SD_DI;
unsigned int cldl=cpi->iop_padat & ~(SD_CLK | SD_DI);
unsigned int cldh=(cpi->iop_padat | SD_DI) & ~SD_CLK;
unsigned int chdl=(cpi->iop_padat | SD_CLK) & ~SD_DI;
unsigned int chdh=cpi->iop_padat | SD_DI | SD_CLK;
// bit 7
if (data_out & 0x80) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 6
if (data_out & 0x40) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 5
if (data_out & 0x20) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 4
if (data_out & 0x10) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 3
if (data_out & 0x08) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 2
if (data_out & 0x04) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 1
if (data_out & 0x02) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 0
if (data_out & 0x01) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
cpi->iop_padat = cldh;
}
inline unsigned char mmc_spi_read(void)
{
unsigned char result = 0;
//volatile cpm8xx_t *cp = (cpm8xx_t *) &immap->im_cpm;
volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
unsigned long clk_high = cpi->iop_padat | SD_CLK | SD_DI;
unsigned long clk_low = (cpi->iop_padat | SD_DI ) & ~SD_CLK;
cpi->iop_padat |= SD_DI;
cpi->iop_padat = clk_high; // Bit 0 (MSB)
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 1
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 2
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 3
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 4
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 5
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 6
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 7
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
return result;
}
////////////////////////////////////////
// burst transfer quite more optimized (approx 10% faster than above)
void mmc_read_block_low_level(unsigned char *dest)
{
//volatile cpm8xx_t *cp = (cpm8xx_t *) &immap->im_cpm;
volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
unsigned long clk_high = cpi->iop_padat | SD_CLK | SD_DI; // load clock port into register ram and prefefine with pin low/high,to avoid reload on any clock trigger, this speeds ups the transmission significantly
unsigned long clk_low = (cpi->iop_padat | SD_DI ) & ~SD_CLK;
cpi->iop_padat |= SD_DI; // for read access we send high bytes only
unsigned char result=0;
unsigned int i=0;
for (i = 0; i < 512; i++)
{
cpi->iop_padat = clk_high; // Bit 0
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 1
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 2
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 3
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 4
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 5
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 6
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
cpi->iop_padat = clk_high; // Bit 7
result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
cpi->iop_padat = clk_low;
*dest++ = result;
result = 0;
}
}
void mmc_write_block_low_level(unsigned char *dest)
{
//volatile cpm8xx_t *cp = (cpm8xx_t *) &immap->im_cpm;
volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
//unsigned long clk_high = cp->cp_padat | SD_CLK; // load clock port into register ram and prefefine with pin low/high,to avoid reload on any clock trigger, this speeds ups the transmission significantly
//unsigned long clk_low = cp->cp_padat & ~SD_CLK;
//unsigned long data_high = cpi->iop_padat | SD_DI;
//unsigned long data_low = cpi->iop_padat & ~SD_DI;
unsigned int cldl=cpi->iop_padat & ~(SD_CLK | SD_DI);
unsigned int cldh=(cpi->iop_padat | SD_DI) & ~SD_CLK;
unsigned int chdl=(cpi->iop_padat | SD_CLK) & ~SD_DI;
unsigned int chdh=cpi->iop_padat | SD_DI | SD_CLK;
//cpi->iop_padat |= SD_DI; // for read access we send high bytes only
unsigned char data = *dest++;
unsigned int i=0;
for (i = 0; i < 512; i++)
{
// bit 7
if (data & 0x80) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 6
if (data & 0x40) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 5
if (data & 0x20) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 4
if (data & 0x10) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 3
if (data & 0x08) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 2
if (data & 0x04) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 1
if (data & 0x02) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
// bit 0
if (data & 0x01) {
wmb();
cpi->iop_padat = cldh; // set data-high
wmb();
cpi->iop_padat = chdh; // latch
}
else {
wmb();
cpi->iop_padat = cldl; // set data-low
wmb();
cpi->iop_padat = chdl; // latch
}
data = *dest++;
}
cpi->iop_padat = cldh;
}
///////////////////////////////////////////////////////////////////////////////////////////////
/////// High-Level Driver
///////////////////////////////////////////////////////////////////////////////////////////////
static int mmc_write_block(unsigned int dest_addr, unsigned char *data)
{
unsigned char r = 0;
int i;
mmc_spi_cs_low();
//for (i = 0; i < 4; i++) mmc_spi_read();
mmc_spi_write(0x58); //cmd24 (WRITE_BLOCK)
mmc_spi_write(0xff & (dest_addr >> 24)); /* msb */
mmc_spi_write(0xff & (dest_addr >> 16));
mmc_spi_write(0xff & (dest_addr >> 8));
mmc_spi_write(0xff & dest_addr); /* lsb */
mmc_spi_write(0xff);
for (i = 0; i < 8; i++)
{
r = mmc_spi_read();
if (r == 0x00) break;
}
if (r != 0x00)
{
mmc_spi_cs_high();
mmc_spi_read();
return(1);
}
mmc_spi_write(0xfe);
mmc_write_block_low_level(data);
//for (i = 0; i < 512; i++) mmc_spi_write(data[i]);
//for (i = 0; i < 2; i++) mmc_spi_write(0xff); // send CRC for nothing
mmc_spi_write(0xff); //fake CRC
mmc_spi_write(0xff);
for (i = 0; i < 1000000; i++)
{
r = mmc_spi_read();
if (r == 0xff) break;
}
if (r != 0xff)
{
mmc_spi_cs_high();
mmc_spi_read();
return(3);
}
mmc_spi_cs_high();
mmc_spi_read();
return(0);
}
static int mmc_write_multiblock(unsigned int dest_addr, unsigned char *data, unsigned int nr_blocks)
{
unsigned char r = 0;
int i;
mmc_spi_cs_low();
//for (i = 0; i < 4; i++) mmc_spi_read();
mmc_spi_write(0x77); //cmd55 (app_cmd)
mmc_spi_write(0);
mmc_spi_write(0);
mmc_spi_write(0);
mmc_spi_write(0);
mmc_spi_write(0xff);
i=255;
do
{
r = mmc_spi_read();
}while((i--)&&(r&0x80));
if (r != 0x00)
{
mmc_spi_cs_high();
mmc_spi_read();
return(4);
}
mmc_spi_cs_high();
mmc_spi_cs_low();
mmc_spi_write(0x57); //acmd23 (SET_WR_BLK_ERASE_COUNT)
mmc_spi_write(0xff & (nr_blocks >> 24)); /* msb */
mmc_spi_write(0xff & (nr_blocks >> 16));
mmc_spi_write(0xff & (nr_blocks >> 8));
mmc_spi_write(0xff & nr_blocks); /* lsb */
mmc_spi_write(0xff);
i=255;
do
{
r = mmc_spi_read();
}while((i--)&&(r&0x80));
if (r != 0x00)
{
mmc_spi_cs_high();
mmc_spi_read();
return(5);
}
mmc_spi_cs_high();
mmc_spi_cs_low();
mmc_spi_write(0x59); //cmd25 (WRITE_MULTIPLE_BLOCK)
mmc_spi_write(0xff & (dest_addr >> 24)); /* msb */
mmc_spi_write(0xff & (dest_addr >> 16));
mmc_spi_write(0xff & (dest_addr >> 8));
mmc_spi_write(0xff & dest_addr); /* lsb */
mmc_spi_write(0xff);
for (i = 0; i < 8; i++)
{
r = mmc_spi_read();
if (r == 0x00) break;
}
if (r != 0x00)
{
mmc_spi_cs_high();
mmc_spi_read();
return(1);
}
while (nr_blocks)
{
mmc_spi_write(0xfc);
mmc_write_block_low_level(data);
//for (i = 0; i < 512; i++) mmc_spi_write(data[i]);
//for (i = 0; i < 2; i++) mmc_spi_write(0xff); // send CRC for nothing
mmc_spi_write(0xff); //fake CRC
mmc_spi_write(0xff);
for (i = 0; i < 1000000; i++)
{
r = mmc_spi_read();
if (r == 0xff) break;
}
if (r != 0xff)
{
mmc_spi_cs_high();
mmc_spi_read();
return(3);
}
nr_blocks--;
data+=512;
}
mmc_spi_write(0xfd);
for (i = 0; i < 1000000; i++)
{
r = mmc_spi_read();
if (r == 0xff) break;
}
if (r != 0xff)
{
mmc_spi_cs_high();
mmc_spi_read();
return(6);
}
mmc_spi_cs_high();
mmc_spi_read();
return(0);
}
static int mmc_read_block(unsigned char *data, unsigned int src_addr)
{
unsigned char r = 0;
int i;
mmc_spi_cs_low();
//for (i = 0; i < 4; i++) mmc_spi_read();
mmc_spi_write(0x51); //cmd17 (READ_SINGLE_BLOCK)
mmc_spi_write(0xff & (src_addr >> 24)); /* msb */
mmc_spi_write(0xff & (src_addr >> 16));
mmc_spi_write(0xff & (src_addr >> 8));
mmc_spi_write(0xff & src_addr); /* lsb */
mmc_spi_read();
for (i = 0; i < 8; i++)
{
r = mmc_spi_read();
if (r == 0x00) break;
}
if (r != 0x00)
{
mmc_spi_cs_high();
mmc_spi_read();
return(1);
}
for (i = 0; i < 100000; i++)
{
r = mmc_spi_read();
if (r == 0xfe) break;
}
if (r != 0xfe)
{
mmc_spi_cs_high();
mmc_spi_read();
return(2);
}
//for (i = 0; i < 512; i++) data[i] = mmc_spi_io(0xff);
mmc_read_block_low_level(data);
mmc_spi_read(); // just read the CRC for nothing
mmc_spi_read();
mmc_spi_cs_high();
mmc_spi_read();
return(0);
}
static int mmc_card_init(void)
{
unsigned char r = 0;
unsigned int i, j;
unsigned long flags;
unsigned char r7vca;
unsigned char r7echo;
save_flags(flags);
cli();
init:
printk("mmc: Card init\n");
mmc_spi_cs_high();
for (i = 0; i < 100; i++) mmc_spi_io(0xff); ///need at least 74clk -- now this is 800clk
unsigned short cntr=0;
do{
mmc_spi_cs_low(); //ATN
mmc_spi_io(0x40); //cmd0
for (i = 0; i < 4; i++) mmc_spi_io(0x00); //arg[4]
mmc_spi_io(0x95); //crc7+1 -- need to be good
for (i = 0; i < 0xff; i++)
{
r = mmc_spi_io(0xff);
if (r != 0xff) break;
}
cntr++;
mmc_spi_io(0xff); //blank msg
mmc_spi_cs_high();
} while((r != 0x01)&&(cntr<0x40)); //continue if r1 response=idle or timeout
if (r != 0x01) //if not idle, return ///actually idle means initializing... (stupid, isn't it?)
{
restore_flags(flags);
printk("mmc: Card init -cmd0 not respond- %d\n",r);
return(1);
}
printk("mmc: Card init *OK1* (cmd0 response)\n");
mmc_type=2;
//ok1
//cntr=0;
//do
//{
//cntr++;
mmc_spi_cs_low();
//--cmd8
mmc_spi_io(0x48); //cmd8 --- check/init SDHC/SDXC
//--args
mmc_spi_io(0x00);
mmc_spi_io(0x00);
mmc_spi_io(0x01);
mmc_spi_io(0xaa); //check pattern (10101010)
//--crc
mmc_spi_io(0x87); //need to be good
i=0;
do
{
r = mmc_spi_io(0xff);
i++;
}while((i<8)&&(r==0xff));
r7vca=mmc_spi_io(0xff);
r7vca=mmc_spi_io(0xff);
r7vca=mmc_spi_io(0xff);
r7echo=mmc_spi_io(0xff); //if there is a valid answer, this should be 0xaa or the communication is corrupted
//if (cntr>10) //only 10 retry...(should be enough)
//{
// restore_flags(flags);
// printk("mmc: Card init -(cmd8) error-\n");
// return(2);
//}
mmc_spi_cs_high();
mmc_spi_io(0xff); //blank msg
//}while ( ((r&0x04)!=0x04) && ((r!=0x01)||(r7echo!=0xaa)) ); //exit if illegal cmd (SDSCv1.0 or MMC) or valid response
if (r7echo==0xaa)
{
printk("mmc: SD Card v2 -- cmd8 response ok\n");
//check voltage support -- cmd8 response vca part
if (!(r7vca&1))
{
restore_flags(flags);
printk("mmc: SD Card voltage not supported\n");
return(2);
}
}
else if((r&0x04)==0x04)
{
printk("mmc: legacy sd or mmc card -- cmd8 response illegal cmd\n");
mmc_type=1; //mmc or legacy sd card
}
//check voltage support for sd/2gb+ support for mmc cards -- cmd58
//cntr=0;
unsigned char o1;
unsigned char o2;
unsigned char o3;
unsigned char o4;
//do
//{
mmc_spi_cs_low(); //ATN
mmc_spi_io(0x7a); //cmd58
mmc_spi_io(0x40); //30:29=10b //mmc 2gb+ init
for (i = 0; i < 3; i++) mmc_spi_io(0x00); //args
mmc_spi_io(0xff); //fake crc (need no more -- disabled by default for spi except cmd0 and cmd8)
//printk("mmc: send cmd58 resp:\n");
i=0;
do
{
r = mmc_spi_io(0xff);
//printk("%d.=%d\t",i,r);
i++;
}while((i<255)&&(r!=0x00));
if (r == 255)
{
printk("mmc: (58) Card not ready / not respond -- %d\n",r);
//break;
};
//read OCR register
//cntr++;
o1=mmc_spi_io(0xff);
o2=mmc_spi_io(0xff);
o3=mmc_spi_io(0xff);
o4=mmc_spi_io(0xff);
//if ((i==8) && (r==0xff))
//{
// restore_flags(flags);
// printk("mmc: Cmd58 timeout\n");
// return(2);
//}
mmc_spi_cs_high();
mmc_spi_io(0xff);
//} while ( (r&0x80==0x80)&& !((r==0x00)||((r&0x04)==0x04)) ); //while busy -- exit if valid response and (ready or invalid cmd)
if (r!=0xff)
{
printk("mmc: i=%d ## o1=%d o2=%d o3=%d o4=%d\n",i,o1,o2,o3,o4);
if ((o1&80) &&(o1&40) && (mmc_type==2))
{
mmc_type=3;
};
};
if (r&0x04)
{
mmc_type=1;
};
m8xx_wdt_reset();
mmc_spi_io(0xff); //blank msg
//mmc_spi_io(0xff); //blank msg
//mmc_spi_io(0xff); //blank msg
printk("mmc: init mmc_type %d \n",mmc_type);
printk("mmc: start sd init -acmd41-\n");
for (j = 0; j < 100; j++)
{
//printk("mmc: %d- cmd55 \n",j);
mmc_spi_cs_low(); //ATN
//mmc_spi_io(0xff);
mmc_spi_io(0x77);
for (i = 0; i < 4; i++) mmc_spi_io(0x00);
mmc_spi_io(0xff);
i=255;
do
{
r = mmc_spi_io(0xff);
}while((i--)&&(r&0x80));
if (r == 0)
{
printk("mmc: (55) Card init *OK2* (SD card)\n");
break;
};
if ((r&0x84)==0x04)
{
printk("mmc: (55) a/cmd41 unknown -- switching to mmc mode init\n");
mmc_type=0;
break;
}
else
{
//printk("mmc: %d : unk55 response - %d \n",j,r);
}
//mmc_spi_io(0xff);
//mmc_spi_io(0xff);
//mmc_spi_io(0xff);
mmc_spi_cs_high();
mmc_spi_io(0xff);
//printk("mmc: %d- acmd41 \n",j);
mmc_spi_cs_low(); //ATN
//mmc_spi_io(0xff);
mmc_spi_io(0x69);
//--args
mmc_spi_io(0x40); //high capacity card support
mmc_spi_io(0x00);
mmc_spi_io(0x00);
mmc_spi_io(0x00);
//--fake_crc
mmc_spi_io(0xff);
i=200;
do
{
r = mmc_spi_io(0xff);
}while((i--)&&(r&0x80));
if (r == 0)
{
printk("mmc: Card init *OK2* (SD card)\n");
break;
};
if ((r&0x84)==0x04)
{
printk("mmc: a/cmd41 unknown -- switching to mmc mode init\n");
mmc_type=0;
break;
}
else
{
printk("mmc: %d : unk41 response - %d \n",j,r);
//if ((j>10)&&(r==0xff)) goto init;
if ((j>98)&&(r==1)) goto init;
}
//mmc_spi_io(0xff);
//mmc_spi_io(0xff);
mmc_spi_cs_high();
mmc_spi_io(0xff);
if(j%10==0)m8xx_wdt_reset();
//mmc_spi_io(0xff);
//mmc_spi_io(0xff);
//mmc_spi_io(0xff);
};
mmc_spi_cs_high();
mmc_spi_io(0xff);
if (mmc_type==0)
{
printk("mmc: legacy/mmc init -cmd1-\n");
for (j = 0; j < 100; j++)
{
printk("mmc: %d- cmd1 \n",j);
mmc_spi_cs_low();
//mmc_spi_io(0xff);
mmc_spi_io(0x41);
for (i = 0; i < 4; i++) mmc_spi_io(0x00);
mmc_spi_io(0xff);
i=0;
do
{
r = mmc_spi_io(0xff);
i++;
}while((i<100)&&(r==0xff));
if (r==0)
{
printk("mmc: Card init *OK2* (SDSCv1 or MMC)\n");
break;
}
mmc_spi_cs_high();
mmc_spi_io(0xff);
if(j%10==0)m8xx_wdt_reset();
}
mmc_spi_cs_high();
mmc_spi_io(0xff);
};
//cntr=0;
if(mmc_type>1)
{
printk("mmc: Cmd58 send\n");
mmc_spi_cs_low(); //ATN
mmc_spi_io(0x7a); //cmd58
mmc_spi_io(0x40); //30:29=10b //mmc 2gb+ init
for (i = 0; i < 3; i++) mmc_spi_io(0x00); //args
mmc_spi_io(0xff); //fake crc (need no more -- disabled by default for spi except cmd0 and cmd8)
//printk("mmc: send cmd58 resp:\n");
i=0;
do
{
r = mmc_spi_io(0xff);
//printk("%d.=%d\t",i,r);
i++;
}while((i<80)&&(r!=0));
//read OCR register
// cntr++;
o1=mmc_spi_io(0xff);
o2=mmc_spi_io(0xff);
o3=mmc_spi_io(0xff);
o4=mmc_spi_io(0xff);
mmc_spi_io(0xff);
mmc_spi_cs_high();
mmc_spi_io(0xff);
if (i>=8)
{
restore_flags(flags);
printk("mmc: Cmd58 timeout\n");
return(2);
}
//} while ( (r&0x80==0x80)&& !((r==0x00)||((r&0x04)==0x04)) ); //while busy -- exit if valid response and (ready or invalid cmd)
printk("mmc: i=%d ## o1=%d o2=%d o3=%d o4=%d\n",i,o1,o2,o3,o4);
if (r!=0xff)
{
if ((o1&80) && (o1&40) && (mmc_type==2))
{
mmc_type=3;
}
}
}
printk("mmc: init memory_card_type=%d\n",mmc_type);
restore_flags(flags);
return(0);
}
static int mmc_card_config(void)
{
unsigned char r = 0;
short i;//,j;
unsigned char csd[32];
unsigned int c_size;
unsigned int c_size_mult;
unsigned int mult;
unsigned int read_bl_len;
unsigned int blocknr = 0;
unsigned int block_len = 0;
unsigned int size = 0;
//j=100;
//do
//{
mmc_spi_cs_high();
//printk("mmc: send cmd9 (SEND_CSD) for %d times - %d\n",101-j,r);
mmc_spi_io(0xff);
mmc_spi_io(0xff);
mmc_spi_io(0xff);
mmc_spi_cs_low();
//for (i = 0; i < 4; i++) mmc_spi_io(0xff); //pre_junk (???)
mmc_spi_io(0x49); //cmd9 (SEND_CSD) -- only enough for sd cards
mmc_spi_io(0x00); //args
mmc_spi_io(0x00); //args
mmc_spi_io(0x00); //args
mmc_spi_io(0x00); //args
mmc_spi_io(0xaf); //fake crc
//printk("mmc: send cmd9 resp:\n");
for (i = 0; i < 8; i++)
{
r = mmc_spi_io(0xff); //wait for R1_READY response
//printk("%d.=%d\t",i,r);
if (r==0) break;
}
//j--;
//}while((j) && (r!=0));
if (r != 0x00) //if not, error
{
mmc_spi_cs_high();
mmc_spi_io(0xff);
printk("mmc: config cmd9 response timeout %d\n",r);
return(1);
}
printk("mmc: valid CSD R1 response: %d\n",r);
for (i = 0; i < 8; i++)
{
r = mmc_spi_io(0xff); //wait for startbit_token (%1111 1110)
//printk("%d.=%d\t",i,r);
if (r == 0xfe) break;
}
if (r != 0xfe) //error
{
mmc_spi_cs_high();
mmc_spi_io(0xff);
printk("mmc: config data response timeout %d\n",r);
return(2);
}
//csd[0]=r;
for (i = 0; i < 16; i++)
{
r = mmc_spi_io(0xff);
csd[i] = r;
}
for (i = 0; i < 2; i++) //crc -- not used
{
r = mmc_spi_io(0xff);
}
mmc_spi_cs_high();
mmc_spi_io(0xff);
//if (r == 0x00)
//{
// return(3); //bad response(?)
//}
read_bl_len=0;
c_size_mult=0;
printk("mmc: mmctype=%d csd[0]=%d\n",mmc_type,csd[0]);
if (((mmc_type==2) || (mmc_type==3)) && ((csd[0]&0xc0)==0x40)) //if SDSCv2 or SDHC/SDXC and CSD vers.2
{
block_len=512;
c_size=csd[9]+csd[8]*256+(csd[7]&0x3f)*256*256;
blocknr=(c_size+1)<<10;
size = (c_size+1)<<9; //(c_size+1)*block_len(=512) kbytes
}
else
{
c_size = csd[8] + csd[7] * 256 + (csd[6] & 0x03) * 256 * 256;
c_size >>= 6;
if ((mmc_type==0) && (c_size==0x0fff))
{
printk("mmc: sorry, no 2gb+ mmc support yet"); //'coz i'm layzee :P
return(2);
}
c_size_mult = csd[10] + (csd[9] & 0x03) * 256;
c_size_mult >>= 7;
read_bl_len = csd[5] & 0x0f;
mult = 1;
mult <<= c_size_mult + 2;
blocknr = (c_size + 1) * mult;
block_len = 1;
block_len <<= read_bl_len;
size = block_len * blocknr;
size >>= 10;
}
if (block_len!=512)
{
printk("mmc: TODO - not-512bytes-a-sector support! -- Currently not supported!\n");
return 2;
}
for(i=0; i<(1<<6); i++) {
hd_blocksizes[i] = 1024;
hd_hardsectsizes[i] = block_len;
hd_maxsect[i] = 256;
}
hd_sizes[0] = size;
hd[0].nr_sects = blocknr;
printk("mmc: Media found Size = %d kB, hardsectsize = %d, sectors = %d\n",
size, block_len, blocknr);
printk("mmc: read_bl_len= %d, c_size=%d, c_size_mult=%d\n",
read_bl_len,c_size,c_size_mult);
return 0;
}
#ifdef CHECK_MEDIA_CHANGE
static int mmc_check_media_change(kdev_t dev)
{
(void)dev;
if (mmc_media_changed == 1)
{
mmc_media_changed = 0;
return 1;
}
else return 0;
}
#endif
static int mmc_init(void)
{
int rc;
rc = mmc_hardware_init();
if ( rc != 0)
{
printk("mmc: error in mmc_hardware_init (%d)\n", rc);
return -1;
}
rc = mmc_card_init();
if ( rc != 0)
{
// Give it an extra shot
rc = mmc_card_init();
if ( rc != 0)
{
printk("mmc: error in mmc_card_init (%d)\n", rc);
return -1;
}
}
memset(hd_sizes, 0, sizeof(hd_sizes));
rc = mmc_card_config();
if ( rc != 0)
{
printk("mmc: error in mmc_card_config (%d)\n", rc);
return -1;
}
blk_size[MAJOR_NR] = hd_sizes;
memset(hd, 0, sizeof(hd));
hd[0].nr_sects = hd_sizes[0]*2;
blksize_size[MAJOR_NR] = hd_blocksizes;
hardsect_size[MAJOR_NR] = hd_hardsectsizes;
max_sectors[MAJOR_NR] = hd_maxsect;
hd_gendisk.nr_real = 1;
register_disk(&hd_gendisk, MKDEV(MAJOR_NR,0), 1<<6,&mmc_bdops, hd_sizes[0]*2);
return 0;
}
static void mmc_exit(void)
{
blk_size[MAJOR_NR] = NULL;
blksize_size[MAJOR_NR] = NULL;
hardsect_size[MAJOR_NR] = NULL;
max_sectors[MAJOR_NR] = NULL;
hd[0].nr_sects = 0;
}
static void mmc_check_media(void)
{
int old_state;
int rc;
old_state = mmc_media_detect;
// TODO: Add card detection here
mmc_media_detect = 1;
if (old_state != mmc_media_detect)
{
mmc_media_changed = 1;
if (mmc_media_detect == 1)
{
rc = mmc_init();
if (rc != 0) printk("mmc: error in mmc_init (%d)\n", rc);
}
else
{
mmc_exit();
}
}
#ifdef CHECK_MEDIA_CHANGE
del_timer(&mmc_timer);
mmc_timer.expires = jiffies + 10*HZ;
add_timer(&mmc_timer);
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////
/////// Driver Interface
///////////////////////////////////////////////////////////////////////////////////////////////
static int mmc_revalidate(kdev_t dev)
{
int target, max_p, start, i;
if (mmc_media_detect == 0) return -ENODEV;
target = DEVICE_NR(dev);
max_p = hd_gendisk.max_p;
start = target << 6;
for (i = max_p - 1; i >= 0; i--) {
int minor = start + i;
invalidate_device(MKDEV(MAJOR_NR, minor), 1);
hd_gendisk.part[minor].start_sect = 0;
hd_gendisk.part[minor].nr_sects = 0;
}
grok_partitions(&hd_gendisk, target, 1 << 6,
hd_sizes[0] * 2);
return 0;
}
static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
if (!inode || !inode->i_rdev)
return -EINVAL;
switch(cmd) {
case BLKGETSIZE:
return put_user(hd[MINOR(inode->i_rdev)].nr_sects, (unsigned long *)arg);
case BLKGETSIZE64:
return put_user((u64)hd[MINOR(inode->i_rdev)].
nr_sects, (u64 *) arg);
case BLKRRPART:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
return mmc_revalidate(inode->i_rdev);
case HDIO_GETGEO:
{
struct hd_geometry *loc, g;
loc = (struct hd_geometry *) arg;
if (!loc)
return -EINVAL;
g.heads = 4;
g.sectors = 16;
g.cylinders = hd[0].nr_sects / (4 * 16);
g.start = hd[MINOR(inode->i_rdev)].start_sect;
return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0;
}
default:
return blk_ioctl(inode->i_rdev, cmd, arg);
}
}
static void mmc_request(request_queue_t *q)
{
unsigned int mmc_address;
unsigned char *buffer_address;
int nr_sectors;
int i;
int cmd;
int rc, code;
(void)q;
while (1)
{
///for address translation: SDHC/SDXC uses addressing blocks of 512bytes / SDSC or legacy cards usese byte addressing
code = 1; // Default is success
INIT_REQUEST;
mmc_address = (mmc_type>2)?(CURRENT->sector + hd[MINOR(CURRENT->rq_dev)].start_sect):((CURRENT->sector + hd[MINOR(CURRENT->rq_dev)].start_sect) * hd_hardsectsizes[0]);
buffer_address = CURRENT->buffer;
nr_sectors = CURRENT->current_nr_sectors;
cmd = CURRENT->cmd;
if (((CURRENT->sector + CURRENT->current_nr_sectors + hd[MINOR(CURRENT->rq_dev)].start_sect) > hd[0].nr_sects) || (mmc_media_detect == 0))
{
code = 0;
}
else if (cmd == READ)
{
spin_unlock_irq(&io_request_lock);
for (i = 0; i < nr_sectors; i++)
{
rc = mmc_read_block(buffer_address, mmc_address);
if (rc != 0)
{
printk("mmc: error in mmc_read_block (%d)\n", rc);
code = 0;
break;
}
else
{
mmc_address +=(mmc_type>2)?1:hd_hardsectsizes[0];
buffer_address +=hd_hardsectsizes[0];
}
}
spin_lock_irq(&io_request_lock);
}
else if (cmd == WRITE)
{
spin_unlock_irq(&io_request_lock);
if ((mmc_type==0) || (mmc_type==4)) ///sorry, it's not for MMC
{
for (i = 0; i < nr_sectors; i++)
{
rc = mmc_write_block(mmc_address, buffer_address);
if (rc != 0)
{
printk("mmc: error in mmc_write_block (%d)\n", rc);
code = 0;
break;
}
else
{
mmc_address +=(mmc_type>2)?1:hd_hardsectsizes[0];
buffer_address +=hd_hardsectsizes[0];
}
}
}
else //but for SD cards
{
rc = mmc_write_multiblock(mmc_address, buffer_address, nr_sectors);
if (rc != 0)
{
printk("mmc: error in mmc_write_multiblock (%d)\n", rc);
code = 0;
break;
}
else
{
mmc_address +=(mmc_type>2)?nr_sectors:(hd_hardsectsizes[0]*nr_sectors);
buffer_address +=(hd_hardsectsizes[0]*nr_sectors);
}
}
spin_lock_irq(&io_request_lock);
}
else
{
code = 0;
}
end_request(code);
}
}
static int mmc_open(struct inode *inode, struct file *filp)
{
//int device;
(void)filp;
if (mmc_media_detect == 0) return -ENODEV;
#if defined(MODULE)
MOD_INC_USE_COUNT;
#endif
return 0;
}
static int mmc_release(struct inode *inode, struct file *filp)
{
(void)filp;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
#if defined(MODULE)
MOD_DEC_USE_COUNT;
#endif
return 0;
}
static int __init mmc_driver_init(void)
{
int rc;
printk("mmc2 Driver $Id: mmc2.c,v 2.0 2011/12/27 13:42:05 satsuse Exp $\n");
rc = devfs_register_blkdev(MAJOR_NR, DEVICE_NAME, &mmc_bdops);
if (rc < 0)
{
printk(KERN_WARNING "mmc: can't get major %d\n", MAJOR_NR);
return rc;
}
blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), mmc_request);
read_ahead[MAJOR_NR] = 8;
add_gendisk(&hd_gendisk);
mmc_check_media();
#ifdef CHECK_MEDIA_CHANGE
init_timer(&mmc_timer);
mmc_timer.expires = jiffies + HZ;
mmc_timer.function = (void *)mmc_check_media;
add_timer(&mmc_timer);
#endif
return 0;
}
static void __exit mmc_driver_exit(void)
{
int i;
del_timer(&mmc_timer);
for (i = 0; i < (1 << 6); i++) //64
fsync_dev(MKDEV(MAJOR_NR, i));
devfs_register_partitions(&hd_gendisk, 0<<6, 1); //0<<6 STILL EQ 0 !!
devfs_unregister_blkdev(MAJOR_NR, DEVICE_NAME);
del_gendisk(&hd_gendisk);
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
mmc_exit();
printk("mmc: removing driver\n");
}
module_init(mmc_driver_init);
module_exit(mmc_driver_exit);