OpenCores
URL https://opencores.org/ocsvn/s80186/s80186/trunk

Subversion Repositories s80186

[/] [s80186/] [trunk/] [bios/] [sd.c] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 jamieiles
// Copyright Jamie Iles, 2017
2
//
3
// This file is part of s80x86.
4
//
5
// s80x86 is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
//
10
// s80x86 is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with s80x86.  If not, see <http://www.gnu.org/licenses/>.
17
 
18
#include "io.h"
19
#include "serial.h"
20
#include "bios.h"
21
#include "utils.h"
22
 
23
#define SPI_CONTROL_PORT 0xfff0
24
#define SPI_TRANSFER_PORT 0xfff2
25
#define SPI_XFER_BUSY (1 << 8)
26
#define SPI_CS_DEACTIVATE (1 << 9)
27
 
28
static char spi_xfer_buf[1280];
29
static char sd_is_sdhc;
30
 
31
static void noinline spi_xfer_buf_set(int offs, unsigned char v)
32
{
33
    spi_xfer_buf[offs] = v;
34
}
35
 
36
static unsigned char noinline spi_xfer_buf_get(int offs)
37
{
38
    return spi_xfer_buf[offs];
39
}
40
 
41
static void spi_wait_idle(void)
42
{
43
    while (inw(SPI_TRANSFER_PORT) & SPI_XFER_BUSY)
44
        continue;
45
}
46
 
47
static void spi_xfer(int len)
48
{
49
    int i;
50
 
51
    for (i = 0; i < len; ++i) {
52
        outw(SPI_TRANSFER_PORT, spi_xfer_buf_get(i));
53
        spi_wait_idle();
54
        spi_xfer_buf_set(i, inw(SPI_TRANSFER_PORT) & 0xff);
55
    }
56
}
57
 
58
static void sd_send_initial_clock(void)
59
{
60
    int i;
61
 
62
    for (i = 0; i < 8; ++i)
63
        spi_xfer_buf_set(i, 0xff);
64
    // No CS, slow clock
65
    outw(SPI_CONTROL_PORT, SPI_CS_DEACTIVATE | 0x1ff);
66
 
67
    spi_xfer(8);
68
}
69
 
70
static void sd_flush_fifo(void)
71
{
72
    int i;
73
 
74
    for (i = 0; i < 128; ++i)
75
        spi_xfer_buf_set(i, 0xff);
76
    // No CS, slow clock
77
    outw(SPI_CONTROL_PORT, 0x1ff);
78
 
79
    spi_xfer(8);
80
}
81
 
82
struct spi_cmd {
83
    unsigned char cmd;
84
    unsigned char arg[4];
85
    unsigned char crc;
86
 
87
    unsigned short data_seg;
88
    const void *data_addr;
89
    unsigned short tx_datalen;
90
    unsigned short rx_datalen;
91
};
92
 
93
struct r1_response {
94
    unsigned char v;
95
};
96
 
97
#define R1_ERROR_MASK 0xfe
98
#define SD_NCR 8
99
#define DATA_START_TOKEN 0xfe
100
#define BLOCK_SIZE 512
101
/* Maximum number of high bytes to read before a data start token. */
102
#define MAX_DATA_START_OFFS 512
103
 
104
static unsigned short spi_command_len(const struct spi_cmd *cmd)
105
{
106
    return 1 + 6 + cmd->tx_datalen + cmd->rx_datalen + SD_NCR;
107
}
108
 
109
static void spi_do_command(const struct spi_cmd *cmd)
110
{
111
    int m, cmdlen;
112
 
113
    cmdlen = spi_command_len(cmd);
114
 
115
    // The command.
116
    spi_xfer_buf_set(0, 0xff);
117
    spi_xfer_buf_set(1, cmd->cmd);
118
    for (m = 0; m < 4; ++m)
119
        spi_xfer_buf_set(2 + m, cmd->arg[m]);
120
    spi_xfer_buf_set(6, cmd->crc);
121
    // Transmit data.
122
    memcpy_seg(get_cs(), spi_xfer_buf + 7, cmd->data_seg, cmd->data_addr,
123
               cmd->tx_datalen);
124
    // Initialize receive buffer so we don't shift out new, garbage data.
125
    for (m = 7 + cmd->tx_datalen; m < cmdlen; ++m)
126
        spi_xfer_buf_set(m, 0xff);
127
 
128
    // Fast clock, CS enabled.
129
    outw(SPI_CONTROL_PORT, 0x1);
130
    spi_xfer(cmdlen);
131
}
132
 
