summaryrefslogtreecommitdiff
path: root/firmware/target/arm/as3525/ata_sd_as3525.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/as3525/ata_sd_as3525.c')
-rw-r--r--firmware/target/arm/as3525/ata_sd_as3525.c341
1 files changed, 341 insertions, 0 deletions
diff --git a/firmware/target/arm/as3525/ata_sd_as3525.c b/firmware/target/arm/as3525/ata_sd_as3525.c
new file mode 100644
index 0000000000..e8f899bc37
--- /dev/null
+++ b/firmware/target/arm/as3525/ata_sd_as3525.c
@@ -0,0 +1,341 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright © 2008 Rafaël Carré
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/* Driver for the ARM PL180 SD/MMC controller inside AS3525 SoC */
23
24#include "config.h" /* for HAVE_MULTIVOLUME */
25
26#include "as3525.h"
27#include "mmci.h"
28#include "panic.h"
29#include "stdbool.h"
30#include "ata.h"
31
32#define NAND_AS3525 0
33#define SD_AS3525 1
34static int pl180_base[2] = { NAND_FLASH_BASE, SD_MCI_BASE };
35
36/* ARM PL180 registers */
37#define MMC_POWER(i) (*(volatile unsigned long *) (pl180_base[i]+0x00))
38#define MMC_CLOCK(i) (*(volatile unsigned long *) (pl180_base[i]+0x04))
39#define MMC_ARGUMENT(i) (*(volatile unsigned long *) (pl180_base[i]+0x08))
40#define MMC_COMMAND(i) (*(volatile unsigned long *) (pl180_base[i]+0x0C))
41#define MMC_RESPCMD(i) (*(volatile unsigned long *) (pl180_base[i]+0x10))
42#define MMC_RESP0(i) (*(volatile unsigned long *) (pl180_base[i]+0x14))
43#define MMC_RESP1(i) (*(volatile unsigned long *) (pl180_base[i]+0x18))
44#define MMC_RESP2(i) (*(volatile unsigned long *) (pl180_base[i]+0x1C))
45#define MMC_RESP3(i) (*(volatile unsigned long *) (pl180_base[i]+0x20))
46#define MMC_DATACTRL(i) (*(volatile unsigned long *) (pl180_base[i]+0x2C))
47#define MMC_STATUS(i) (*(volatile unsigned long *) (pl180_base[i]+0x34))
48#define MMC_CLEAR(i) (*(volatile unsigned long *) (pl180_base[i]+0x38))
49#define MMC_MASK0(i) (*(volatile unsigned long *) (pl180_base[i]+0x3C))
50#define MMC_MASK1(i) (*(volatile unsigned long *) (pl180_base[i]+0x40))
51#define MMC_SELECT(i) (*(volatile unsigned long *) (pl180_base[i]+0x44))
52
53
54/* SD commands */
55#define GO_IDLE_STATE 0
56#define MMC_CMD_READ_CID 2
57#define SEND_IF_COND 8
58#define SEND_OP_COND 41
59#define APP_CMD 55
60
61/* command flags */
62#define MMC_NO_FLAGS (0<<0)
63#define MMC_RESP (1<<0)
64#define MMC_LONG_RESP (1<<1)
65#define MMC_ARG (1<<2)
66
67#ifdef BOOTLOADER
68#define DEBUG
69void reset_screen(void);
70void printf(const char *format, ...);
71#endif
72
73struct mmc_command
74{
75 int cmd;
76 int arg;
77 int resp[4];
78 int flags;
79};
80
81static inline void mci_delay(void) { int i = 0xffff; while(i--) ; }
82
83static void mci_set_clock_divider(const int drive, int divider)
84{
85 int clock = MMC_CLOCK(drive);
86
87 if(divider > 1)
88 {
89 /* use divide logic */
90 clock &= ~MCI_CLK_BYPASS;
91
92 /* convert divider to MMC_CLOCK logic */
93 divider = (divider/2) - 1;
94 if(divider >= 256)
95 divider = 255;
96 }
97 else
98 {
99 /* bypass dividing logic */
100 clock |= MCI_CLK_BYPASS;
101 divider = 0;
102 }
103
104 MMC_CLOCK(drive) = clock | divider;
105
106 mci_delay();
107}
108
109static int send_cmd(const int drive, struct mmc_command *cmd)
110{
111 int val, status;
112
113 while(MMC_STATUS(drive) & MCI_CMDACTIVE); /* useless */
114
115 if(MMC_COMMAND(drive) & MCI_CPSM_ENABLE) /* clears existing command */
116 {
117 MMC_COMMAND(drive) = 0;
118 mci_delay();
119 }
120
121 val = cmd->cmd | MCI_CPSM_ENABLE;
122 if(cmd->flags & MMC_RESP)
123 {
124 val |= MCI_CPSM_RESPONSE;
125 if(cmd->flags & MMC_LONG_RESP)
126 val |= MCI_CPSM_LONGRSP;
127 }
128
129 MMC_CLEAR(drive) = 0x7ff;
130
131 MMC_ARGUMENT(drive) = (cmd->flags & MMC_ARG) ? cmd->arg : 0;
132 MMC_COMMAND(drive) = val;
133
134 while(MMC_STATUS(drive) & MCI_CMDACTIVE);
135
136 MMC_COMMAND(drive) = 0;
137 MMC_ARGUMENT(drive) = ~0;
138
139 do
140 {
141 status = MMC_STATUS(drive);
142 if(cmd->flags & MMC_RESP)
143 {
144 if(status & MCI_CMDTIMEOUT)
145 {
146 if(cmd->cmd == SEND_IF_COND)
147 break; /* SDHC test can fail */
148 panicf("Response timeout");
149 }
150 else if(status & (MCI_CMDCRCFAIL|MCI_CMDRESPEND))
151 { /* resp received */
152 cmd->resp[0] = MMC_RESP0(drive);
153 if(cmd->flags & MMC_LONG_RESP)
154 {
155 cmd->resp[1] = MMC_RESP1(drive);
156 cmd->resp[2] = MMC_RESP2(drive);
157 cmd->resp[3] = MMC_RESP3(drive);
158 }
159 break;
160 }
161 }
162 else
163 if(status & MCI_CMDSENT)
164 break;
165
166 } while(1);
167
168 MMC_CLEAR(drive) = 0x7ff;
169 return status;
170}
171
172static void sd_init_card(const int drive)
173{
174 struct mmc_command cmd_app, cmd_op_cond, cmd_idle, cmd_if_cond;
175 int status;
176 bool sdhc;
177
178#ifdef DEBUG
179 reset_screen();
180 printf("now - powered up");
181#endif
182
183 cmd_idle.cmd = GO_IDLE_STATE;
184 cmd_idle.arg = 0;
185 cmd_idle.flags = MMC_NO_FLAGS;
186 if(send_cmd(drive, &cmd_idle) != MCI_CMDSENT)
187 panicf("goto idle failed!");
188#ifdef DEBUG
189 else
190 printf("now - idle");
191#endif
192
193 mci_delay();
194
195 cmd_if_cond.cmd = SEND_IF_COND;
196 cmd_if_cond.arg = (1 /* 2.7-3.6V */ << 8) | 0xAA /* check pattern */;
197 cmd_if_cond.flags = MMC_RESP | MMC_ARG;
198
199 cmd_app.cmd = APP_CMD;
200 cmd_app.flags = MMC_RESP | MMC_ARG;
201 cmd_app.arg = 0; /* 31:16 RCA (0) , 15:0 stuff bits */
202
203 cmd_op_cond.cmd = SEND_OP_COND;
204 cmd_op_cond.flags = MMC_RESP | MMC_ARG;
205
206#ifdef DEBUG
207 printf("now - card powering up");
208#endif
209
210 sdhc = false;
211 status = send_cmd(drive, &cmd_if_cond);
212 if(status & (MCI_CMDCRCFAIL|MCI_CMDRESPEND))
213 {
214 if((cmd_if_cond.resp[0] & 0xFFF) == cmd_if_cond.arg)
215 sdhc = true;
216#ifdef DEBUG
217 else
218 printf("Bad resp: %x",cmd_if_cond.arg);
219#endif
220 }
221#ifdef DEBUG
222 else
223 printf("cmd_if_cond stat: 0x%x",status);
224
225 printf("%s Capacity",sdhc?"High":"Normal");
226 mci_delay();
227 mci_delay();
228 mci_delay();
229#endif
230
231#ifdef DEBUG
232 int loop = 0;
233#endif
234 do {
235 mci_delay();
236 mci_delay();
237#ifdef DEBUG
238 reset_screen();
239 printf("Loop number #%d", ++loop);
240#endif
241 /* app_cmd */
242 status = send_cmd(drive, &cmd_app);
243 if( !(status & (MCI_CMDCRCFAIL|MCI_CMDRESPEND)) ||
244 !(cmd_app.resp[0] & (1<<5)) )
245 {
246 panicf("app_cmd failed");
247 }
248
249 cmd_op_cond.arg = sdhc ? 0x40FF8000 : (8<<0x14); /* ocr */
250 status = send_cmd(drive, &cmd_op_cond);
251 if(!(status & (MCI_CMDCRCFAIL|MCI_CMDRESPEND)))
252 panicf("cmd_op_cond failed");
253
254#ifdef DEBUG
255 printf("OP COND: 0x%.8x", cmd_op_cond.resp[0]);
256#endif
257 } while(!(cmd_op_cond.resp[0] & (1<<31))); /* until card is powered up */
258
259#ifdef DEBUG
260 printf("now - card ready !");
261#endif
262}
263
264static void init_pl180_controller(const int drive)
265{
266 MMC_COMMAND(drive) = MMC_DATACTRL(drive) = 0;
267 MMC_CLEAR(drive) = 0x7ff;
268
269 MMC_MASK0(drive) = MMC_MASK1(drive) = 0; /* disable all interrupts */
270
271 MMC_POWER(drive) = MCI_PWR_UP | (10 /*voltage*/ << 2); /* use OF voltage */
272 mci_delay();
273
274 MMC_POWER(drive) |= MCI_PWR_ON;
275 mci_delay();
276
277 MMC_SELECT(drive) = 0;
278
279 MMC_CLOCK(drive) = MCI_CLK_ENABLE;
280 MMC_CLOCK(drive) &= ~MCI_CLK_PWRSAVE;
281
282 /* set MCLK divider */
283 mci_set_clock_divider(drive, 200);
284}
285
286int ata_init(void)
287{
288 /* reset peripherals */
289
290 CCU_SRC =
291#ifdef HAVE_MULTIVOLUME
292 CCU_SRC_SDMCI_EN |
293#endif
294 CCU_SRC_NAF_EN | CCU_SRC_IDE_EN | CCU_SRC_IDE_AHB_EN | CCU_SRC_MST_EN;
295
296 CCU_SRL = CCU_SRL_MAGIC_NUMBER;
297 CCU_SRL = 0;
298
299 GPIOC_DIR &= ~(1<<1);
300 if(GPIOC_PIN(1))
301 CCU_SPARE1 |= 4; /* sets bit 3 of undocumented register */
302 else
303 CCU_SPARE1 &= ~4; /* or clear it */
304
305 CGU_IDE = (1<<7)|(1<<6); /* enable, 24MHz clock */
306 CGU_MEMSTICK = (1<<8); /* enable, 24MHz clock */
307
308 CGU_PERI |= CGU_NAF_CLOCK_ENABLE;
309#ifdef HAVE_MULTIVOLUME
310 CGU_PERI |= CGU_MCI_CLOCK_ENABLE;
311#endif
312
313 CCU_IO &= ~8; /* bits 3:2 = 01, xpd is SD interface */
314 CCU_IO |= 4;
315
316 init_pl180_controller(NAND_AS3525);
317 sd_init_card(NAND_AS3525);
318
319#ifdef HAVE_MULTIVOLUME
320 init_pl180_controller(SD_AS3525);
321 sd_init_card(SD_AS3525);
322#endif
323
324 return 0;
325}
326
327int ata_read_sectors(IF_MV2(int drive,) unsigned long start, int count, void* buf)
328{
329 (void)start;
330 (void)count;
331 (void)buf;
332 return 0; /* TODO */
333}
334
335int ata_write_sectors(IF_MV2(int drive,) unsigned long start, int count, const void* buf)
336{
337 (void)start;
338 (void)count;
339 (void)buf;
340 return 0; /* TODO */
341}