From fec3193f8a32ce4158767ee063105692cce4c1b8 Mon Sep 17 00:00:00 2001 From: Marcin Bukat Date: Tue, 1 Mar 2011 07:53:46 +0000 Subject: Move ata_mmc.c into target tree as it is SH (ondio) specific. Associated header file is left intact as it seems to be used in many places for historical reasons git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29478 a1c6a512-1295-4272-9138-f99709370657 --- firmware/SOURCES | 4 +- firmware/drivers/ata_mmc.c | 1027 ----------------------------- firmware/target/sh/archos/ondio/ata_mmc.c | 1027 +++++++++++++++++++++++++++++ 3 files changed, 1028 insertions(+), 1030 deletions(-) delete mode 100644 firmware/drivers/ata_mmc.c create mode 100644 firmware/target/sh/archos/ondio/ata_mmc.c diff --git a/firmware/SOURCES b/firmware/SOURCES index e0186af..b1378ff 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -197,9 +197,6 @@ drivers/touchscreen.c /* Storage */ #if (CONFIG_PLATFORM & PLATFORM_NATIVE) -#if (CONFIG_STORAGE & STORAGE_MMC) -drivers/ata_mmc.c -#endif #if (CONFIG_STORAGE & STORAGE_NAND) && (CONFIG_NAND == NAND_IFP7XX) drivers/ata_flash.c #endif @@ -636,6 +633,7 @@ target/sh/archos/ondio/button-ondio.c target/sh/archos/ondio/power-ondio.c target/sh/archos/ondio/powermgmt-ondio.c target/sh/archos/ondio/usb-ondio.c +target/sh/archos/ondio/ata_mmc.c target/sh/archos/mascodec-archos.c target/sh/archos/audio-archos.c #if (CONFIG_TUNER & TEA5767) diff --git a/firmware/drivers/ata_mmc.c b/firmware/drivers/ata_mmc.c deleted file mode 100644 index fc2efcd..0000000 --- a/firmware/drivers/ata_mmc.c +++ /dev/null @@ -1,1027 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2004 by Jens Arnold - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include -#include "mmc.h" -#include "ata_mmc.h" -#include "sdmmc.h" -#include "ata_idle_notify.h" -#include "kernel.h" -#include "thread.h" -#include "led.h" -#include "sh7034.h" -#include "system.h" -#include "debug.h" -#include "panic.h" -#include "usb.h" -#include "power.h" -#include "string.h" -#include "hwcompat.h" -#include "adc.h" -#include "bitswap.h" -#include "disk.h" /* for mount/unmount */ -#include "storage.h" - -#define BLOCK_SIZE 512 /* fixed */ - -/* Command definitions */ -#define CMD_GO_IDLE_STATE 0x40 /* R1 */ -#define CMD_SEND_OP_COND 0x41 /* R1 */ -#define CMD_SEND_CSD 0x49 /* R1 */ -#define CMD_SEND_CID 0x4a /* R1 */ -#define CMD_STOP_TRANSMISSION 0x4c /* R1 */ -#define CMD_SEND_STATUS 0x4d /* R2 */ -#define CMD_SET_BLOCKLEN 0x50 /* R1 */ -#define CMD_READ_SINGLE_BLOCK 0x51 /* R1 */ -#define CMD_READ_MULTIPLE_BLOCK 0x52 /* R1 */ -#define CMD_WRITE_BLOCK 0x58 /* R1b */ -#define CMD_WRITE_MULTIPLE_BLOCK 0x59 /* R1b */ -#define CMD_READ_OCR 0x7a /* R3 */ - -/* Response formats: - R1 = single byte, msb=0, various error flags - R1b = R1 + busy token(s) - R2 = 2 bytes (1st byte identical to R1), additional flags - R3 = 5 bytes (R1 + OCR register) -*/ - -#define R1_PARAMETER_ERR 0x40 -#define R1_ADDRESS_ERR 0x20 -#define R1_ERASE_SEQ_ERR 0x10 -#define R1_COM_CRC_ERR 0x08 -#define R1_ILLEGAL_CMD 0x04 -#define R1_ERASE_RESET 0x02 -#define R1_IN_IDLE_STATE 0x01 - -#define R2_OUT_OF_RANGE 0x80 -#define R2_ERASE_PARAM 0x40 -#define R2_WP_VIOLATION 0x20 -#define R2_CARD_ECC_FAIL 0x10 -#define R2_CC_ERROR 0x08 -#define R2_ERROR 0x04 -#define R2_ERASE_SKIP 0x02 -#define R2_CARD_LOCKED 0x01 - -/* Data start tokens */ - -#define DT_START_BLOCK 0xfe -#define DT_START_WRITE_MULTIPLE 0xfc -#define DT_STOP_TRAN 0xfd - -/* for compatibility */ -static long last_disk_activity = -1; - -/* private variables */ - -static struct mutex mmc_mutex SHAREDBSS_ATTR; - -#ifdef HAVE_HOTSWAP -static long mmc_stack[((DEFAULT_STACK_SIZE*2) + 0x800)/sizeof(long)]; -#else -static long mmc_stack[(DEFAULT_STACK_SIZE*2)/sizeof(long)]; -#endif -static const char mmc_thread_name[] = "mmc"; -static struct event_queue mmc_queue SHAREDBSS_ATTR; -static bool initialized = false; -static bool new_mmc_circuit; - -static enum { - MMC_UNKNOWN, - MMC_UNTOUCHED, - MMC_TOUCHED -} mmc_status = MMC_UNKNOWN; - -static enum { - SER_POLL_WRITE, - SER_POLL_READ, - SER_DISABLED -} serial_mode; - -static const unsigned char dummy[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF -}; - -/* 2 buffers used alternatively for writing, including start token, - * dummy CRC and an extra byte to keep word alignment. */ -static unsigned char write_buffer[2][BLOCK_SIZE+4]; -static int current_buffer = 0; -static const unsigned char *send_block_addr = NULL; - -static tCardInfo card_info[2]; -#ifndef HAVE_MULTIDRIVE -static int current_card = 0; -#endif -static bool last_mmc_status = false; -static int countdown = -1; /* for mmc switch debouncing. -1 because the - countdown should not happen if the card - is inserted at boot */ -static bool usb_activity; /* monitoring the USB bridge */ -static long last_usb_activity; - -/* private function declarations */ - -static int select_card(int card_no); -static void deselect_card(void); -static void setup_sci1(int bitrate_register); -static void set_sci1_poll_read(void); -static void write_transfer(const unsigned char *buf, int len) - __attribute__ ((section(".icode"))); -static void read_transfer(unsigned char *buf, int len) - __attribute__ ((section(".icode"))); -static unsigned char poll_byte(long timeout); -static unsigned char poll_busy(long timeout); -static unsigned char send_cmd(int cmd, unsigned long parameter, void *data); -static int receive_cxd(unsigned char *buf); -static int initialize_card(int card_no); -static int receive_block(unsigned char *inbuf, long timeout); -static void send_block_prepare(void); -static int send_block_send(unsigned char start_token, long timeout, - bool prepare_next); -static void mmc_tick(void); - -/* implementation */ - -void mmc_enable_int_flash_clock(bool on) -{ - /* Internal flash clock is enabled by setting PA12 high with the new - * clock circuit, and by setting it low with the old clock circuit */ - if (on ^ new_mmc_circuit) - and_b(~0x10, &PADRH); /* clear clock gate PA12 */ - else - or_b(0x10, &PADRH); /* set clock gate PA12 */ -} - -static int select_card(int card_no) -{ - mutex_lock(&mmc_mutex); - led(true); - last_disk_activity = current_tick; - - mmc_enable_int_flash_clock(card_no == 0); - - if (!card_info[card_no].initialized) - { - setup_sci1(7); /* Initial rate: 375 kbps (need <= 400 per mmc specs) */ - write_transfer(dummy, 10); /* allow the card to synchronize */ - while (!(SSR1 & SCI_TEND)); - } - - if (card_no == 0) /* internal */ - and_b(~0x04, &PADRH); /* assert CS */ - else /* external */ - and_b(~0x02, &PADRH); /* assert CS */ - - if (card_info[card_no].initialized) - { - setup_sci1(card_info[card_no].bitrate_register); - return 0; - } - else - { - return initialize_card(card_no); - } -} - -static void deselect_card(void) -{ - while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ - or_b(0x06, &PADRH); /* deassert CS (both cards) */ - - led(false); - mutex_unlock(&mmc_mutex); - last_disk_activity = current_tick; -} - -static void setup_sci1(int bitrate_register) -{ - while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ - - SCR1 = 0; /* disable serial port */ - SMR1 = SYNC_MODE; /* no prescale */ - BRR1 = bitrate_register; - SSR1 = 0; - - SCR1 = SCI_TE; /* enable transmitter */ - serial_mode = SER_POLL_WRITE; -} - -static void set_sci1_poll_read(void) -{ - while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ - SCR1 = 0; /* disable transmitter (& receiver) */ - SCR1 = (SCI_TE|SCI_RE); /* re-enable transmitter & receiver */ - while (!(SSR1 & SCI_TEND)); /* wait for SCI init completion (!) */ - serial_mode = SER_POLL_READ; - TDR1 = 0xFF; /* send do-nothing while reading */ -} - -static void write_transfer(const unsigned char *buf, int len) -{ - const unsigned char *buf_end = buf + len; - register unsigned char data; - - if (serial_mode != SER_POLL_WRITE) - { - while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ - SCR1 = 0; /* disable transmitter & receiver */ - SSR1 = 0; /* clear all flags */ - SCR1 = SCI_TE; /* enable transmitter only */ - serial_mode = SER_POLL_WRITE; - } - - while (buf < buf_end) - { - data = fliptable[(signed char)(*buf++)]; /* bitswap */ - while (!(SSR1 & SCI_TDRE)); /* wait for end of transfer */ - TDR1 = data; /* write byte */ - SSR1 = 0; /* start transmitting */ - } -} - -/* don't call this with len == 0 */ -static void read_transfer(unsigned char *buf, int len) -{ - unsigned char *buf_end = buf + len - 1; - register signed char data; - - if (serial_mode != SER_POLL_READ) - set_sci1_poll_read(); - - SSR1 = 0; /* start receiving first byte */ - while (buf < buf_end) - { - while (!(SSR1 & SCI_RDRF)); /* wait for data */ - data = RDR1; /* read byte */ - SSR1 = 0; /* start receiving */ - *buf++ = fliptable[data]; /* bitswap */ - } - while (!(SSR1 & SCI_RDRF)); /* wait for last byte */ - *buf = fliptable[(signed char)(RDR1)]; /* read & bitswap */ -} - -/* returns 0xFF on timeout, timeout is in bytes */ -static unsigned char poll_byte(long timeout) -{ - long i; - unsigned char data = 0; /* stop the compiler complaining */ - - if (serial_mode != SER_POLL_READ) - set_sci1_poll_read(); - - i = 0; - do { - SSR1 = 0; /* start receiving */ - while (!(SSR1 & SCI_RDRF)); /* wait for data */ - data = RDR1; /* read byte */ - } while ((data == 0xFF) && (++i < timeout)); - - return fliptable[(signed char)data]; -} - -/* returns 0 on timeout, timeout is in bytes */ -static unsigned char poll_busy(long timeout) -{ - long i; - unsigned char data, dummy; - - if (serial_mode != SER_POLL_READ) - set_sci1_poll_read(); - - /* get data response */ - SSR1 = 0; /* start receiving */ - while (!(SSR1 & SCI_RDRF)); /* wait for data */ - data = fliptable[(signed char)(RDR1)]; /* read byte */ - - /* wait until the card is ready again */ - i = 0; - do { - SSR1 = 0; /* start receiving */ - while (!(SSR1 & SCI_RDRF)); /* wait for data */ - dummy = RDR1; /* read byte */ - } while ((dummy != 0xFF) && (++i < timeout)); - - return (dummy == 0xFF) ? data : 0; -} - -/* Send MMC command and get response. Returns R1 byte directly. - * Returns further R2 or R3 bytes in *data (can be NULL for other commands) */ -static unsigned char send_cmd(int cmd, unsigned long parameter, void *data) -{ - static struct { - unsigned char cmd; - unsigned long parameter; - const unsigned char crc7; /* fixed, valid for CMD0 only */ - const unsigned char trailer; - } __attribute__((packed)) command = {0x40, 0, 0x95, 0xFF}; - - unsigned char ret; - - command.cmd = cmd; - command.parameter = htobe32(parameter); - - write_transfer((unsigned char *)&command, sizeof(command)); - - ret = poll_byte(20); - - switch (cmd) - { - case CMD_SEND_CSD: /* R1 response, leave open */ - case CMD_SEND_CID: - case CMD_READ_SINGLE_BLOCK: - case CMD_READ_MULTIPLE_BLOCK: - return ret; - - case CMD_SEND_STATUS: /* R2 response, close with dummy */ - read_transfer(data, 1); - break; - - case CMD_READ_OCR: /* R3 response, close with dummy */ - read_transfer(data, 4); - break; - - default: /* R1 response, close with dummy */ - break; /* also catches block writes */ - } - write_transfer(dummy, 1); - return ret; -} - -/* Receive CID/ CSD data (16 bytes) */ -static int receive_cxd(unsigned char *buf) -{ - if (poll_byte(20) != DT_START_BLOCK) - { - write_transfer(dummy, 1); - return -1; /* not start of data */ - } - - read_transfer(buf, 16); - write_transfer(dummy, 3); /* 2 bytes dontcare crc + 1 byte trailer */ - return 0; -} - - -static int initialize_card(int card_no) -{ - int rc, i; - int blk_exp, ts_exp, taac_exp; - tCardInfo *card = &card_info[card_no]; - - static const char mantissa[] = { /* *10 */ - 0, 10, 12, 13, 15, 20, 25, 30, - 35, 40, 45, 50, 55, 60, 70, 80 - }; - static const int exponent[] = { /* use varies */ - 1, 10, 100, 1000, 10000, 100000, 1000000, - 10000000, 100000000, 1000000000 - }; - - if (card_no == 1) - mmc_status = MMC_TOUCHED; - - /* switch to SPI mode */ - if (send_cmd(CMD_GO_IDLE_STATE, 0, NULL) != 0x01) - return -1; /* error or no response */ - - /* initialize card */ - for (i = HZ;;) /* try for 1 second*/ - { - sleep(1); - if (send_cmd(CMD_SEND_OP_COND, 0, NULL) == 0) - break; - if (--i <= 0) - return -2; /* timeout */ - } - - /* get OCR register */ - if (send_cmd(CMD_READ_OCR, 0, &card->ocr)) - return -3; - card->ocr = betoh32(card->ocr); /* no-op on big endian */ - - /* check voltage */ - if (!(card->ocr & 0x00100000)) /* 3.2 .. 3.3 V */ - return -4; - - /* get CSD register */ - if (send_cmd(CMD_SEND_CSD, 0, NULL)) - return -5; - rc = receive_cxd((unsigned char*)card->csd); - if (rc) - return rc * 10 - 5; - - blk_exp = card_extract_bits(card->csd, 83, 4); - if (blk_exp < 9) /* block size < 512 bytes not supported */ - return -6; - - card->numblocks = (card_extract_bits(card->csd, 73, 12) + 1) - << (card_extract_bits(card->csd, 49, 3) + 2 + blk_exp - 9); - card->blocksize = BLOCK_SIZE; - - /* max transmission speed, clock divider */ - ts_exp = card_extract_bits(card->csd, 98, 3); - ts_exp = (ts_exp > 3) ? 3 : ts_exp; - card->speed = mantissa[card_extract_bits(card->csd, 102, 4)] - * exponent[ts_exp + 4]; - card->bitrate_register = (FREQ/4-1) / card->speed; - - /* NSAC, TAAC, read timeout */ - card->nsac = 100 * card_extract_bits(card->csd, 111, 8); - card->taac = mantissa[card_extract_bits(card->csd, 118, 4)]; - taac_exp = card_extract_bits(card->csd, 114, 3); - card->read_timeout = ((FREQ/4) / (card->bitrate_register + 1) - * card->taac / exponent[9 - taac_exp] - + (10 * card->nsac)); - card->read_timeout /= 8; /* clocks -> bytes */ - card->taac = card->taac * exponent[taac_exp] / 10; - - /* r2w_factor, write timeout */ - card->r2w_factor = BIT_N(card_extract_bits(card->csd, 28, 3)); - card->write_timeout = card->read_timeout * card->r2w_factor; - - if (card->r2w_factor > 32) /* Such cards often need extra read delay */ - card->read_timeout *= 4; - - /* switch to full speed */ - setup_sci1(card->bitrate_register); - - /* always use 512 byte blocks */ - if (send_cmd(CMD_SET_BLOCKLEN, BLOCK_SIZE, NULL)) - return -7; - - /* get CID register */ - if (send_cmd(CMD_SEND_CID, 0, NULL)) - return -8; - rc = receive_cxd((unsigned char*)card->cid); - if (rc) - return rc * 10 - 8; - - card->initialized = true; - return 0; -} - -tCardInfo *mmc_card_info(int card_no) -{ - tCardInfo *card = &card_info[card_no]; - - if (!card->initialized && ((card_no == 0) || mmc_detect())) - { - select_card(card_no); - deselect_card(); - } - return card; -} - -/* Receive one block with DMA and bitswap it (chasing bitswap). */ -static int receive_block(unsigned char *inbuf, long timeout) -{ - unsigned long buf_end; - - if (poll_byte(timeout) != DT_START_BLOCK) - { - write_transfer(dummy, 1); - return -1; /* not start of data */ - } - - while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ - - SCR1 = 0; /* disable serial */ - SSR1 = 0; /* clear all flags */ - - /* setup DMA channel 0 */ - CHCR0 = 0; /* disable */ - SAR0 = RDR1_ADDR; - DAR0 = (unsigned long) inbuf; - DTCR0 = BLOCK_SIZE; - CHCR0 = 0x4601; /* fixed source address, RXI1, enable */ - DMAOR = 0x0001; - SCR1 = (SCI_RE|SCI_RIE); /* kick off DMA */ - - /* DMA receives 2 bytes more than DTCR2, but the last 2 bytes are not - * stored. The first extra byte is available from RDR1 after the DMA ends, - * the second one is lost because of the SCI overrun. However, this - * behaviour conveniently discards the crc. */ - - yield(); /* be nice */ - - /* Bitswap received data, chasing the DMA pointer */ - buf_end = (unsigned long)inbuf + BLOCK_SIZE; - do - { - /* Call bitswap whenever (a multiple of) 8 bytes are - * available (value optimised by experimentation). */ - int swap_now = (DAR0 - (unsigned long)inbuf) & ~0x00000007; - if (swap_now) - { - bitswap(inbuf, swap_now); - inbuf += swap_now; - } - } - while ((unsigned long)inbuf < buf_end); - - while (!(CHCR0 & 0x0002)); /* wait for end of DMA */ - while (!(SSR1 & SCI_ORER)); /* wait for the trailing bytes */ - SCR1 = 0; - serial_mode = SER_DISABLED; - - write_transfer(dummy, 1); /* send trailer */ - last_disk_activity = current_tick; - return 0; -} - -/* Prepare a block for sending by copying it to the next write buffer - * and bitswapping it. */ -static void send_block_prepare(void) -{ - unsigned char *dest; - - current_buffer ^= 1; /* toggle buffer */ - dest = write_buffer[current_buffer] + 2; - - memcpy(dest, send_block_addr, BLOCK_SIZE); - bitswap(dest, BLOCK_SIZE); - - send_block_addr += BLOCK_SIZE; -} - -/* Send one block with DMA from the current write buffer, possibly preparing - * the next block within the next write buffer in the background. */ -static int send_block_send(unsigned char start_token, long timeout, - bool prepare_next) -{ - int rc = 0; - unsigned char *curbuf = write_buffer[current_buffer]; - - curbuf[1] = fliptable[(signed char)start_token]; - *(unsigned short *)(curbuf + BLOCK_SIZE + 2) = 0xFFFF; - - while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ - - SCR1 = 0; /* disable serial */ - SSR1 = 0; /* clear all flags */ - - /* setup DMA channel 0 */ - CHCR0 = 0; /* disable */ - SAR0 = (unsigned long)(curbuf + 1); - DAR0 = TDR1_ADDR; - DTCR0 = BLOCK_SIZE + 3; /* start token + block + dummy crc */ - CHCR0 = 0x1701; /* fixed dest. address, TXI1, enable */ - DMAOR = 0x0001; - SCR1 = (SCI_TE|SCI_TIE); /* kick off DMA */ - - if (prepare_next) - send_block_prepare(); - yield(); /* be nice */ - - while (!(CHCR0 & 0x0002)); /* wait for end of DMA */ - while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ - SCR1 = 0; - serial_mode = SER_DISABLED; - - if ((poll_busy(timeout) & 0x1F) != 0x05) /* something went wrong */ - rc = -1; - - write_transfer(dummy, 1); - last_disk_activity = current_tick; - - return rc; -} - -int mmc_read_sectors(IF_MD2(int drive,) - unsigned long start, - int incount, - void* inbuf) -{ - int rc = 0; - int lastblock = 0; - unsigned long end_block; - tCardInfo *card; -#ifndef HAVE_MULTIDRIVE - int drive = current_card; -#endif - - card = &card_info[drive]; - rc = select_card(drive); - if (rc) - { - rc = rc * 10 - 1; - goto error; - } - - end_block = start + incount; - if (end_block > card->numblocks) - { - rc = -2; - goto error; - } - - /* Some cards don't like reading the very last block with - * CMD_READ_MULTIPLE_BLOCK, so make sure this block is always - * read with CMD_READ_SINGLE_BLOCK. */ - if (end_block == card->numblocks) - lastblock = 1; - - if (incount > 1) - { - /* MMC4.2: make multiplication conditional */ - if (send_cmd(CMD_READ_MULTIPLE_BLOCK, start * BLOCK_SIZE, NULL)) - { - rc = -3; - goto error; - } - while (--incount >= lastblock) - { - rc = receive_block(inbuf, card->read_timeout); - if (rc) - { - /* If an error occurs during multiple block reading, the - * host still needs to send CMD_STOP_TRANSMISSION */ - send_cmd(CMD_STOP_TRANSMISSION, 0, NULL); - rc = rc * 10 - 4; - goto error; - } - inbuf += BLOCK_SIZE; - start++; - /* ^^ necessary for the abovementioned last block special case */ - } - if (send_cmd(CMD_STOP_TRANSMISSION, 0, NULL)) - { - rc = -5; - goto error; - } - } - if (incount > 0) - { - /* MMC4.2: make multiplication conditional */ - if (send_cmd(CMD_READ_SINGLE_BLOCK, start * BLOCK_SIZE, NULL)) - { - rc = -6; - goto error; - } - rc = receive_block(inbuf, card->read_timeout); - if (rc) - { - rc = rc * 10 - 7; - goto error; - } - } - - error: - - deselect_card(); - - return rc; -} - -int mmc_write_sectors(IF_MD2(int drive,) - unsigned long start, - int count, - const void* buf) -{ - int rc = 0; - int write_cmd; - unsigned char start_token; - tCardInfo *card; -#ifndef HAVE_MULTIDRIVE - int drive = current_card; -#endif - - card = &card_info[drive]; - rc = select_card(drive); - if (rc) - { - rc = rc * 10 - 1; - goto error; - } - - if (start + count > card->numblocks) - panicf("Writing past end of card"); - - send_block_addr = buf; - send_block_prepare(); - - if (count > 1) - { - write_cmd = CMD_WRITE_MULTIPLE_BLOCK; - start_token = DT_START_WRITE_MULTIPLE; - } - else - { - write_cmd = CMD_WRITE_BLOCK; - start_token = DT_START_BLOCK; - } - /* MMC4.2: make multiplication conditional */ - if (send_cmd(write_cmd, start * BLOCK_SIZE, NULL)) - { - rc = -2; - goto error; - } - while (--count >= 0) - { - rc = send_block_send(start_token, card->write_timeout, count > 0); - if (rc) - { - rc = rc * 10 - 3; - break; - /* If an error occurs during multiple block writing, - * the STOP_TRAN token still needs to be sent. */ - } - } - if (write_cmd == CMD_WRITE_MULTIPLE_BLOCK) - { - static const unsigned char stop_tran = DT_STOP_TRAN; - write_transfer(&stop_tran, 1); - poll_busy(card->write_timeout); - } - - error: - - deselect_card(); - - return rc; -} - -bool mmc_disk_is_active(void) -{ - /* this is correct unless early return from write gets implemented */ - return mutex_test(&mmc_mutex); -} - -static void mmc_thread(void) -{ - struct queue_event ev; - bool idle_notified = false; - - while (1) { - queue_wait_w_tmo(&mmc_queue, &ev, HZ); - switch ( ev.id ) - { - case SYS_USB_CONNECTED: - usb_acknowledge(SYS_USB_CONNECTED_ACK); - /* Wait until the USB cable is extracted again */ - usb_wait_for_disconnect(&mmc_queue); - break; - -#ifdef HAVE_HOTSWAP - case SYS_HOTSWAP_INSERTED: - disk_mount(1); /* mount MMC */ - queue_broadcast(SYS_FS_CHANGED, 0); - break; - - case SYS_HOTSWAP_EXTRACTED: - disk_unmount(1); /* release "by force" */ - queue_broadcast(SYS_FS_CHANGED, 0); - break; -#endif - - default: - if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) - { - idle_notified = false; - } - else - { - if (!idle_notified) - { - call_storage_idle_notifys(false); - idle_notified = true; - } - } - break; - } - } -} - -bool mmc_detect(void) -{ - return (adc_read(ADC_MMC_SWITCH) < 0x200); -} - -bool mmc_touched(void) -{ - if (mmc_status == MMC_UNKNOWN) /* try to detect */ - { - mutex_lock(&mmc_mutex); - setup_sci1(7); /* safe value */ - and_b(~0x02, &PADRH); /* assert CS */ - if (send_cmd(CMD_SEND_OP_COND, 0, NULL) == 0xFF) - mmc_status = MMC_UNTOUCHED; - else - mmc_status = MMC_TOUCHED; - - deselect_card(); - } - return mmc_status == MMC_TOUCHED; -} - -bool mmc_usb_active(int delayticks) -{ - /* reading "inactive" is delayed by user-supplied monoflop value */ - return (usb_activity || - TIME_BEFORE(current_tick, last_usb_activity + delayticks)); -} - -static void mmc_tick(void) -{ - bool current_status; - - if (new_mmc_circuit) - /* USB bridge activity is 0 on idle, ~527 on active */ - current_status = adc_read(ADC_USB_ACTIVE) > 0x100; - else - current_status = adc_read(ADC_USB_ACTIVE) < 0x190; - - if (!current_status && usb_activity) - last_usb_activity = current_tick; - usb_activity = current_status; - - current_status = mmc_detect(); - /* Only report when the status has changed */ - if (current_status != last_mmc_status) - { - last_mmc_status = current_status; - countdown = HZ/3; - } - else - { - /* Count down until it gets negative */ - if (countdown >= 0) - countdown--; - - if (countdown == 0) - { - if (current_status) - { - queue_broadcast(SYS_HOTSWAP_INSERTED, 0); - } - else - { - queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0); - mmc_status = MMC_UNTOUCHED; - card_info[1].initialized = false; - } - } - } -} - -void mmc_enable(bool on) -{ - PBCR1 &= ~0x0CF0; /* PB13, PB11 and PB10 become GPIO, - * if not modified below */ - if (on) - PBCR1 |= 0x08A0; /* as SCK1, TxD1, RxD1 */ - - and_b(~0x80, &PADRL); /* assert flash reset */ - sleep(HZ/100); - or_b(0x80, &PADRL); /* de-assert flash reset */ - sleep(HZ/100); - card_info[0].initialized = false; - card_info[1].initialized = false; -} - -int mmc_init(void) -{ - int rc = 0; - - if (!initialized) - { - mutex_init(&mmc_mutex); - queue_init(&mmc_queue, true); - } - mutex_lock(&mmc_mutex); - led(false); - - last_mmc_status = mmc_detect(); -#ifndef HAVE_MULTIDRIVE - /* Use MMC if inserted, internal flash otherwise */ - current_card = last_mmc_status ? 1 : 0; -#endif - - if (!initialized) - { - if (!last_mmc_status) - mmc_status = MMC_UNTOUCHED; - - /* Port setup */ - PACR1 &= ~0x0F3C; /* GPIO function for PA13 (flash busy), PA12 - * (clk gate), PA10 (flash CS), PA9 (MMC CS) */ - PACR2 &= ~0x4000; /* GPIO for PA7 (flash reset) */ - PADR |= 0x0680; /* set all the selects + reset high (=inactive) */ - PAIOR |= 0x1680; /* make outputs for them and the PA12 clock gate */ - - PBCR1 &= ~0x0CF0; /* GPIO function for PB13, PB11 and PB10 */ - PBDR |= 0x2C00; /* SCK1, TxD1 and RxD1 high in GPIO */ - PBIOR |= 0x2000; /* SCK1 output */ - PBIOR &= ~0x0C00; /* TxD1, RxD1 input */ - - IPRE &= 0x0FFF; /* disable SCI1 interrupts for the CPU */ - - new_mmc_circuit = ((HW_MASK & MMC_CLOCK_POLARITY) != 0); - - create_thread(mmc_thread, mmc_stack, - sizeof(mmc_stack), 0, mmc_thread_name - IF_PRIO(, PRIORITY_SYSTEM) - IF_COP(, CPU)); - tick_add_task(mmc_tick); - initialized = true; - } - mmc_enable(true); - - mutex_unlock(&mmc_mutex); - return rc; -} - -long mmc_last_disk_activity(void) -{ - return last_disk_activity; -} - -#ifdef STORAGE_GET_INFO -void mmc_get_info(IF_MD2(int drive,) struct storage_info *info) -{ -#ifndef HAVE_MULTIDRIVE - const int drive=0; -#endif - info->sector_size=card_info[drive].blocksize; - info->num_sectors=card_info[drive].numblocks; - info->vendor="Rockbox"; - if(drive==0) - { - info->product="Internal Storage"; - } - else - { - info->product="MMC Card Slot"; - } - info->revision="0.00"; -} -#endif - -#ifdef HAVE_HOTSWAP -bool mmc_removable(IF_MD_NONVOID(int drive)) -{ -#ifndef HAVE_MULTIDRIVE - const int drive=0; -#endif - return (drive==1); -} - -bool mmc_present(IF_MD_NONVOID(int drive)) -{ -#ifndef HAVE_MULTIDRIVE - const int drive=0; -#endif - if(drive==0) - { - return true; - } - else - { - return mmc_detect(); - } -} -#endif - - -void mmc_sleep(void) -{ -} - -void mmc_spin(void) -{ -} - -void mmc_spindown(int seconds) -{ - (void)seconds; -} - -#ifdef CONFIG_STORAGE_MULTI -int mmc_num_drives(int first_drive) -{ - /* We don't care which logical drive number(s) we have been assigned */ - (void)first_drive; - -#ifdef HAVE_MULTIDRIVE - return 2; -#else - return 1; -#endif -} -#endif diff --git a/firmware/target/sh/archos/ondio/ata_mmc.c b/firmware/target/sh/archos/ondio/ata_mmc.c new file mode 100644 index 0000000..fc2efcd --- /dev/null +++ b/firmware/target/sh/archos/ondio/ata_mmc.c @@ -0,0 +1,1027 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2004 by Jens Arnold + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include "mmc.h" +#include "ata_mmc.h" +#include "sdmmc.h" +#include "ata_idle_notify.h" +#include "kernel.h" +#include "thread.h" +#include "led.h" +#include "sh7034.h" +#include "system.h" +#include "debug.h" +#include "panic.h" +#include "usb.h" +#include "power.h" +#include "string.h" +#include "hwcompat.h" +#include "adc.h" +#include "bitswap.h" +#include "disk.h" /* for mount/unmount */ +#include "storage.h" + +#define BLOCK_SIZE 512 /* fixed */ + +/* Command definitions */ +#define CMD_GO_IDLE_STATE 0x40 /* R1 */ +#define CMD_SEND_OP_COND 0x41 /* R1 */ +#define CMD_SEND_CSD 0x49 /* R1 */ +#define CMD_SEND_CID 0x4a /* R1 */ +#define CMD_STOP_TRANSMISSION 0x4c /* R1 */ +#define CMD_SEND_STATUS 0x4d /* R2 */ +#define CMD_SET_BLOCKLEN 0x50 /* R1 */ +#define CMD_READ_SINGLE_BLOCK 0x51 /* R1 */ +#define CMD_READ_MULTIPLE_BLOCK 0x52 /* R1 */ +#define CMD_WRITE_BLOCK 0x58 /* R1b */ +#define CMD_WRITE_MULTIPLE_BLOCK 0x59 /* R1b */ +#define CMD_READ_OCR 0x7a /* R3 */ + +/* Response formats: + R1 = single byte, msb=0, various error flags + R1b = R1 + busy token(s) + R2 = 2 bytes (1st byte identical to R1), additional flags + R3 = 5 bytes (R1 + OCR register) +*/ + +#define R1_PARAMETER_ERR 0x40 +#define R1_ADDRESS_ERR 0x20 +#define R1_ERASE_SEQ_ERR 0x10 +#define R1_COM_CRC_ERR 0x08 +#define R1_ILLEGAL_CMD 0x04 +#define R1_ERASE_RESET 0x02 +#define R1_IN_IDLE_STATE 0x01 + +#define R2_OUT_OF_RANGE 0x80 +#define R2_ERASE_PARAM 0x40 +#define R2_WP_VIOLATION 0x20 +#define R2_CARD_ECC_FAIL 0x10 +#define R2_CC_ERROR 0x08 +#define R2_ERROR 0x04 +#define R2_ERASE_SKIP 0x02 +#define R2_CARD_LOCKED 0x01 + +/* Data start tokens */ + +#define DT_START_BLOCK 0xfe +#define DT_START_WRITE_MULTIPLE 0xfc +#define DT_STOP_TRAN 0xfd + +/* for compatibility */ +static long last_disk_activity = -1; + +/* private variables */ + +static struct mutex mmc_mutex SHAREDBSS_ATTR; + +#ifdef HAVE_HOTSWAP +static long mmc_stack[((DEFAULT_STACK_SIZE*2) + 0x800)/sizeof(long)]; +#else +static long mmc_stack[(DEFAULT_STACK_SIZE*2)/sizeof(long)]; +#endif +static const char mmc_thread_name[] = "mmc"; +static struct event_queue mmc_queue SHAREDBSS_ATTR; +static bool initialized = false; +static bool new_mmc_circuit; + +static enum { + MMC_UNKNOWN, + MMC_UNTOUCHED, + MMC_TOUCHED +} mmc_status = MMC_UNKNOWN; + +static enum { + SER_POLL_WRITE, + SER_POLL_READ, + SER_DISABLED +} serial_mode; + +static const unsigned char dummy[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* 2 buffers used alternatively for writing, including start token, + * dummy CRC and an extra byte to keep word alignment. */ +static unsigned char write_buffer[2][BLOCK_SIZE+4]; +static int current_buffer = 0; +static const unsigned char *send_block_addr = NULL; + +static tCardInfo card_info[2]; +#ifndef HAVE_MULTIDRIVE +static int current_card = 0; +#endif +static bool last_mmc_status = false; +static int countdown = -1; /* for mmc switch debouncing. -1 because the + countdown should not happen if the card + is inserted at boot */ +static bool usb_activity; /* monitoring the USB bridge */ +static long last_usb_activity; + +/* private function declarations */ + +static int select_card(int card_no); +static void deselect_card(void); +static void setup_sci1(int bitrate_register); +static void set_sci1_poll_read(void); +static void write_transfer(const unsigned char *buf, int len) + __attribute__ ((section(".icode"))); +static void read_transfer(unsigned char *buf, int len) + __attribute__ ((section(".icode"))); +static unsigned char poll_byte(long timeout); +static unsigned char poll_busy(long timeout); +static unsigned char send_cmd(int cmd, unsigned long parameter, void *data); +static int receive_cxd(unsigned char *buf); +static int initialize_card(int card_no); +static int receive_block(unsigned char *inbuf, long timeout); +static void send_block_prepare(void); +static int send_block_send(unsigned char start_token, long timeout, + bool prepare_next); +static void mmc_tick(void); + +/* implementation */ + +void mmc_enable_int_flash_clock(bool on) +{ + /* Internal flash clock is enabled by setting PA12 high with the new + * clock circuit, and by setting it low with the old clock circuit */ + if (on ^ new_mmc_circuit) + and_b(~0x10, &PADRH); /* clear clock gate PA12 */ + else + or_b(0x10, &PADRH); /* set clock gate PA12 */ +} + +static int select_card(int card_no) +{ + mutex_lock(&mmc_mutex); + led(true); + last_disk_activity = current_tick; + + mmc_enable_int_flash_clock(card_no == 0); + + if (!card_info[card_no].initialized) + { + setup_sci1(7); /* Initial rate: 375 kbps (need <= 400 per mmc specs) */ + write_transfer(dummy, 10); /* allow the card to synchronize */ + while (!(SSR1 & SCI_TEND)); + } + + if (card_no == 0) /* internal */ + and_b(~0x04, &PADRH); /* assert CS */ + else /* external */ + and_b(~0x02, &PADRH); /* assert CS */ + + if (card_info[card_no].initialized) + { + setup_sci1(card_info[card_no].bitrate_register); + return 0; + } + else + { + return initialize_card(card_no); + } +} + +static void deselect_card(void) +{ + while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ + or_b(0x06, &PADRH); /* deassert CS (both cards) */ + + led(false); + mutex_unlock(&mmc_mutex); + last_disk_activity = current_tick; +} + +static void setup_sci1(int bitrate_register) +{ + while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ + + SCR1 = 0; /* disable serial port */ + SMR1 = SYNC_MODE; /* no prescale */ + BRR1 = bitrate_register; + SSR1 = 0; + + SCR1 = SCI_TE; /* enable transmitter */ + serial_mode = SER_POLL_WRITE; +} + +static void set_sci1_poll_read(void) +{ + while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ + SCR1 = 0; /* disable transmitter (& receiver) */ + SCR1 = (SCI_TE|SCI_RE); /* re-enable transmitter & receiver */ + while (!(SSR1 & SCI_TEND)); /* wait for SCI init completion (!) */ + serial_mode = SER_POLL_READ; + TDR1 = 0xFF; /* send do-nothing while reading */ +} + +static void write_transfer(const unsigned char *buf, int len) +{ + const unsigned char *buf_end = buf + len; + register unsigned char data; + + if (serial_mode != SER_POLL_WRITE) + { + while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ + SCR1 = 0; /* disable transmitter & receiver */ + SSR1 = 0; /* clear all flags */ + SCR1 = SCI_TE; /* enable transmitter only */ + serial_mode = SER_POLL_WRITE; + } + + while (buf < buf_end) + { + data = fliptable[(signed char)(*buf++)]; /* bitswap */ + while (!(SSR1 & SCI_TDRE)); /* wait for end of transfer */ + TDR1 = data; /* write byte */ + SSR1 = 0; /* start transmitting */ + } +} + +/* don't call this with len == 0 */ +static void read_transfer(unsigned char *buf, int len) +{ + unsigned char *buf_end = buf + len - 1; + register signed char data; + + if (serial_mode != SER_POLL_READ) + set_sci1_poll_read(); + + SSR1 = 0; /* start receiving first byte */ + while (buf < buf_end) + { + while (!(SSR1 & SCI_RDRF)); /* wait for data */ + data = RDR1; /* read byte */ + SSR1 = 0; /* start receiving */ + *buf++ = fliptable[data]; /* bitswap */ + } + while (!(SSR1 & SCI_RDRF)); /* wait for last byte */ + *buf = fliptable[(signed char)(RDR1)]; /* read & bitswap */ +} + +/* returns 0xFF on timeout, timeout is in bytes */ +static unsigned char poll_byte(long timeout) +{ + long i; + unsigned char data = 0; /* stop the compiler complaining */ + + if (serial_mode != SER_POLL_READ) + set_sci1_poll_read(); + + i = 0; + do { + SSR1 = 0; /* start receiving */ + while (!(SSR1 & SCI_RDRF)); /* wait for data */ + data = RDR1; /* read byte */ + } while ((data == 0xFF) && (++i < timeout)); + + return fliptable[(signed char)data]; +} + +/* returns 0 on timeout, timeout is in bytes */ +static unsigned char poll_busy(long timeout) +{ + long i; + unsigned char data, dummy; + + if (serial_mode != SER_POLL_READ) + set_sci1_poll_read(); + + /* get data response */ + SSR1 = 0; /* start receiving */ + while (!(SSR1 & SCI_RDRF)); /* wait for data */ + data = fliptable[(signed char)(RDR1)]; /* read byte */ + + /* wait until the card is ready again */ + i = 0; + do { + SSR1 = 0; /* start receiving */ + while (!(SSR1 & SCI_RDRF)); /* wait for data */ + dummy = RDR1; /* read byte */ + } while ((dummy != 0xFF) && (++i < timeout)); + + return (dummy == 0xFF) ? data : 0; +} + +/* Send MMC command and get response. Returns R1 byte directly. + * Returns further R2 or R3 bytes in *data (can be NULL for other commands) */ +static unsigned char send_cmd(int cmd, unsigned long parameter, void *data) +{ + static struct { + unsigned char cmd; + unsigned long parameter; + const unsigned char crc7; /* fixed, valid for CMD0 only */ + const unsigned char trailer; + } __attribute__((packed)) command = {0x40, 0, 0x95, 0xFF}; + + unsigned char ret; + + command.cmd = cmd; + command.parameter = htobe32(parameter); + + write_transfer((unsigned char *)&command, sizeof(command)); + + ret = poll_byte(20); + + switch (cmd) + { + case CMD_SEND_CSD: /* R1 response, leave open */ + case CMD_SEND_CID: + case CMD_READ_SINGLE_BLOCK: + case CMD_READ_MULTIPLE_BLOCK: + return ret; + + case CMD_SEND_STATUS: /* R2 response, close with dummy */ + read_transfer(data, 1); + break; + + case CMD_READ_OCR: /* R3 response, close with dummy */ + read_transfer(data, 4); + break; + + default: /* R1 response, close with dummy */ + break; /* also catches block writes */ + } + write_transfer(dummy, 1); + return ret; +} + +/* Receive CID/ CSD data (16 bytes) */ +static int receive_cxd(unsigned char *buf) +{ + if (poll_byte(20) != DT_START_BLOCK) + { + write_transfer(dummy, 1); + return -1; /* not start of data */ + } + + read_transfer(buf, 16); + write_transfer(dummy, 3); /* 2 bytes dontcare crc + 1 byte trailer */ + return 0; +} + + +static int initialize_card(int card_no) +{ + int rc, i; + int blk_exp, ts_exp, taac_exp; + tCardInfo *card = &card_info[card_no]; + + static const char mantissa[] = { /* *10 */ + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80 + }; + static const int exponent[] = { /* use varies */ + 1, 10, 100, 1000, 10000, 100000, 1000000, + 10000000, 100000000, 1000000000 + }; + + if (card_no == 1) + mmc_status = MMC_TOUCHED; + + /* switch to SPI mode */ + if (send_cmd(CMD_GO_IDLE_STATE, 0, NULL) != 0x01) + return -1; /* error or no response */ + + /* initialize card */ + for (i = HZ;;) /* try for 1 second*/ + { + sleep(1); + if (send_cmd(CMD_SEND_OP_COND, 0, NULL) == 0) + break; + if (--i <= 0) + return -2; /* timeout */ + } + + /* get OCR register */ + if (send_cmd(CMD_READ_OCR, 0, &card->ocr)) + return -3; + card->ocr = betoh32(card->ocr); /* no-op on big endian */ + + /* check voltage */ + if (!(card->ocr & 0x00100000)) /* 3.2 .. 3.3 V */ + return -4; + + /* get CSD register */ + if (send_cmd(CMD_SEND_CSD, 0, NULL)) + return -5; + rc = receive_cxd((unsigned char*)card->csd); + if (rc) + return rc * 10 - 5; + + blk_exp = card_extract_bits(card->csd, 83, 4); + if (blk_exp < 9) /* block size < 512 bytes not supported */ + return -6; + + card->numblocks = (card_extract_bits(card->csd, 73, 12) + 1) + << (card_extract_bits(card->csd, 49, 3) + 2 + blk_exp - 9); + card->blocksize = BLOCK_SIZE; + + /* max transmission speed, clock divider */ + ts_exp = card_extract_bits(card->csd, 98, 3); + ts_exp = (ts_exp > 3) ? 3 : ts_exp; + card->speed = mantissa[card_extract_bits(card->csd, 102, 4)] + * exponent[ts_exp + 4]; + card->bitrate_register = (FREQ/4-1) / card->speed; + + /* NSAC, TAAC, read timeout */ + card->nsac = 100 * card_extract_bits(card->csd, 111, 8); + card->taac = mantissa[card_extract_bits(card->csd, 118, 4)]; + taac_exp = card_extract_bits(card->csd, 114, 3); + card->read_timeout = ((FREQ/4) / (card->bitrate_register + 1) + * card->taac / exponent[9 - taac_exp] + + (10 * card->nsac)); + card->read_timeout /= 8; /* clocks -> bytes */ + card->taac = card->taac * exponent[taac_exp] / 10; + + /* r2w_factor, write timeout */ + card->r2w_factor = BIT_N(card_extract_bits(card->csd, 28, 3)); + card->write_timeout = card->read_timeout * card->r2w_factor; + + if (card->r2w_factor > 32) /* Such cards often need extra read delay */ + card->read_timeout *= 4; + + /* switch to full speed */ + setup_sci1(card->bitrate_register); + + /* always use 512 byte blocks */ + if (send_cmd(CMD_SET_BLOCKLEN, BLOCK_SIZE, NULL)) + return -7; + + /* get CID register */ + if (send_cmd(CMD_SEND_CID, 0, NULL)) + return -8; + rc = receive_cxd((unsigned char*)card->cid); + if (rc) + return rc * 10 - 8; + + card->initialized = true; + return 0; +} + +tCardInfo *mmc_card_info(int card_no) +{ + tCardInfo *card = &card_info[card_no]; + + if (!card->initialized && ((card_no == 0) || mmc_detect())) + { + select_card(card_no); + deselect_card(); + } + return card; +} + +/* Receive one block with DMA and bitswap it (chasing bitswap). */ +static int receive_block(unsigned char *inbuf, long timeout) +{ + unsigned long buf_end; + + if (poll_byte(timeout) != DT_START_BLOCK) + { + write_transfer(dummy, 1); + return -1; /* not start of data */ + } + + while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ + + SCR1 = 0; /* disable serial */ + SSR1 = 0; /* clear all flags */ + + /* setup DMA channel 0 */ + CHCR0 = 0; /* disable */ + SAR0 = RDR1_ADDR; + DAR0 = (unsigned long) inbuf; + DTCR0 = BLOCK_SIZE; + CHCR0 = 0x4601; /* fixed source address, RXI1, enable */ + DMAOR = 0x0001; + SCR1 = (SCI_RE|SCI_RIE); /* kick off DMA */ + + /* DMA receives 2 bytes more than DTCR2, but the last 2 bytes are not + * stored. The first extra byte is available from RDR1 after the DMA ends, + * the second one is lost because of the SCI overrun. However, this + * behaviour conveniently discards the crc. */ + + yield(); /* be nice */ + + /* Bitswap received data, chasing the DMA pointer */ + buf_end = (unsigned long)inbuf + BLOCK_SIZE; + do + { + /* Call bitswap whenever (a multiple of) 8 bytes are + * available (value optimised by experimentation). */ + int swap_now = (DAR0 - (unsigned long)inbuf) & ~0x00000007; + if (swap_now) + { + bitswap(inbuf, swap_now); + inbuf += swap_now; + } + } + while ((unsigned long)inbuf < buf_end); + + while (!(CHCR0 & 0x0002)); /* wait for end of DMA */ + while (!(SSR1 & SCI_ORER)); /* wait for the trailing bytes */ + SCR1 = 0; + serial_mode = SER_DISABLED; + + write_transfer(dummy, 1); /* send trailer */ + last_disk_activity = current_tick; + return 0; +} + +/* Prepare a block for sending by copying it to the next write buffer + * and bitswapping it. */ +static void send_block_prepare(void) +{ + unsigned char *dest; + + current_buffer ^= 1; /* toggle buffer */ + dest = write_buffer[current_buffer] + 2; + + memcpy(dest, send_block_addr, BLOCK_SIZE); + bitswap(dest, BLOCK_SIZE); + + send_block_addr += BLOCK_SIZE; +} + +/* Send one block with DMA from the current write buffer, possibly preparing + * the next block within the next write buffer in the background. */ +static int send_block_send(unsigned char start_token, long timeout, + bool prepare_next) +{ + int rc = 0; + unsigned char *curbuf = write_buffer[current_buffer]; + + curbuf[1] = fliptable[(signed char)start_token]; + *(unsigned short *)(curbuf + BLOCK_SIZE + 2) = 0xFFFF; + + while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ + + SCR1 = 0; /* disable serial */ + SSR1 = 0; /* clear all flags */ + + /* setup DMA channel 0 */ + CHCR0 = 0; /* disable */ + SAR0 = (unsigned long)(curbuf + 1); + DAR0 = TDR1_ADDR; + DTCR0 = BLOCK_SIZE + 3; /* start token + block + dummy crc */ + CHCR0 = 0x1701; /* fixed dest. address, TXI1, enable */ + DMAOR = 0x0001; + SCR1 = (SCI_TE|SCI_TIE); /* kick off DMA */ + + if (prepare_next) + send_block_prepare(); + yield(); /* be nice */ + + while (!(CHCR0 & 0x0002)); /* wait for end of DMA */ + while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ + SCR1 = 0; + serial_mode = SER_DISABLED; + + if ((poll_busy(timeout) & 0x1F) != 0x05) /* something went wrong */ + rc = -1; + + write_transfer(dummy, 1); + last_disk_activity = current_tick; + + return rc; +} + +int mmc_read_sectors(IF_MD2(int drive,) + unsigned long start, + int incount, + void* inbuf) +{ + int rc = 0; + int lastblock = 0; + unsigned long end_block; + tCardInfo *card; +#ifndef HAVE_MULTIDRIVE + int drive = current_card; +#endif + + card = &card_info[drive]; + rc = select_card(drive); + if (rc) + { + rc = rc * 10 - 1; + goto error; + } + + end_block = start + incount; + if (end_block > card->numblocks) + { + rc = -2; + goto error; + } + + /* Some cards don't like reading the very last block with + * CMD_READ_MULTIPLE_BLOCK, so make sure this block is always + * read with CMD_READ_SINGLE_BLOCK. */ + if (end_block == card->numblocks) + lastblock = 1; + + if (incount > 1) + { + /* MMC4.2: make multiplication conditional */ + if (send_cmd(CMD_READ_MULTIPLE_BLOCK, start * BLOCK_SIZE, NULL)) + { + rc = -3; + goto error; + } + while (--incount >= lastblock) + { + rc = receive_block(inbuf, card->read_timeout); + if (rc) + { + /* If an error occurs during multiple block reading, the + * host still needs to send CMD_STOP_TRANSMISSION */ + send_cmd(CMD_STOP_TRANSMISSION, 0, NULL); + rc = rc * 10 - 4; + goto error; + } + inbuf += BLOCK_SIZE; + start++; + /* ^^ necessary for the abovementioned last block special case */ + } + if (send_cmd(CMD_STOP_TRANSMISSION, 0, NULL)) + { + rc = -5; + goto error; + } + } + if (incount > 0) + { + /* MMC4.2: make multiplication conditional */ + if (send_cmd(CMD_READ_SINGLE_BLOCK, start * BLOCK_SIZE, NULL)) + { + rc = -6; + goto error; + } + rc = receive_block(inbuf, card->read_timeout); + if (rc) + { + rc = rc * 10 - 7; + goto error; + } + } + + error: + + deselect_card(); + + return rc; +} + +int mmc_write_sectors(IF_MD2(int drive,) + unsigned long start, + int count, + const void* buf) +{ + int rc = 0; + int write_cmd; + unsigned char start_token; + tCardInfo *card; +#ifndef HAVE_MULTIDRIVE + int drive = current_card; +#endif + + card = &card_info[drive]; + rc = select_card(drive); + if (rc) + { + rc = rc * 10 - 1; + goto error; + } + + if (start + count > card->numblocks) + panicf("Writing past end of card"); + + send_block_addr = buf; + send_block_prepare(); + + if (count > 1) + { + write_cmd = CMD_WRITE_MULTIPLE_BLOCK; + start_token = DT_START_WRITE_MULTIPLE; + } + else + { + write_cmd = CMD_WRITE_BLOCK; + start_token = DT_START_BLOCK; + } + /* MMC4.2: make multiplication conditional */ + if (send_cmd(write_cmd, start * BLOCK_SIZE, NULL)) + { + rc = -2; + goto error; + } + while (--count >= 0) + { + rc = send_block_send(start_token, card->write_timeout, count > 0); + if (rc) + { + rc = rc * 10 - 3; + break; + /* If an error occurs during multiple block writing, + * the STOP_TRAN token still needs to be sent. */ + } + } + if (write_cmd == CMD_WRITE_MULTIPLE_BLOCK) + { + static const unsigned char stop_tran = DT_STOP_TRAN; + write_transfer(&stop_tran, 1); + poll_busy(card->write_timeout); + } + + error: + + deselect_card(); + + return rc; +} + +bool mmc_disk_is_active(void) +{ + /* this is correct unless early return from write gets implemented */ + return mutex_test(&mmc_mutex); +} + +static void mmc_thread(void) +{ + struct queue_event ev; + bool idle_notified = false; + + while (1) { + queue_wait_w_tmo(&mmc_queue, &ev, HZ); + switch ( ev.id ) + { + case SYS_USB_CONNECTED: + usb_acknowledge(SYS_USB_CONNECTED_ACK); + /* Wait until the USB cable is extracted again */ + usb_wait_for_disconnect(&mmc_queue); + break; + +#ifdef HAVE_HOTSWAP + case SYS_HOTSWAP_INSERTED: + disk_mount(1); /* mount MMC */ + queue_broadcast(SYS_FS_CHANGED, 0); + break; + + case SYS_HOTSWAP_EXTRACTED: + disk_unmount(1); /* release "by force" */ + queue_broadcast(SYS_FS_CHANGED, 0); + break; +#endif + + default: + if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) + { + idle_notified = false; + } + else + { + if (!idle_notified) + { + call_storage_idle_notifys(false); + idle_notified = true; + } + } + break; + } + } +} + +bool mmc_detect(void) +{ + return (adc_read(ADC_MMC_SWITCH) < 0x200); +} + +bool mmc_touched(void) +{ + if (mmc_status == MMC_UNKNOWN) /* try to detect */ + { + mutex_lock(&mmc_mutex); + setup_sci1(7); /* safe value */ + and_b(~0x02, &PADRH); /* assert CS */ + if (send_cmd(CMD_SEND_OP_COND, 0, NULL) == 0xFF) + mmc_status = MMC_UNTOUCHED; + else + mmc_status = MMC_TOUCHED; + + deselect_card(); + } + return mmc_status == MMC_TOUCHED; +} + +bool mmc_usb_active(int delayticks) +{ + /* reading "inactive" is delayed by user-supplied monoflop value */ + return (usb_activity || + TIME_BEFORE(current_tick, last_usb_activity + delayticks)); +} + +static void mmc_tick(void) +{ + bool current_status; + + if (new_mmc_circuit) + /* USB bridge activity is 0 on idle, ~527 on active */ + current_status = adc_read(ADC_USB_ACTIVE) > 0x100; + else + current_status = adc_read(ADC_USB_ACTIVE) < 0x190; + + if (!current_status && usb_activity) + last_usb_activity = current_tick; + usb_activity = current_status; + + current_status = mmc_detect(); + /* Only report when the status has changed */ + if (current_status != last_mmc_status) + { + last_mmc_status = current_status; + countdown = HZ/3; + } + else + { + /* Count down until it gets negative */ + if (countdown >= 0) + countdown--; + + if (countdown == 0) + { + if (current_status) + { + queue_broadcast(SYS_HOTSWAP_INSERTED, 0); + } + else + { + queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0); + mmc_status = MMC_UNTOUCHED; + card_info[1].initialized = false; + } + } + } +} + +void mmc_enable(bool on) +{ + PBCR1 &= ~0x0CF0; /* PB13, PB11 and PB10 become GPIO, + * if not modified below */ + if (on) + PBCR1 |= 0x08A0; /* as SCK1, TxD1, RxD1 */ + + and_b(~0x80, &PADRL); /* assert flash reset */ + sleep(HZ/100); + or_b(0x80, &PADRL); /* de-assert flash reset */ + sleep(HZ/100); + card_info[0].initialized = false; + card_info[1].initialized = false; +} + +int mmc_init(void) +{ + int rc = 0; + + if (!initialized) + { + mutex_init(&mmc_mutex); + queue_init(&mmc_queue, true); + } + mutex_lock(&mmc_mutex); + led(false); + + last_mmc_status = mmc_detect(); +#ifndef HAVE_MULTIDRIVE + /* Use MMC if inserted, internal flash otherwise */ + current_card = last_mmc_status ? 1 : 0; +#endif + + if (!initialized) + { + if (!last_mmc_status) + mmc_status = MMC_UNTOUCHED; + + /* Port setup */ + PACR1 &= ~0x0F3C; /* GPIO function for PA13 (flash busy), PA12 + * (clk gate), PA10 (flash CS), PA9 (MMC CS) */ + PACR2 &= ~0x4000; /* GPIO for PA7 (flash reset) */ + PADR |= 0x0680; /* set all the selects + reset high (=inactive) */ + PAIOR |= 0x1680; /* make outputs for them and the PA12 clock gate */ + + PBCR1 &= ~0x0CF0; /* GPIO function for PB13, PB11 and PB10 */ + PBDR |= 0x2C00; /* SCK1, TxD1 and RxD1 high in GPIO */ + PBIOR |= 0x2000; /* SCK1 output */ + PBIOR &= ~0x0C00; /* TxD1, RxD1 input */ + + IPRE &= 0x0FFF; /* disable SCI1 interrupts for the CPU */ + + new_mmc_circuit = ((HW_MASK & MMC_CLOCK_POLARITY) != 0); + + create_thread(mmc_thread, mmc_stack, + sizeof(mmc_stack), 0, mmc_thread_name + IF_PRIO(, PRIORITY_SYSTEM) + IF_COP(, CPU)); + tick_add_task(mmc_tick); + initialized = true; + } + mmc_enable(true); + + mutex_unlock(&mmc_mutex); + return rc; +} + +long mmc_last_disk_activity(void) +{ + return last_disk_activity; +} + +#ifdef STORAGE_GET_INFO +void mmc_get_info(IF_MD2(int drive,) struct storage_info *info) +{ +#ifndef HAVE_MULTIDRIVE + const int drive=0; +#endif + info->sector_size=card_info[drive].blocksize; + info->num_sectors=card_info[drive].numblocks; + info->vendor="Rockbox"; + if(drive==0) + { + info->product="Internal Storage"; + } + else + { + info->product="MMC Card Slot"; + } + info->revision="0.00"; +} +#endif + +#ifdef HAVE_HOTSWAP +bool mmc_removable(IF_MD_NONVOID(int drive)) +{ +#ifndef HAVE_MULTIDRIVE + const int drive=0; +#endif + return (drive==1); +} + +bool mmc_present(IF_MD_NONVOID(int drive)) +{ +#ifndef HAVE_MULTIDRIVE + const int drive=0; +#endif + if(drive==0) + { + return true; + } + else + { + return mmc_detect(); + } +} +#endif + + +void mmc_sleep(void) +{ +} + +void mmc_spin(void) +{ +} + +void mmc_spindown(int seconds) +{ + (void)seconds; +} + +#ifdef CONFIG_STORAGE_MULTI +int mmc_num_drives(int first_drive) +{ + /* We don't care which logical drive number(s) we have been assigned */ + (void)first_drive; + +#ifdef HAVE_MULTIDRIVE + return 2; +#else + return 1; +#endif +} +#endif -- cgit v1.1