133
static int find_r1_response(struct r1_response *r1)
134
{
135
    int i;
136
 
137
    r1->v = 0;
138
 
139
    for (i = 0; i < sizeof(spi_xfer_buf) / sizeof(spi_xfer_buf[0]); ++i)
140
        if (spi_xfer_buf_get(i) != 0xff) {
141
            r1->v = spi_xfer_buf_get(i);
142
            break;
143
        }
144
 
145
    if (i == sizeof(spi_xfer_buf))
146
        return -1;
147
 
148
    return i;
149
}
150
 
151
static int sd_send_reset(void)
152
{
153
    struct spi_cmd cmd = {
154
        .cmd = 0x40, .crc = 0x95, .rx_datalen = 1,
155
    };
156
    struct r1_response r1;
157
 
158
    spi_do_command(&cmd);
159
    if (find_r1_response(&r1) < 0)
160
        return -1;
161
 
162
    return r1.v & R1_ERROR_MASK;
163
}
164
 
165
static int sd_send_if_cond(void)
166
{
167
    struct spi_cmd cmd = {
168
        .cmd = 0x48,
169
        .crc = 0x87,
170
        .arg = {0x00, 0x00, 0x01, 0xaa},
171
        .rx_datalen = 1,
172
    };
173
    struct r1_response r1;
174
 
175
    spi_do_command(&cmd);
176
    if (find_r1_response(&r1) < 0)
177
        return -1;
178
 
179
    return r1.v & R1_ERROR_MASK;
180
}
181
 
182
static int sd_send_read_ocr(void)
183
{
184
    struct spi_cmd cmd = {
185
        .cmd = 0x7a, .rx_datalen = 5,
186
    };
187
    struct r1_response r1;
188
 
189
    spi_do_command(&cmd);
190
    int r1_offs = find_r1_response(&r1);
191
    if (r1_offs < 0)
192
        return -1;
193
 
194
    union {
195
        unsigned char u8[4];
196
        unsigned long u32;
197
    } converter = {
198
        .u8 =
199
            {
200
                spi_xfer_buf_get(r1_offs + 4), spi_xfer_buf_get(r1_offs + 3),
201
                spi_xfer_buf_get(r1_offs + 2), spi_xfer_buf_get(r1_offs + 1),
202
            },
203
    };
204
 
205
    if (converter.u32 & (1LU << 30))
206
        sd_is_sdhc = 1;
207
    else
208
        sd_is_sdhc = 0;
209
 
210
    return r1.v & R1_ERROR_MASK;
211
}
212
 
213
static int send_acmd(void)
214
{
215
    struct spi_cmd cmd = {
216
        .cmd = 0x77, .arg = {0x00, 0x00, 0x00, 0x00}, .rx_datalen = 1,
217
    };
218
    struct r1_response r1;
219
 
220
    spi_do_command(&cmd);
221
    if (find_r1_response(&r1) < 0)
222
        return -1;
223
 
224
    return r1.v & R1_ERROR_MASK;
225
}
226
 
227
static int sd_wait_ready(void)
228
{
229
    struct r1_response r1 = {};
230
 
231
    do {
232
        struct spi_cmd cmd = {
233
            .cmd = 0x69, .arg = {0x40, 0x00, 0x00, 0x00}, .rx_datalen = 1,
234
        };
235
        int rc = send_acmd();
236
 
237
        if (rc)
238
            return rc;
239
 
240
        spi_do_command(&cmd);
241
        if (find_r1_response(&r1) < 0)
242
            return -1;
243
 
244
        if (r1.v & R1_ERROR_MASK)
245
            return r1.v & R1_ERROR_MASK;
246
    } while (r1.v & 0x1);
247
 
248
    return 0;
249
}
250
 
251
static int sd_set_blocklen(void)
252
{
253
    struct spi_cmd cmd = {
254
        .cmd = 0x50,
255
        /* BLOCK_SIZE bytes */
256
        .arg = {0x00, 0x00, 0x02, 0x00},
257
        .rx_datalen = 1,
258
    };
259
    struct r1_response r1;
260
 
261
    spi_do_command(&cmd);
262
    if (find_r1_response(&r1) < 0)
263
        return -1;
264
 
265
    return r1.v & R1_ERROR_MASK;
266
}
267
 
268
static int find_data_start(int r1offs)
269
{
270
    ++r1offs;
271
    while (spi_xfer_buf_get(r1offs) != DATA_START_TOKEN &&
272
           r1offs < sizeof(spi_xfer_buf))
273
        ++r1offs;
274
 
275
    return r1offs == sizeof(spi_xfer_buf) ? -1 : r1offs + 1;
276
}
277
 
278
static int noinline sd_make_write_request(unsigned long address)
279
{
280
    struct spi_cmd cmd = {
281
        .cmd = 0x58, .rx_datalen = 1,
282
    };
283
    struct r1_response r1;
284
 
285
    cmd.arg[0] = (address >> 24) & 0xff;
286
    cmd.arg[1] = (address >> 16) & 0xff;
287
    cmd.arg[2] = (address >> 8) & 0xff;
288
    cmd.arg[3] = (address >> 0) & 0xff;
289
 
290
    spi_do_command(&cmd);
291
    if (find_r1_response(&r1) < 0)
292
        return -1;
293
 
294
    return r1.v & R1_ERROR_MASK;
295
}
296
 
