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/target/sh/archos/ondio/ata_mmc.c | 1027 +++++++++++++++++++++++++++++ 1 file changed, 1027 insertions(+) create mode 100644 firmware/target/sh/archos/ondio/ata_mmc.c (limited to 'firmware/target/sh') 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 0000000000..fc2efcd3e4 --- /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.2.3