Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 10 | * Copyright (C) 2004 by Jens Arnold |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 11 | * |
| 12 | * All files in this archive are subject to the GNU General Public License. |
| 13 | * See the file COPYING in the source tree root for full license agreement. |
| 14 | * |
| 15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 16 | * KIND, either express or implied. |
| 17 | * |
| 18 | ****************************************************************************/ |
| 19 | #include <stdbool.h> |
| 20 | #include "ata.h" |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 21 | #include "ata_mmc.h" |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 22 | #include "kernel.h" |
| 23 | #include "thread.h" |
| 24 | #include "led.h" |
| 25 | #include "sh7034.h" |
| 26 | #include "system.h" |
| 27 | #include "debug.h" |
| 28 | #include "panic.h" |
| 29 | #include "usb.h" |
| 30 | #include "power.h" |
| 31 | #include "string.h" |
| 32 | #include "hwcompat.h" |
Jörg Hohensohn | 00be746 | 2004-09-11 09:06:58 +0000 | [diff] [blame] | 33 | #include "adc.h" |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 34 | #include "bitswap.h" |
| 35 | |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 36 | #define SECTOR_SIZE 512 |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 37 | |
| 38 | /* Command definitions */ |
| 39 | #define CMD_GO_IDLE_STATE 0x40 /* R1 */ |
| 40 | #define CMD_SEND_OP_COND 0x41 /* R1 */ |
| 41 | #define CMD_SEND_CSD 0x49 /* R1 */ |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 42 | #define CMD_SEND_CID 0x4a /* R1 */ |
| 43 | #define CMD_STOP_TRANSMISSION 0x4c /* R1 */ |
| 44 | #define CMD_SEND_STATUS 0x4d /* R2 */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 45 | #define CMD_READ_SINGLE_BLOCK 0x51 /* R1 */ |
| 46 | #define CMD_READ_MULTIPLE_BLOCK 0x52 /* R1 */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 47 | #define CMD_WRITE_BLOCK 0x58 /* R1b */ |
| 48 | #define CMD_WRITE_MULTIPLE_BLOCK 0x59 /* R1b */ |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 49 | #define CMD_READ_OCR 0x7a /* R3 */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 50 | |
| 51 | /* Response formats: |
| 52 | R1 = single byte, msb=0, various error flags |
| 53 | R1b = R1 + busy token(s) |
| 54 | R2 = 2 bytes (1st byte identical to R1), additional flags |
| 55 | R3 = 5 bytes (R1 + OCR register) |
| 56 | */ |
| 57 | |
| 58 | #define R1_PARAMETER_ERR 0x40 |
| 59 | #define R1_ADDRESS_ERR 0x20 |
| 60 | #define R1_ERASE_SEQ_ERR 0x10 |
| 61 | #define R1_COM_CRC_ERR 0x08 |
| 62 | #define R1_ILLEGAL_CMD 0x04 |
| 63 | #define R1_ERASE_RESET 0x02 |
| 64 | #define R1_IN_IDLE_STATE 0x01 |
| 65 | |
| 66 | #define R2_OUT_OF_RANGE 0x80 |
| 67 | #define R2_ERASE_PARAM 0x40 |
| 68 | #define R2_WP_VIOLATION 0x20 |
| 69 | #define R2_CARD_ECC_FAIL 0x10 |
| 70 | #define R2_CC_ERROR 0x08 |
| 71 | #define R2_ERROR 0x04 |
| 72 | #define R2_ERASE_SKIP 0x02 |
| 73 | #define R2_CARD_LOCKED 0x01 |
| 74 | |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 75 | /* Data start tokens */ |
| 76 | |
| 77 | #define DT_START_BLOCK 0xfe |
| 78 | #define DT_START_WRITE_MULTIPLE 0xfc |
| 79 | #define DT_STOP_TRAN 0xfd |
| 80 | |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 81 | /* for compatibility */ |
| 82 | bool old_recorder = false; /* FIXME: get rid of this cross-dependency */ |
| 83 | int ata_spinup_time = 0; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 84 | char ata_device = 0; /* device 0 (master) or 1 (slave) */ |
| 85 | int ata_io_address = 0; /* 0x300 or 0x200, only valid on recorder */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 86 | long last_disk_activity = -1; |
| 87 | |
| 88 | /* private variables */ |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 89 | |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 90 | static struct mutex mmc_mutex; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 91 | |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 92 | static bool initialized = false; |
Jens Arnold | a15386b | 2004-10-04 22:29:06 +0000 | [diff] [blame] | 93 | static bool delayed_write = false; |
| 94 | static unsigned char delayed_sector[SECTOR_SIZE]; |
| 95 | static int delayed_sector_num; |
| 96 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 97 | static enum { |
| 98 | SER_POLL_WRITE, |
| 99 | SER_POLL_READ, |
| 100 | SER_DISABLED |
| 101 | } serial_mode; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 102 | |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 103 | static const unsigned char dummy[] = { |
| 104 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF |
| 105 | }; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 106 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 107 | /* 2 buffers for writing, include start token and dummy crc and an extra |
| 108 | * byte to keep word alignment */ |
| 109 | static unsigned char sector_buffer[2][(SECTOR_SIZE+4)]; |
| 110 | static int current_buffer = 0; |
| 111 | |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 112 | static tCardInfo card_info[2]; |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 113 | static int current_card = 0; |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 114 | static bool last_mmc_status = false; |
| 115 | static int countdown; /* for mmc switch debouncing */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 116 | |
| 117 | /* private function declarations */ |
| 118 | |
| 119 | static int select_card(int card_no); |
| 120 | static void deselect_card(void); |
| 121 | static void setup_sci1(int bitrate_register); |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 122 | static void set_sci1_poll_read(void); |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 123 | static void write_transfer(const unsigned char *buf, int len) |
| 124 | __attribute__ ((section(".icode"))); |
| 125 | static void read_transfer(unsigned char *buf, int len) |
| 126 | __attribute__ ((section(".icode"))); |
| 127 | static unsigned char poll_byte(int timeout); |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 128 | static unsigned char poll_busy(int timeout); |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 129 | static int send_cmd(int cmd, unsigned long parameter, unsigned char *response); |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 130 | static int receive_cxd(unsigned char *buf); |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 131 | static int initialize_card(int card_no); |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 132 | static int receive_sector(unsigned char *inbuf, unsigned char *swapbuf, |
| 133 | int timeout); |
| 134 | static void swapcopy_sector(const unsigned char *buf); |
| 135 | static int send_sector(const unsigned char *nextbuf, int timeout); |
| 136 | static int send_single_sector(const unsigned char *buf, int timeout); |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 137 | |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 138 | static void mmc_tick(void); |
| 139 | |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 140 | /* implementation */ |
| 141 | |
Jens Arnold | 5789ee9 | 2004-10-10 19:51:11 +0000 | [diff] [blame] | 142 | void mmc_select_clock(int card_no) |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 143 | { |
Jens Arnold | 212f18f | 2004-10-01 17:01:40 +0000 | [diff] [blame] | 144 | if (card_no == 0) /* internal */ |
| 145 | or_b(0x10, &PADRH); /* set clock gate PA12 CHECKME: mask? */ |
| 146 | else /* external */ |
| 147 | and_b(~0x10, &PADRH); /* clear clock gate PA12 CHECKME: mask?*/ |
Jens Arnold | 5789ee9 | 2004-10-10 19:51:11 +0000 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | static int select_card(int card_no) |
| 151 | { |
| 152 | mmc_select_clock(card_no); |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 153 | last_disk_activity = current_tick; |
Jens Arnold | 212f18f | 2004-10-01 17:01:40 +0000 | [diff] [blame] | 154 | |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 155 | if (!card_info[card_no].initialized) |
| 156 | { |
Jens Arnold | 6cb7991 | 2004-09-29 01:10:32 +0000 | [diff] [blame] | 157 | setup_sci1(7); /* Initial rate: 375 kbps (need <= 400 per mmc specs) */ |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 158 | write_transfer(dummy, 10); /* allow the card to synchronize */ |
| 159 | while (!(SSR1 & SCI_TEND)); |
| 160 | } |
| 161 | |
Jens Arnold | 212f18f | 2004-10-01 17:01:40 +0000 | [diff] [blame] | 162 | if (card_no == 0) /* internal */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 163 | and_b(~0x04, &PADRH); /* assert CS */ |
Jens Arnold | 212f18f | 2004-10-01 17:01:40 +0000 | [diff] [blame] | 164 | else /* external */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 165 | and_b(~0x02, &PADRH); /* assert CS */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 166 | |
| 167 | if (card_info[card_no].initialized) |
| 168 | { |
| 169 | setup_sci1(card_info[card_no].bitrate_register); |
| 170 | return 0; |
| 171 | } |
| 172 | else |
| 173 | { |
| 174 | return initialize_card(card_no); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | static void deselect_card(void) |
| 179 | { |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 180 | while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 181 | or_b(0x06, &PADRH); /* deassert CS (both cards) */ |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 182 | |
| 183 | last_disk_activity = current_tick; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 184 | } |
| 185 | |
| 186 | static void setup_sci1(int bitrate_register) |
| 187 | { |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 188 | while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 189 | |
| 190 | SCR1 = 0; /* disable serial port */ |
| 191 | SMR1 = SYNC_MODE; /* no prescale */ |
| 192 | BRR1 = bitrate_register; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 193 | SSR1 = 0; |
| 194 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 195 | SCR1 = SCI_TE; /* enable transmitter */ |
| 196 | serial_mode = SER_POLL_WRITE; |
| 197 | } |
| 198 | |
| 199 | static void set_sci1_poll_read(void) |
| 200 | { |
| 201 | while(!(SSR1 & SCI_TEND)); /* wait for end of transfer */ |
| 202 | SCR1 = 0; /* disable transmitter (& receiver) */ |
| 203 | SCR1 = (SCI_TE|SCI_RE); /* re-enable transmitter & receiver */ |
| 204 | while(!(SSR1 & SCI_TEND)); /* wait for SCI init completion (!) */ |
| 205 | serial_mode = SER_POLL_READ; |
| 206 | TDR1 = 0xFF; /* send do-nothing while reading */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | static void write_transfer(const unsigned char *buf, int len) |
| 210 | { |
| 211 | const unsigned char *buf_end = buf + len; |
Jens Arnold | 3681308 | 2004-10-04 17:53:53 +0000 | [diff] [blame] | 212 | register unsigned char data; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 213 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 214 | if (serial_mode != SER_POLL_WRITE) |
| 215 | { |
| 216 | while(!(SSR1 & SCI_TEND)); /* wait for end of transfer */ |
| 217 | SCR1 = 0; /* disable transmitter & receiver */ |
| 218 | SSR1 = 0; /* clear all flags */ |
| 219 | SCR1 = SCI_TE; /* enable transmitter only */ |
| 220 | serial_mode = SER_POLL_WRITE; |
| 221 | } |
| 222 | |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 223 | while (buf < buf_end) |
| 224 | { |
Jens Arnold | 3681308 | 2004-10-04 17:53:53 +0000 | [diff] [blame] | 225 | data = fliptable[(signed char)(*buf++)]; /* bitswap */ |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 226 | while (!(SSR1 & SCI_TDRE)); /* wait for end of transfer */ |
Jens Arnold | 3681308 | 2004-10-04 17:53:53 +0000 | [diff] [blame] | 227 | TDR1 = data; /* write byte */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 228 | SSR1 = 0; /* start transmitting */ |
| 229 | } |
| 230 | } |
| 231 | |
Jens Arnold | 3681308 | 2004-10-04 17:53:53 +0000 | [diff] [blame] | 232 | /* don't call this with len == 0 */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 233 | static void read_transfer(unsigned char *buf, int len) |
| 234 | { |
Jens Arnold | 3681308 | 2004-10-04 17:53:53 +0000 | [diff] [blame] | 235 | unsigned char *buf_end = buf + len - 1; |
| 236 | register signed char data; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 237 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 238 | if (serial_mode != SER_POLL_READ) |
| 239 | set_sci1_poll_read(); |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 240 | |
Jens Arnold | 3681308 | 2004-10-04 17:53:53 +0000 | [diff] [blame] | 241 | SSR1 = 0; /* start receiving first byte */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 242 | while (buf < buf_end) |
| 243 | { |
Jens Arnold | 3681308 | 2004-10-04 17:53:53 +0000 | [diff] [blame] | 244 | while (!(SSR1 & SCI_RDRF)); /* wait for data */ |
| 245 | data = RDR1; /* read byte */ |
| 246 | SSR1 = 0; /* start receiving */ |
| 247 | *buf++ = fliptable[data]; /* bitswap */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 248 | } |
Jens Arnold | 3681308 | 2004-10-04 17:53:53 +0000 | [diff] [blame] | 249 | while (!(SSR1 & SCI_RDRF)); /* wait for last byte */ |
| 250 | *buf = fliptable[(signed char)(RDR1)]; /* read & bitswap */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 251 | } |
| 252 | |
Jens Arnold | 0660105 | 2004-10-09 22:48:10 +0000 | [diff] [blame] | 253 | /* returns 0xFF on timeout, timeout is in bytes */ |
| 254 | static unsigned char poll_byte(int timeout) |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 255 | { |
| 256 | int i; |
| 257 | unsigned char data = 0; /* stop the compiler complaining */ |
| 258 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 259 | if (serial_mode != SER_POLL_READ) |
| 260 | set_sci1_poll_read(); |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 261 | |
| 262 | i = 0; |
| 263 | do { |
| 264 | SSR1 = 0; /* start receiving */ |
| 265 | while (!(SSR1 & SCI_RDRF)); /* wait for data */ |
| 266 | data = RDR1; /* read byte */ |
| 267 | } while ((data == 0xFF) && (++i < timeout)); |
| 268 | |
| 269 | return fliptable[(signed char)data]; |
| 270 | } |
| 271 | |
Jens Arnold | 0660105 | 2004-10-09 22:48:10 +0000 | [diff] [blame] | 272 | /* returns 0 on timeout, timeout is in bytes */ |
| 273 | static unsigned char poll_busy(int timeout) |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 274 | { |
| 275 | int i; |
Jens Arnold | 7d8598f | 2004-09-29 22:44:02 +0000 | [diff] [blame] | 276 | unsigned char data, dummy; |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 277 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 278 | if (serial_mode != SER_POLL_READ) |
| 279 | set_sci1_poll_read(); |
Jens Arnold | 7d8598f | 2004-09-29 22:44:02 +0000 | [diff] [blame] | 280 | |
| 281 | /* get data response */ |
| 282 | SSR1 = 0; /* start receiving */ |
| 283 | while (!(SSR1 & SCI_RDRF)); /* wait for data */ |
Jens Arnold | a15386b | 2004-10-04 22:29:06 +0000 | [diff] [blame] | 284 | data = fliptable[(signed char)(RDR1)]; /* read byte */ |
Jens Arnold | 7d8598f | 2004-09-29 22:44:02 +0000 | [diff] [blame] | 285 | |
| 286 | /* wait until the card is ready again */ |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 287 | i = 0; |
| 288 | do { |
| 289 | SSR1 = 0; /* start receiving */ |
| 290 | while (!(SSR1 & SCI_RDRF)); /* wait for data */ |
Jens Arnold | 7d8598f | 2004-09-29 22:44:02 +0000 | [diff] [blame] | 291 | dummy = RDR1; /* read byte */ |
| 292 | } while ((dummy != 0xFF) && (++i < timeout)); |
| 293 | |
Jens Arnold | 0660105 | 2004-10-09 22:48:10 +0000 | [diff] [blame] | 294 | return (dummy == 0xFF) ? data : 0; |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 295 | } |
| 296 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 297 | /* Send MMC command and get response */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 298 | static int send_cmd(int cmd, unsigned long parameter, unsigned char *response) |
| 299 | { |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 300 | unsigned char command[] = {0x40, 0x00, 0x00, 0x00, 0x00, 0x95, 0xFF}; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 301 | |
| 302 | command[0] = cmd; |
| 303 | |
| 304 | if (parameter != 0) |
| 305 | { |
| 306 | command[1] = (parameter >> 24) & 0xFF; |
| 307 | command[2] = (parameter >> 16) & 0xFF; |
| 308 | command[3] = (parameter >> 8) & 0xFF; |
| 309 | command[4] = parameter & 0xFF; |
| 310 | } |
| 311 | |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 312 | write_transfer(command, 7); |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 313 | |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 314 | response[0] = poll_byte(20); |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 315 | |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 316 | if (response[0] != 0x00) |
| 317 | { |
| 318 | write_transfer(dummy, 1); |
| 319 | return -1; |
| 320 | } |
| 321 | |
| 322 | switch (cmd) |
| 323 | { |
| 324 | case CMD_SEND_CSD: /* R1 response, leave open */ |
| 325 | case CMD_SEND_CID: |
| 326 | case CMD_READ_SINGLE_BLOCK: |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 327 | case CMD_READ_MULTIPLE_BLOCK: |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 328 | break; |
| 329 | |
| 330 | case CMD_SEND_STATUS: /* R2 response, close with dummy */ |
| 331 | read_transfer(response + 1, 1); |
| 332 | write_transfer(dummy, 1); |
| 333 | break; |
| 334 | |
| 335 | case CMD_READ_OCR: /* R3 response, close with dummy */ |
| 336 | read_transfer(response + 1, 4); |
| 337 | write_transfer(dummy, 1); |
| 338 | break; |
| 339 | |
| 340 | default: /* R1 response, close with dummy */ |
| 341 | write_transfer(dummy, 1); |
| 342 | break; /* also catches block writes */ |
| 343 | } |
| 344 | |
| 345 | return 0; |
| 346 | } |
| 347 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 348 | /* Receive CID/ CSD data (16 bytes) */ |
| 349 | static int receive_cxd(unsigned char *buf) |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 350 | { |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 351 | if (poll_byte(20) != DT_START_BLOCK) |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 352 | { |
| 353 | write_transfer(dummy, 1); |
| 354 | return -1; /* not start of data */ |
| 355 | } |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 356 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 357 | read_transfer(buf, 16); |
| 358 | write_transfer(dummy, 3); /* 2 bytes dontcare crc + 1 byte trailer */ |
| 359 | return 0; |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 360 | } |
| 361 | |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 362 | /* helper function to extract n (<=32) bits from an arbitrary position. |
| 363 | counting from MSB to LSB */ |
| 364 | unsigned long mmc_extract_bits( |
| 365 | const unsigned long *p, /* the start of the bitfield array */ |
| 366 | unsigned int start, /* bit no. to start reading */ |
| 367 | unsigned int size) /* how many bits to read */ |
| 368 | { |
| 369 | unsigned int bit_index; |
| 370 | unsigned int bits_to_use; |
| 371 | unsigned long mask; |
| 372 | unsigned long result; |
| 373 | |
| 374 | if (size == 1) |
| 375 | { /* short cut */ |
| 376 | return ((p[start/32] >> (31 - (start % 32))) & 1); |
| 377 | } |
| 378 | |
| 379 | result = 0; |
| 380 | while (size) |
| 381 | { |
| 382 | bit_index = start % 32; |
| 383 | bits_to_use = MIN(32 - bit_index, size); |
| 384 | mask = 0xFFFFFFFF >> (32 - bits_to_use); |
| 385 | |
| 386 | result <<= bits_to_use; /* start last round */ |
| 387 | result |= (p[start/32] >> (32 - bits_to_use - bit_index)) & mask; |
| 388 | |
| 389 | start += bits_to_use; |
| 390 | size -= bits_to_use; |
| 391 | } |
| 392 | |
| 393 | return result; |
| 394 | } |
| 395 | |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 396 | static int initialize_card(int card_no) |
| 397 | { |
| 398 | int i, temp; |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 399 | unsigned char response[5]; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 400 | tCardInfo *card = &card_info[card_no]; |
| 401 | |
| 402 | static const char mantissa[] = { /* *10 */ |
| 403 | 0, 10, 12, 13, 15, 20, 25, 30, |
| 404 | 35, 40, 45, 50, 55, 60, 70, 80 |
| 405 | }; |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 406 | static const int exponent[] = { /* use varies */ |
| 407 | 1, 10, 100, 1000, 10000, 100000, 1000000, |
| 408 | 10000000, 100000000, 1000000000 |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 409 | }; |
| 410 | |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 411 | /* switch to SPI mode */ |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 412 | send_cmd(CMD_GO_IDLE_STATE, 0, response); |
| 413 | if (response[0] != 0x01) |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 414 | return -1; /* error response */ |
| 415 | |
| 416 | /* initialize card */ |
| 417 | i = 0; |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 418 | while (send_cmd(CMD_SEND_OP_COND, 0, response) && (++i < 200)); |
| 419 | if (response[0] != 0x00) |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 420 | return -2; /* not ready */ |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 421 | |
| 422 | /* get OCR register */ |
| 423 | if (send_cmd(CMD_READ_OCR, 0, response)) |
| 424 | return -3; |
| 425 | card->ocr = (response[1] << 24) + (response[2] << 16) |
| 426 | + (response[3] << 8) + response[4]; |
| 427 | |
| 428 | /* check voltage */ |
| 429 | if (!(card->ocr & 0x00100000)) /* 3.2 .. 3.3 V */ |
| 430 | return -4; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 431 | |
| 432 | /* get CSD register */ |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 433 | if (send_cmd(CMD_SEND_CSD, 0, response)) |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 434 | return -5; |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 435 | if (receive_cxd((unsigned char*)card->csd)) |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 436 | return -6; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 437 | |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 438 | /* check block size */ |
| 439 | if ((1 << mmc_extract_bits(card->csd, 44, 4)) != SECTOR_SIZE) |
| 440 | return -7; |
| 441 | |
| 442 | /* max transmission speed, clock divider */ |
| 443 | temp = mmc_extract_bits(card->csd, 29, 3); |
| 444 | temp = (temp > 3) ? 3 : temp; |
| 445 | card->speed = mantissa[mmc_extract_bits(card->csd, 25, 4)] |
| 446 | * exponent[temp + 4]; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 447 | card->bitrate_register = (FREQ/4-1) / card->speed; |
| 448 | |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 449 | /* NSAC, TSAC, read timeout */ |
| 450 | card->nsac = 100 * mmc_extract_bits(card->csd, 16, 8); |
| 451 | card->tsac = mantissa[mmc_extract_bits(card->csd, 9, 4)]; |
| 452 | temp = mmc_extract_bits(card->csd, 13, 3); |
| 453 | card->read_timeout = ((FREQ/4) / (card->bitrate_register + 1) |
| 454 | * card->tsac / exponent[9 - temp] |
| 455 | + (10 * card->nsac)); |
| 456 | card->read_timeout /= 8; /* clocks -> bytes */ |
| 457 | card->tsac *= exponent[temp]; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 458 | |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 459 | /* r2w_factor, write timeout */ |
| 460 | temp = mmc_extract_bits(card->csd, 99, 3); |
| 461 | temp = (temp > 5) ? 5 : temp; |
| 462 | card->r2w_factor = 1 << temp; |
| 463 | card->write_timeout = card->read_timeout * card->r2w_factor; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 464 | |
| 465 | /* switch to full speed */ |
| 466 | setup_sci1(card->bitrate_register); |
| 467 | |
| 468 | /* get CID register */ |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 469 | if (send_cmd(CMD_SEND_CID, 0, response)) |
| 470 | return -8; |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 471 | if (receive_cxd((unsigned char*)card->cid)) |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 472 | return -9; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 473 | |
| 474 | card->initialized = true; |
| 475 | return 0; |
| 476 | } |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 477 | |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 478 | tCardInfo *mmc_card_info(int card_no) |
| 479 | { |
| 480 | tCardInfo *card = &card_info[card_no]; |
| 481 | |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 482 | if (!card->initialized && ((card_no == 0) || mmc_detect())) |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 483 | { |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 484 | mutex_lock(&mmc_mutex); |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 485 | select_card(card_no); |
| 486 | deselect_card(); |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 487 | mutex_unlock(&mmc_mutex); |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 488 | } |
| 489 | return card; |
| 490 | } |
| 491 | |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 492 | /* Receive one sector with dma, possibly swapping the previously received |
| 493 | * sector in the background */ |
| 494 | static int receive_sector(unsigned char *inbuf, unsigned char *swapbuf, |
| 495 | int timeout) |
| 496 | { |
| 497 | if (poll_byte(timeout) != DT_START_BLOCK) |
| 498 | { |
| 499 | write_transfer(dummy, 1); |
| 500 | return -1; /* not start of data */ |
| 501 | } |
| 502 | |
| 503 | while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ |
| 504 | |
| 505 | SCR1 = 0; /* disable serial */ |
| 506 | SSR1 = 0; /* clear all flags */ |
| 507 | |
| 508 | /* setup DMA channel 2 */ |
| 509 | CHCR2 = 0; /* disable */ |
| 510 | SAR2 = RDR1_ADDR; |
| 511 | DAR2 = (unsigned long) inbuf; |
| 512 | DTCR2 = SECTOR_SIZE; |
| 513 | CHCR2 = 0x4601; /* fixed source address, RXI1, enable */ |
| 514 | DMAOR = 0x0001; |
| 515 | SCR1 = (SCI_RE|SCI_RIE); /* kick off DMA */ |
| 516 | |
| 517 | /* dma receives 2 bytes more than DTCR2, but the last 2 bytes are not |
| 518 | * stored. The first extra byte is available from RDR1 after the DMA ends, |
| 519 | * the second one is lost because of the SCI overrun. However, this |
| 520 | * behaviour conveniently discards the crc. */ |
| 521 | |
| 522 | if (swapbuf != NULL) /* bitswap previous sector */ |
| 523 | bitswap(swapbuf, SECTOR_SIZE); |
| 524 | yield(); /* be nice */ |
| 525 | |
| 526 | while (!(CHCR2 & 0x0002)); /* wait for end of DMA */ |
| 527 | while (!(SSR1 & SCI_ORER)); /* wait for the trailing bytes */ |
| 528 | SCR1 = 0; |
| 529 | serial_mode = SER_DISABLED; |
| 530 | |
| 531 | write_transfer(dummy, 1); /* send trailer */ |
| 532 | return 0; |
| 533 | } |
| 534 | |
| 535 | /* copies one sector into the next-current write buffer, then bitswaps */ |
| 536 | static void swapcopy_sector(const unsigned char *buf) |
| 537 | { |
| 538 | unsigned char *curbuf; |
| 539 | |
| 540 | current_buffer ^= 1; /* toggles between 0 and 1 */ |
| 541 | |
| 542 | curbuf = sector_buffer[current_buffer]; |
| 543 | curbuf[1] = DT_START_WRITE_MULTIPLE; |
| 544 | curbuf[(SECTOR_SIZE+2)] = curbuf[(SECTOR_SIZE+3)] = 0xFF; /* dummy crc */ |
| 545 | memcpy(curbuf + 2, buf, SECTOR_SIZE); |
| 546 | bitswap(curbuf + 1, (SECTOR_SIZE+1)); |
| 547 | } |
| 548 | |
| 549 | /* Send one sector with dma from the current sector buffer, possibly preparing |
| 550 | * the next sector within the other sector buffer in the background. Use |
| 551 | * for multisector transfer only */ |
| 552 | static int send_sector(const unsigned char *nextbuf, int timeout) |
| 553 | { |
| 554 | int ret = 0; |
| 555 | |
| 556 | while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ |
| 557 | |
| 558 | SCR1 = 0; /* disable serial */ |
| 559 | SSR1 = 0; /* clear all flags */ |
| 560 | |
| 561 | /* setup DMA channel 2 */ |
| 562 | CHCR2 = 0; /* disable */ |
| 563 | SAR2 = (unsigned long)(sector_buffer[current_buffer] + 1); |
| 564 | DAR2 = TDR1_ADDR; |
| 565 | DTCR2 = (SECTOR_SIZE+3); |
| 566 | CHCR2 = 0x1701; /* fixed dest. address, TXI1, enable */ |
| 567 | DMAOR = 0x0001; |
| 568 | SCR1 = (SCI_TE|SCI_TIE); /* kick off DMA */ |
| 569 | |
| 570 | if (nextbuf != NULL) /* prepare next sector */ |
| 571 | swapcopy_sector(nextbuf); |
| 572 | yield(); /* be nice */ |
| 573 | |
| 574 | while (!(CHCR2 & 0x0002)); /* wait for end of DMA */ |
| 575 | while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ |
| 576 | SCR1 = 0; |
| 577 | serial_mode = SER_DISABLED; |
| 578 | |
| 579 | if ((poll_busy(timeout) & 0x1F) != 0x05) /* something went wrong */ |
| 580 | ret = -1; |
| 581 | |
| 582 | write_transfer(dummy, 1); |
| 583 | |
| 584 | return ret; |
| 585 | } |
| 586 | |
| 587 | /* Send one sector with polled i/o. Use for single sector transfers only. */ |
| 588 | static int send_single_sector(const unsigned char *buf, int timeout) |
| 589 | { |
| 590 | int ret = 0; |
| 591 | unsigned char start_token = DT_START_BLOCK; |
| 592 | |
| 593 | write_transfer(&start_token, 1); |
| 594 | write_transfer(buf, SECTOR_SIZE); |
| 595 | write_transfer(dummy, 2); /* crc - dontcare */ |
| 596 | |
| 597 | if ((poll_busy(timeout) & 0x1F) != 0x05) /* something went wrong */ |
| 598 | ret = -1; |
| 599 | |
| 600 | write_transfer(dummy, 1); |
| 601 | |
| 602 | return ret; |
| 603 | } |
| 604 | |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 605 | int ata_read_sectors(unsigned long start, |
| 606 | int incount, |
| 607 | void* inbuf) |
| 608 | { |
| 609 | int ret = 0; |
| 610 | int i; |
| 611 | unsigned long addr; |
| 612 | unsigned char response; |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 613 | void *inbuf_prev = NULL; |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 614 | tCardInfo *card = &card_info[current_card]; |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 615 | |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 616 | addr = start * SECTOR_SIZE; |
| 617 | |
| 618 | mutex_lock(&mmc_mutex); |
| 619 | ret = select_card(current_card); |
| 620 | |
| 621 | if (ret == 0) |
| 622 | { |
| 623 | if (incount == 1) |
| 624 | { |
| 625 | ret = send_cmd(CMD_READ_SINGLE_BLOCK, addr, &response); |
| 626 | if (ret == 0) |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 627 | { |
| 628 | ret = receive_sector(inbuf, inbuf_prev, card->read_timeout); |
| 629 | inbuf_prev = inbuf; |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 630 | last_disk_activity = current_tick; |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 631 | } |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 632 | } |
| 633 | else |
| 634 | { |
| 635 | ret = send_cmd(CMD_READ_MULTIPLE_BLOCK, addr, &response); |
| 636 | for (i = 0; (i < incount) && (ret == 0); i++) |
| 637 | { |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 638 | ret = receive_sector(inbuf, inbuf_prev, card->read_timeout); |
| 639 | inbuf_prev = inbuf; |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 640 | inbuf += SECTOR_SIZE; |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 641 | last_disk_activity = current_tick; |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 642 | } |
| 643 | if (ret == 0) |
| 644 | ret = send_cmd(CMD_STOP_TRANSMISSION, 0, &response); |
| 645 | } |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 646 | if (ret == 0) |
| 647 | bitswap(inbuf_prev, SECTOR_SIZE); |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 648 | } |
| 649 | |
| 650 | deselect_card(); |
| 651 | mutex_unlock(&mmc_mutex); |
| 652 | |
Jens Arnold | a15386b | 2004-10-04 22:29:06 +0000 | [diff] [blame] | 653 | /* only flush if reading went ok */ |
| 654 | if ( (ret == 0) && delayed_write ) |
| 655 | ata_flush(); |
| 656 | |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 657 | return ret; |
| 658 | } |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 659 | |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 660 | int ata_write_sectors(unsigned long start, |
| 661 | int count, |
| 662 | const void* buf) |
| 663 | { |
| 664 | int ret = 0; |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 665 | int i; |
| 666 | unsigned long addr; |
| 667 | unsigned char response; |
| 668 | tCardInfo *card = &card_info[current_card]; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 669 | |
| 670 | if (start == 0) |
| 671 | panicf("Writing on sector 0\n"); |
| 672 | |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 673 | addr = start * SECTOR_SIZE; |
| 674 | |
| 675 | mutex_lock(&mmc_mutex); |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 676 | ret = select_card(current_card); |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 677 | |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 678 | if (ret == 0) |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 679 | { |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 680 | if (count == 1) |
| 681 | { |
| 682 | ret = send_cmd(CMD_WRITE_BLOCK, addr, &response); |
| 683 | if (ret == 0) |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 684 | ret = send_single_sector(buf, card->write_timeout); |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 685 | last_disk_activity = current_tick; |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 686 | } |
| 687 | else |
| 688 | { |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 689 | swapcopy_sector(buf); /* prepare first sector */ |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 690 | ret = send_cmd(CMD_WRITE_MULTIPLE_BLOCK, addr, &response); |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 691 | for (i = 1; (i < count) && (ret == 0); i++) |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 692 | { |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 693 | buf += SECTOR_SIZE; |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 694 | ret = send_sector(buf, card->write_timeout); |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 695 | last_disk_activity = current_tick; |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 696 | } |
| 697 | if (ret == 0) |
| 698 | { |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 699 | ret = send_sector(NULL, card->write_timeout); |
| 700 | if (ret == 0) |
| 701 | { |
| 702 | response = DT_STOP_TRAN; |
| 703 | write_transfer(&response, 1); |
| 704 | poll_busy(card->write_timeout); |
| 705 | } |
Jens Arnold | 6f9a7eb | 2004-10-06 20:43:12 +0000 | [diff] [blame] | 706 | last_disk_activity = current_tick; |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 707 | } |
| 708 | } |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 709 | } |
| 710 | |
| 711 | deselect_card(); |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 712 | mutex_unlock(&mmc_mutex); |
| 713 | |
Jens Arnold | a15386b | 2004-10-04 22:29:06 +0000 | [diff] [blame] | 714 | /* only flush if writing went ok */ |
| 715 | if ( (ret == 0) && delayed_write ) |
| 716 | ata_flush(); |
| 717 | |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 718 | return ret; |
| 719 | } |
| 720 | |
Jens Arnold | a15386b | 2004-10-04 22:29:06 +0000 | [diff] [blame] | 721 | /* While there is no spinup, the delayed write is still here to avoid |
| 722 | wearing the flash unnecessarily */ |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 723 | extern void ata_delayed_write(unsigned long sector, const void* buf) |
| 724 | { |
Jens Arnold | a15386b | 2004-10-04 22:29:06 +0000 | [diff] [blame] | 725 | memcpy(delayed_sector, buf, SECTOR_SIZE); |
| 726 | delayed_sector_num = sector; |
| 727 | delayed_write = true; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 728 | } |
| 729 | |
| 730 | extern void ata_flush(void) |
| 731 | { |
Jens Arnold | a15386b | 2004-10-04 22:29:06 +0000 | [diff] [blame] | 732 | if ( delayed_write ) { |
| 733 | DEBUGF("ata_flush()\n"); |
| 734 | delayed_write = false; |
| 735 | ata_write_sectors(delayed_sector_num, 1, delayed_sector); |
| 736 | } |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 737 | } |
| 738 | |
| 739 | void ata_spindown(int seconds) |
| 740 | { |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 741 | (void)seconds; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 742 | } |
| 743 | |
| 744 | bool ata_disk_is_active(void) |
Jens Arnold | 0660105 | 2004-10-09 22:48:10 +0000 | [diff] [blame] | 745 | { |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 746 | /* this is correct unless early return from write gets implemented */ |
| 747 | return mmc_mutex.locked; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 748 | } |
| 749 | |
| 750 | int ata_standby(int time) |
| 751 | { |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 752 | (void)time; |
| 753 | |
Jens Arnold | fc9aada | 2004-10-01 21:41:44 +0000 | [diff] [blame] | 754 | return 0; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 755 | } |
| 756 | |
| 757 | int ata_sleep(void) |
| 758 | { |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 759 | return 0; |
| 760 | } |
| 761 | |
| 762 | void ata_spin(void) |
| 763 | { |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 764 | } |
| 765 | |
Jens Arnold | 5789ee9 | 2004-10-10 19:51:11 +0000 | [diff] [blame] | 766 | bool mmc_detect(void) |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 767 | { |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 768 | return adc_read(ADC_MMC_SWITCH) < 0x200 ? true : false; |
| 769 | } |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 770 | |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 771 | static void mmc_tick(void) |
| 772 | { |
| 773 | bool current_status; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 774 | |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 775 | current_status = mmc_detect(); |
| 776 | |
| 777 | /* Only report when the status has changed */ |
| 778 | if (current_status != last_mmc_status) |
| 779 | { |
| 780 | last_mmc_status = current_status; |
| 781 | countdown = 30; |
| 782 | } |
| 783 | else |
| 784 | { |
| 785 | /* Count down until it gets negative */ |
| 786 | if (countdown >= 0) |
| 787 | countdown--; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 788 | |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 789 | /* Report to the thread if we have had 3 identical status |
| 790 | readings in a row */ |
| 791 | if (countdown == 0) |
| 792 | { |
| 793 | if (current_status) |
| 794 | { |
| 795 | queue_broadcast(SYS_MMC_INSERTED, NULL); |
| 796 | } |
| 797 | else |
| 798 | { |
| 799 | queue_broadcast(SYS_MMC_EXTRACTED, NULL); |
| 800 | card_info[1].initialized = false; |
| 801 | } |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 802 | } |
| 803 | } |
| 804 | } |
| 805 | |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 806 | int ata_soft_reset(void) |
| 807 | { |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 808 | return 0; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 809 | } |
| 810 | |
| 811 | void ata_enable(bool on) |
| 812 | { |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 813 | PBCR1 &= ~0x0CF0; /* PB13, PB11 and PB10 become GPIOs, if not modified below */ |
Jörg Hohensohn | c4b326c | 2004-10-01 16:57:54 +0000 | [diff] [blame] | 814 | PACR2 &= ~0x4000; /* use PA7 (bridge reset) as GPIO */ |
Jörg Hohensohn | c4e8bed | 2004-09-11 15:18:10 +0000 | [diff] [blame] | 815 | if (on) |
| 816 | { |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 817 | PBCR1 |= 0x08A0; /* as SCK1, TxD1, RxD1 */ |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 818 | IPRE &= 0x0FFF; /* disable SCI1 interrupts for the CPU */ |
Jörg Hohensohn | c4e8bed | 2004-09-11 15:18:10 +0000 | [diff] [blame] | 819 | } |
Jens Arnold | de6f799 | 2004-09-29 00:50:40 +0000 | [diff] [blame] | 820 | and_b(~0x80, &PADRL); /* assert reset */ |
| 821 | sleep(HZ/20); |
| 822 | or_b(0x80, &PADRL); /* de-assert reset */ |
| 823 | sleep(HZ/20); |
| 824 | card_info[0].initialized = false; |
| 825 | card_info[1].initialized = false; |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 826 | } |
| 827 | |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 828 | int ata_init(void) |
| 829 | { |
| 830 | int rc = 0; |
| 831 | |
Jens Arnold | dc6caf9 | 2004-10-03 23:32:09 +0000 | [diff] [blame] | 832 | mutex_init(&mmc_mutex); |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 833 | |
| 834 | led(false); |
| 835 | |
Jörg Hohensohn | 00be746 | 2004-09-11 09:06:58 +0000 | [diff] [blame] | 836 | /* Port setup */ |
Jörg Hohensohn | c4b326c | 2004-10-01 16:57:54 +0000 | [diff] [blame] | 837 | PACR1 &= ~0x0F00; /* GPIO function for PA12, /IRQ1 for PA13 */ |
| 838 | PACR1 |= 0x0400; |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 839 | PADR |= 0x0680; /* set all the selects + reset high (=inactive) */ |
| 840 | PAIOR |= 0x1680; /* make outputs for them and the PA12 clock gate */ |
| 841 | |
| 842 | PBDR |= 0x2C00; /* SCK1, TxD1 and RxD1 high when GPIO CHECKME: mask */ |
| 843 | PBIOR |= 0x2000; /* SCK1 output */ |
| 844 | PBIOR &= ~0x0C00; /* TxD1, RxD1 input */ |
Jörg Hohensohn | 00be746 | 2004-09-11 09:06:58 +0000 | [diff] [blame] | 845 | |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 846 | last_mmc_status = mmc_detect(); |
| 847 | if (last_mmc_status) |
Jörg Hohensohn | 00be746 | 2004-09-11 09:06:58 +0000 | [diff] [blame] | 848 | { /* MMC inserted */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 849 | current_card = 1; |
Jörg Hohensohn | 00be746 | 2004-09-11 09:06:58 +0000 | [diff] [blame] | 850 | } |
| 851 | else |
| 852 | { /* no MMC, use internal memory */ |
Jens Arnold | 90cbd3b | 2004-09-28 06:23:57 +0000 | [diff] [blame] | 853 | current_card = 0; |
Jörg Hohensohn | 00be746 | 2004-09-11 09:06:58 +0000 | [diff] [blame] | 854 | } |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 855 | |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 856 | ata_enable(true); |
Jens Arnold | a450e34 | 2004-10-09 01:14:55 +0000 | [diff] [blame] | 857 | |
Jens Arnold | f5bdf69 | 2004-10-10 00:35:19 +0000 | [diff] [blame] | 858 | if ( !initialized ) |
| 859 | { |
| 860 | tick_add_task(mmc_tick); |
Jörg Hohensohn | 57ea92c | 2004-09-11 03:48:05 +0000 | [diff] [blame] | 861 | initialized = true; |
| 862 | } |
| 863 | |
| 864 | return rc; |
| 865 | } |
| 866 | |