297
static void noinline sd_write_block(unsigned short sseg, unsigned short saddr)
298
{
299
    spi_xfer_buf_set(0, 0xff); // Gap
300
    spi_xfer_buf_set(1, 0xfe); // Data start token
301
    memcpy_seg(get_cs(), spi_xfer_buf + 2, sseg, (const void *)saddr, 512);
302
    spi_xfer_buf_set(2 + 512, 0x0);  // CRC1
303
    spi_xfer_buf_set(3 + 512, 0x0);  // CRC2
304
    spi_xfer_buf_set(4 + 512, 0xff); // Packet response
305
 
306
    spi_xfer(5 + 512);
307
}
308
 
309
static int noinline write_received(unsigned char packet_response)
310
{
311
    return (packet_response & 0x1) && ((packet_response >> 1) & 0x7) == 0x2;
312
}
313
 
314
static int noinline wait_for_write_completion(void)
315
{
316
    int i = 0;
317
 
318
    for (i = 0; i < 4096; ++i) {
319
        spi_xfer_buf_set(0, 0xff);
320
        spi_xfer(1);
321
        if (spi_xfer_buf_get(0) != 0)
322
            return 0;
323
    }
324
 
325
    return -1;
326
}
327
 
328
int write_sector(unsigned short sector,
329
                 unsigned short sseg,
330
                 unsigned short saddr)
331
{
332
    unsigned long address = sector;
333
 
334
    if (!sd_is_sdhc)
335
        address *= 512;
336
 
337
    if (sd_make_write_request(address))
338
        return -1;
339
 
340
    sd_write_block(sseg, saddr);
341
 
342
    unsigned char packet_response = spi_xfer_buf_get(4 + 512);
343
    if (!write_received(packet_response))
344
        return -1;
345
 
346
    return wait_for_write_completion();
347
}
348
 
349
int read_sector(unsigned short sector,
350
                unsigned short dseg,
351
                unsigned short daddr)
352
{
353
    struct spi_cmd cmd = {
354
        .cmd = 0x51,
355
        /* r1, start token, data, CRC16 */
356
        .rx_datalen = 1 + 1 + BLOCK_SIZE + 2 + MAX_DATA_START_OFFS,
357
    };
358
    struct r1_response r1;
359
    int r1offs, data_start;
360
    unsigned long address = sector;
361
 
362
    if (!sd_is_sdhc)
363
        address *= 512;
364
 
365
    cmd.arg[0] = (address >> 24) & 0xff;
366
    cmd.arg[1] = (address >> 16) & 0xff;
367
    cmd.arg[2] = (address >> 8) & 0xff;
368
    cmd.arg[3] = (address >> 0) & 0xff;
369
 
370
    spi_do_command(&cmd);
371
    r1offs = find_r1_response(&r1);
372
    if (r1offs < 0) {
373
        putstr("Failed to find R1 response\n");
374
        return -1;
375
    }
376
    if (r1.v & R1_ERROR_MASK) {
377
        putstr("Read sector failed\n");
378
        return -1;
379
    }
380
 
381
    data_start = find_data_start(r1offs);
382
    if (data_start < 0) {
383
        putstr("No data start token\n");
384
        return -1;
385
    }
386
 
387
    memcpy_seg(dseg, (void *)daddr, get_cs(), spi_xfer_buf + data_start, 512);
388
 
389
    return 0;
390
}
391
 
392
void sd_init(void)
393
{
394
    sd_send_initial_clock();
395
    sd_flush_fifo();
396
    if (sd_send_reset())
397
        panic("Failed to reset SD card");
398
    if (sd_send_if_cond())
399
        panic("Failed to send interface conditions to SD card");
400
    if (sd_send_read_ocr())
401
        panic("Failed to read OCR for SD card");
402
    if (sd_wait_ready())
403
        panic("Failed to wait for SD card init");
404
    if (sd_send_read_ocr())
405
        panic("Failed to re-read OCR for SD card");
406
    if (sd_set_blocklen())
407
        panic("Failed to wait for SD card init");
408
 
409
    putstr("SD card ready\n");
410
}
411
 
412
void sd_boot(void)
413
{
414
    if (read_sector(0, 0, 0x7c00))
415
        panic("Failed to read boot sector\n");
416
 
417
    putstr("Booting from SD card...\n");
418
    asm volatile(
419
        "mov $0x80, %dl\n"
420
        "jmp $0x0000, $0x7c00");
421
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.