From e7b025ba10b25bc50ad5a307bb2ec2480242a975 Mon Sep 17 00:00:00 2001 From: Daniel Ankers Date: Tue, 21 Nov 2006 22:55:39 +0000 Subject: Initial NAND driver for Sansa. This has had limited testing, and no testing on 6 or 8Gb models git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11567 a1c6a512-1295-4272-9138-f99709370657 --- firmware/target/arm/sandisk/sansa-e200/ata-e200.c | 624 ++++++++++++++++++--- .../target/arm/sandisk/sansa-e200/ata-target.h | 44 +- 2 files changed, 600 insertions(+), 68 deletions(-) (limited to 'firmware') diff --git a/firmware/target/arm/sandisk/sansa-e200/ata-e200.c b/firmware/target/arm/sandisk/sansa-e200/ata-e200.c index 0c37753c53..98d71c26ce 100644 --- a/firmware/target/arm/sandisk/sansa-e200/ata-e200.c +++ b/firmware/target/arm/sandisk/sansa-e200/ata-e200.c @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2006 Daniel Stenberg + * Copyright (C) 2006 Daniel Ankers * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. @@ -16,106 +16,461 @@ * KIND, either express or implied. * ****************************************************************************/ - +/* TODO: Add ATA Callback support */ +#include "lcd.h" #include "ata.h" +#include "ata-target.h" +#include "cpu.h" +#include "system.h" +#include #include #include +#include "kernel.h" +#include "thread.h" + +#define NOINLINE_ATTR __attribute__((noinline)) /* don't inline the loops */ +#define BLOCK_SIZE (512) #define SECTOR_SIZE (512) +#define STATUS_REG (*(volatile unsigned int *)(0x70008204)) +#define REG_1 (*(volatile unsigned int *)(0x70008208)) +#define UNKNOWN (*(volatile unsigned int *)(0x70008210)) +#define BLOCK_SIZE_REG (*(volatile unsigned int *)(0x7000821c)) +#define BLOCK_COUNT_REG (*(volatile unsigned int *)(0x70008220)) +#define REG_5 (*(volatile unsigned int *)(0x70008224)) +#define CMD_REG0 (*(volatile unsigned int *)(0x70008228)) +#define CMD_REG1 (*(volatile unsigned int *)(0x7000822c)) +#define CMD_REG2 (*(volatile unsigned int *)(0x70008230)) +#define RESPONSE_REG (*(volatile unsigned int *)(0x70008234)) +#define SD_STATE_REG (*(volatile unsigned int *)(0x70008238)) +#define REG_11 (*(volatile unsigned int *)(0x70008240)) +#define REG_12 (*(volatile unsigned int *)(0x70008244)) +#define DATA_REG (*(volatile unsigned int *)(0x70008280)) + +#define DATA_DONE (1 << 12) +#define CMD_DONE (1 << 13) +#define ERROR_BITS (0x3f) +#define FIFO_FULL (1 << 7) +#define FIFO_EMPTY (1 << 6) + +/* SD States */ +#define IDLE 0 +#define READY 1 +#define IDENT 2 +#define STBY 3 +#define TRAN 4 +#define DATA 5 +#define RCV 6 +#define PRG 7 +#define DIS 8 + +#define FIFO_SIZE 16 /* FIFO is 16 words deep */ + +/* SD Commands */ +#define GO_IDLE_STATE 0 +#define ALL_SEND_CID 2 +#define SEND_RELATIVE_ADDR 3 +#define SET_DSR 4 +#define SWITCH_FUNC 6 +#define SELECT_CARD 7 +#define DESELECT_CARD 7 +#define SEND_CSD 9 +#define SEND_CID 10 +#define STOP_TRANSMISSION 12 +#define SEND_STATUS 13 +#define GO_INACTIVE_STATE 15 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define READ_MULTIPLE_BLOCK 18 +#define WRITE_BLOCK 24 +#define WRITE_MULTIPLE_BLOCK 25 +#define ERASE_WR_BLK_START 32 +#define ERASE_WR_BLK_END 33 +#define ERASE 38 + +/* Application Specific commands */ +#define SET_BUS_WIDTH 6 +#define SD_APP_OP_COND 41 + +#define READ_TIMEOUT 5*HZ +#define WRITE_TIMEOUT 0.5*HZ + static unsigned short identify_info[SECTOR_SIZE]; int ata_spinup_time = 0; long last_disk_activity = -1; +static bool delayed_write = false; -void flash_select_chip(int no, int sel) -{ - -} +static unsigned char current_bank = 0; /* The bank that we are working with */ -unsigned char flash_read_data(void) -{ +static tSDCardInfo card_info[2]; -} +/* For multi volume support */ +static int current_card = 0; -void flash_write_data(unsigned char data) -{ +static struct mutex ata_mtx; -} +/* Private Functions */ -void flash_write_cmd(unsigned char cmd) +bool sd_send_command(unsigned int cmd, unsigned long arg1, unsigned int arg2) { - + bool result = false; + unsigned char cbuf[32]; + do + { + CMD_REG0 = cmd; + CMD_REG1 = (unsigned int)((arg1 & 0xffff0000) >> 16); + CMD_REG2 = (unsigned int)((arg1 & 0xffff)); + UNKNOWN = arg2; + while ((STATUS_REG & CMD_DONE) == 0) + { + /* Busy wait */ + } + if ((STATUS_REG & ERROR_BITS) == 0) + { + result = true; + } else { + snprintf(cbuf, sizeof(cbuf), "%x", (STATUS_REG & ERROR_BITS)); + lcd_puts(0,10,cbuf); + lcd_update(); + } + } while ((STATUS_REG & ERROR_BITS) != 0); + return result; } -void flash_write_addr(unsigned char addr) +void sd_read_response(unsigned int *response, int type) { - + int i; + int words; /* Number of 16 bit words to read from RESPONSE_REG */ + unsigned int response_from_card[9]; + if(type == 2) + { + words = 9; /* R2 types are 8.5 16-bit words long */ + } else { + words = 3; + } + + for (i = 0; i < words; i++) /* RESPONSE_REG is read MSB first */ + { + response_from_card[i] = RESPONSE_REG; /* Read most significant 16-bit word */ + } + + switch (type) + { + case 1: + /* Response type 1 has the following structure: + Start bit + Transmission bit + Command index (6 bits) + Card Status (32 bits) + CRC7 (7 bits) + Stop bit + */ + /* TODO: Sanity checks */ + response[0] = ((response_from_card[0] & 0xff) << 24) + + (response_from_card[1] << 8) + + ((response_from_card[2] & 0xff00) >> 8); + break; + case 2: + /* Response type 2 has the following structure: + Start bit + Transmission bit + Reserved (6 bits) + CSD/CID register (127 bits) + Stop bit + */ + response[3] = ((response_from_card[0]&0xff)<<24) + + (response_from_card[1]<<8) + + ((response_from_card[2]&0xff00)>>8); + response[2] = ((response_from_card[2]&0xff)<<24) + + (response_from_card[3]<<8) + + ((response_from_card[4]&0xff00)>>8); + response[1] = ((response_from_card[4]&0xff)<<24) + + (response_from_card[5]<<8) + + ((response_from_card[6]&0xff00)>>8); + response[0] = ((response_from_card[6]&0xff)<<24) + + (response_from_card[7]<<8) + + ((response_from_card[8]&0xff00)>>8); + break; + case 3: + /* Response type 3 has the following structure: + Start bit + Transmission bit + Reserved (6 bits) + OCR register (32 bits) + Reserved (7 bits) + Stop bit + */ + response[0] = ((response_from_card[0] & 0xff) << 24) + + (response_from_card[1] << 8) + + ((response_from_card[2] & 0xff00) >> 8); + /* Types 4-6 not supported yet */ + } } -void flash_wait_ready(void) +bool sd_send_acommand(unsigned int cmd, unsigned long arg1, unsigned int arg2) { - + unsigned int returncode; + if (sd_send_command(55, (card_info[current_card].rca)<<16, 1) == false) + return false; + sd_read_response(&returncode, 1); + if (sd_send_command(cmd, arg1, arg2) == false) + return false; + return true; } -int flash_map_sector(int sector, int* chip, int* chip_sector) +void sd_wait_for_state(tSDCardInfo* card, unsigned int state) { - + unsigned int response = 0; + while(((response >> 9) & 0xf) != state) + { + sd_send_command(SEND_STATUS, (card->rca) << 16, 1); + sd_read_response(&response, 1); + /* TODO: Add a timeout and error handling */ + } + SD_STATE_REG = state; } -int flash_read_id(int no) -{ -} +static void copy_read_sectors(unsigned char* buf, int wordcount) + NOINLINE_ATTR ICODE_ATTR; -int flash_read_sector(int sector, unsigned char* buf, - unsigned char* oob) +static void copy_read_sectors(unsigned char* buf, int wordcount) { - + unsigned int tmp = 0; + + if ( (unsigned long)buf & 1) + { /* not 16-bit aligned, copy byte by byte */ + unsigned char* bufend = buf + wordcount*2; + do + { + tmp = DATA_REG; + *buf++ = tmp & 0xff; + *buf++ = tmp >> 8; + } while (buf < bufend); /* tail loop is faster */ + } + else + { /* 16-bit aligned, can do faster copy */ + unsigned short* wbuf = (unsigned short*)buf; + unsigned short* wbufend = wbuf + wordcount; + do + { + *wbuf = DATA_REG; + } while (++wbuf < wbufend); /* tail loop is faster */ + } } -int flash_read_sector_oob(int sector, unsigned char* oob) -{ - -} - -static int flash_get_n_segments(void) -{ - -} +static void copy_write_sectors(const unsigned char* buf, int wordcount) + NOINLINE_ATTR ICODE_ATTR; -static int flash_get_n_phblocks(void) +static void copy_write_sectors(const unsigned char* buf, int wordcount) { - + if ( (unsigned long)buf & 1) + { /* not 16-bit aligned, copy byte by byte */ + unsigned short tmp = 0; + const unsigned char* bufend = buf + wordcount*2; + do + { + tmp = (unsigned short) *buf++; + tmp |= (unsigned short) *buf++ << 8; + DATA_REG = tmp; + } while (buf < bufend); /* tail loop is faster */ + } + else + { /* 16-bit aligned, can do faster copy */ + unsigned short* wbuf = (unsigned short*)buf; + unsigned short* wbufend = wbuf + wordcount; + do + { + lcd_update(); + DATA_REG = *wbuf; + } while (++wbuf < wbufend); /* tail loop is faster */ + } } -static int flash_get_n_sectors_in_block(void) -{ - -} -static int flash_phblock_to_sector(int segment, int block) +void sd_select_bank(unsigned char bank) { - + unsigned int response; + unsigned char card_data[512]; + unsigned char* write_buf; + int i; + tSDCardInfo *card = &card_info[0]; /* Bank selection will only be done on + the onboard flash */ + if (current_bank != bank) + { + memset(card_data, 0, 512); + sd_wait_for_state(card, TRAN); + BLOCK_SIZE_REG = 512; + BLOCK_COUNT_REG = 1; + sd_send_command(35, 0, 0x1c0d); /* CMD35 is vendor specific */ + sd_read_response(&response, 1); + SD_STATE_REG = PRG; + + card_data[0] = bank; + + /* Write the card data */ + write_buf = card_data; + for (i = 0; i < BLOCK_SIZE / 2; i += FIFO_SIZE) + { + /* Wait for the FIFO to be empty */ + while((STATUS_REG & FIFO_EMPTY) == 0) {} /* Erm... is this right? */ + + copy_write_sectors(write_buf, FIFO_SIZE); + + write_buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */ + } + + while((STATUS_REG & DATA_DONE) == 0) {} + current_bank = bank; + } } -static int flash_is_bad_block(unsigned char* oob) +void sd_init_device(void) { - +/* SD Protocol registers */ + unsigned int dummy; + int i; + + static unsigned int read_bl_len = 0; + static unsigned int c_size = 0; + static unsigned int c_size_mult = 0; + static unsigned long mult = 0; + + unsigned char carddata[512]; + unsigned char *dataptr; + tSDCardInfo *card = &card_info[0]; /* Init onboard flash only */ + +/* Initialise card data as blank */ + card->initialized = false; + card->ocr = 0; + card->csd[0] = 0; + card->csd[1] = 0; + card->csd[2] = 0; + card->cid[0] = 0; + card->cid[1] = 0; + card->cid[2] = 0; + card->rca = 0; + + card->capacity = 0; + card->numblocks = 0; + card->block_size = 0; + card->block_exp = 0; + +/* Enable and initialise controller */ + GPIOG_ENABLE |= (0x3 << 5); + GPIOG_OUTPUT_EN |= (0x3 << 5); + GPIOG_OUTPUT_VAL |= (0x3 << 5); + outl(inl(0x70000088) & ~(0x4), 0x70000088); + outl(inl(0x7000008c) & ~(0x4), 0x7000008c); + outl(inl(0x70000080) | 0x4, 0x70000080); + outl(inl(0x70000084) | 0x4, 0x70000084); + REG_1 = 6; + outl(inl(0x70000014) & ~(0x3ffff), 0x70000014); + outl((inl(0x70000014) & ~(0x3ffff)) | 0x255aa, 0x70000014); + outl(0x1010, 0x70000034); + + GPIOA_ENABLE |= (1 << 7); + GPIOA_OUTPUT_EN &= ~(1 << 7); + GPIOD_ENABLE |= (0x1f); + GPIOD_OUTPUT_EN |= (0x1f); + GPIOD_OUTPUT_VAL |= (0x1f); + outl(inl(0x6000600c) | (1 << 14), 0x6000600c); + outl(inl(0x60006004) | (1 << 14), 0x60006004); + outl(inl(0x60006004) & ~(1 << 14), 0x60006004); /* Reset Controller? */ + outl(0, 0x6000b000); + outl(0, 0x6000a000); /* Init DMA controller? */ + +/* Init NAND */ + REG_11 |= (1 << 15); + REG_12 |= (1 << 15); + REG_12 &= ~(3 << 12); + REG_12 |= (1 << 13); + REG_11 &= ~(3 << 12); + REG_11 |= (1 << 13); + + SD_STATE_REG = TRAN; + REG_5 = 0xf; + + sd_send_command(GO_IDLE_STATE, 0, 256); + while ((card->ocr & (1 << 31)) == 0) /* Loop until the card is powered up */ + { + sd_send_acommand(SD_APP_OP_COND, 0x100000, 3); + sd_read_response(&(card->ocr), 3); + + if (card->ocr == 0) + { + /* TODO: Handle failure */ + while (1) {}; + } + } + + sd_send_command(ALL_SEND_CID, 0, 2); + sd_read_response(card->cid, 2); + sd_send_command(SEND_RELATIVE_ADDR, 0, 1); + sd_read_response(&card->rca, 1); + card->rca >>= 16; /* The Relative Card Address is the top 16 bits of the + 32 bits returned. Whenever it is used, it gets + shifted left by 16 bits, so this step could possibly + be skipped. */ + + sd_send_command(SEND_CSD, card->rca << 16, 2); + sd_read_response(card->csd, 2); + + /* Parse disk geometry */ + /* These calculations come from the Sandisk SD card product manual */ + read_bl_len = ((card->csd[2] >> 16) & 0xf); + c_size = ((card->csd[2] & (0x3ff)) << 2) + + ((card->csd[1] & (0xc0000000)) >> 30); + c_size_mult = ((card->csd[1] >> 15) & 0x7); + mult = (1<<(c_size_mult + 2)); + card->max_read_bl_len = (1<block_size = BLOCK_SIZE; /* Always use 512 byte blocks */ + card->numblocks = (c_size + 1) * mult * (card->max_read_bl_len / 512); + card->capacity = card->numblocks * card->block_size; + + REG_1 = 0; + sd_send_command(SELECT_CARD, card->rca << 16, 129); + sd_read_response(&dummy, 1); /* I don't think we use the result from this */ + sd_send_acommand(SET_BUS_WIDTH, (card->rca << 16) | 2, 1); + sd_read_response(&dummy, 1); /* 4 bit wide bus */ + sd_send_command(SET_BLOCKLEN, card->block_size, 1); + sd_read_response(&dummy, 1); + BLOCK_SIZE_REG = card->block_size; + + /* If this card is > 4Gb, then we need to enable bank switching */ + if(card->numblocks > 0x7a77ff) + { + SD_STATE_REG = TRAN; + BLOCK_COUNT_REG = 1; + sd_send_command(SWITCH_FUNC, 0x80ffffef, 0x1c05); + sd_read_response(&dummy, 1); + /* Read 512 bytes from the card. + The first 512 bits contain the status information + TODO: Do something useful with this! */ + dataptr = carddata; + for (i = 0; i < BLOCK_SIZE / 2; i += FIFO_SIZE) + { + /* Wait for the FIFO to be full */ + while((STATUS_REG & FIFO_FULL) == 0) {} + + copy_read_sectors(dataptr, FIFO_SIZE); + + dataptr += (FIFO_SIZE*2); /* Advance one chunk of 16 words */ + } + } + mutex_init(&ata_mtx); } -int flash_disk_scan(void) -{ - -} +/* API Functions */ -int flash_disk_find_block(int block) +void ata_led(bool onoff) { - + (void)onoff; } -int flash_disk_read_sectors(unsigned long start, - int count, - void* buf) +/* write the delayed sector to volume 0 */ +extern void ata_flush(void) { } @@ -125,21 +480,161 @@ int ata_read_sectors(IF_MV2(int drive,) int incount, void* inbuf) { - + int ret = 0; + long timeout; + int count; + void* buf; + long spinup_start; + unsigned int dummy; + unsigned int response; + unsigned int i; + tSDCardInfo *card = &card_info[current_card]; + + /* TODO: Add DMA support. */ + +#ifdef HAVE_MULTIVOLUME + (void)drive; /* unused for now */ +#endif + mutex_lock(&ata_mtx); + + last_disk_activity = current_tick; + spinup_start = current_tick; + + ata_led(true); + + timeout = current_tick + READ_TIMEOUT; + + /* TODO: Select device */ + if(current_card == 0) + { + if(start > 0x7a77ff) + { + sd_select_bank(1); + start-=0x7a77ff; + } else { + sd_select_bank(0); + } + } + + buf = inbuf; + count = incount; + while (TIME_BEFORE(current_tick, timeout)) { + ret = 0; + last_disk_activity = current_tick; + + SD_STATE_REG = TRAN; + BLOCK_COUNT_REG = count; + sd_send_command(READ_MULTIPLE_BLOCK, start * BLOCK_SIZE, 0x1c25); + sd_read_response(&dummy, 1); + /* TODO: Don't assume BLOCK_SIZE == SECTOR_SIZE */ + + for (i = 0; i < count * card->block_size / 2; i += FIFO_SIZE) + { + /* Wait for the FIFO to be full */ + while((STATUS_REG & FIFO_FULL) == 0) {} + + copy_read_sectors(buf, FIFO_SIZE); + + buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */ + + /* TODO: Switch bank if necessary */ + + last_disk_activity = current_tick; + } + udelay(75); + sd_send_command(STOP_TRANSMISSION, 0, 1); + sd_read_response(&dummy, 1); + + response = 0; + sd_wait_for_state(card, TRAN); + break; + } + ata_led(false); + + mutex_unlock(&ata_mtx); + + /* only flush if reading went ok */ + if ( (ret == 0) && delayed_write ) + ata_flush(); + + return ret; } + int ata_write_sectors(IF_MV2(int drive,) unsigned long start, int count, const void* buf) { - (void)start; - (void)count; - (void)buf; - return -1; +/* Write support is not finished yet */ +/* TODO: The standard suggests using ACMD23 prior to writing multiple blocks + to improve performance */ + unsigned int response; + void const* write_buf; + int ret = 0; + unsigned int i; + long timeout; + tSDCardInfo *card = &card_info[current_card]; + + mutex_lock(&ata_mtx); + ata_led(true); + if(current_card == 0) + { + if(start <= 0x7a77ff) + { + sd_select_bank(0); + } else { + sd_select_bank(1); + start -= 0x7a77ff; + } + } + +retry: + sd_wait_for_state(card, TRAN); + BLOCK_COUNT_REG = count; + sd_send_command(WRITE_MULTIPLE_BLOCK, start * SECTOR_SIZE, 0x1c2d); + sd_read_response(&response, 1); + write_buf = buf; + for (i = 0; i < count * card->block_size / 2; i += FIFO_SIZE) + { + if(i >= (count * card->block_size / 2)-FIFO_SIZE) + { + /* Set SD_STATE_REG to PRG for the last buffer fill */ + SD_STATE_REG = PRG; + } + + /* Wait for the FIFO to be empty */ + while((STATUS_REG & FIFO_EMPTY) == 0) {} + /* Perhaps we could use bit 8 of card status (READY_FOR_DATA)? */ + + copy_write_sectors(write_buf, FIFO_SIZE); + + write_buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */ + /* TODO: Switch bank if necessary */ + + last_disk_activity = current_tick; + } + + timeout = current_tick + WRITE_TIMEOUT; + + while((STATUS_REG & DATA_DONE) == 0) { + if(current_tick >= timeout) + { + sd_send_command(STOP_TRANSMISSION, 0, 1); + sd_read_response(&response, 1); + goto retry; + } + } + sd_send_command(STOP_TRANSMISSION, 0, 1); + sd_read_response(&response, 1); + + sd_wait_for_state(card, TRAN); + mutex_unlock(&ata_mtx); + ata_led(false); + return ret; } -/* schedule a single sector write, executed with the the next spinup +/* schedule a single sector write, executed with the the next spinup (volume 0 only, used for config sector) */ extern void ata_delayed_write(unsigned long sector, const void* buf) { @@ -147,12 +642,6 @@ extern void ata_delayed_write(unsigned long sector, const void* buf) (void)buf; } -/* write the delayed sector to volume 0 */ -extern void ata_flush(void) -{ - -} - void ata_spindown(int seconds) { (void)seconds; @@ -189,10 +678,11 @@ void ata_enable(bool on) unsigned short* ata_get_identify(void) { - + return identify_info; } int ata_init(void) { + sd_init_device(); return 0; } diff --git a/firmware/target/arm/sandisk/sansa-e200/ata-target.h b/firmware/target/arm/sandisk/sansa-e200/ata-target.h index 67a01ed518..dfdd3fe6ae 100644 --- a/firmware/target/arm/sandisk/sansa-e200/ata-target.h +++ b/firmware/target/arm/sandisk/sansa-e200/ata-target.h @@ -1 +1,43 @@ -/* nothing here yet */ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 by Daniel Ankers + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef ATA_TARGET_H +#define ATA_TARGET_H + +#include "inttypes.h" + +typedef struct +{ + bool initialized; + + unsigned int ocr; /* OCR register */ + unsigned int csd[4]; /* CSD register */ + unsigned int cid[4]; /* CID register */ + unsigned int rca; + + uint64_t capacity; /* size in bytes */ + unsigned long numblocks; /* size in flash blocks */ + unsigned int block_size; /* block size in bytes */ + unsigned int max_read_bl_len;/* max read data block length */ + unsigned int block_exp; /* block size exponent */ +} tSDCardInfo; + +tSDCardInfo *sd_card_info(int card_no); +bool sd_touched(void); + +#endif -- cgit v1.2.3