Kernel: Add a generic SD/MMC storage card driver, using SPI bus
authorWill Sowerbutts <will@sowerbutts.com>
Mon, 29 Dec 2014 00:47:58 +0000 (00:47 +0000)
committerWill Sowerbutts <will@sowerbutts.com>
Mon, 29 Dec 2014 01:05:22 +0000 (01:05 +0000)
functions provided by the platform. Also includes a driver for the SD
card SPI bus on the n8vem-mark4 platform.

Kernel/cpu-z180/z180.h
Kernel/dev/devsd.c [new file with mode: 0644]
Kernel/dev/devsd.h [new file with mode: 0644]
Kernel/platform-n8vem-mark4/Makefile
Kernel/platform-n8vem-mark4/README
Kernel/platform-n8vem-mark4/config.h
Kernel/platform-n8vem-mark4/devices.c
Kernel/platform-n8vem-mark4/devsdspi.c [new file with mode: 0644]
Kernel/platform-n8vem-mark4/uzi.lnk

index 591b896..61b4d44 100644 (file)
@@ -46,6 +46,9 @@ __sfr __at (Z180_IO_BASE + 0x1B) ASCI_ASTC0H;   /* ASCI time constant register c
 __sfr __at (Z180_IO_BASE + 0x1C) ASCI_ASTC1L;   /* ASCI time constant register channel 1 low  */
 __sfr __at (Z180_IO_BASE + 0x1D) ASCI_ASTC1H;   /* ASCI time constant register channel 1 high */
 
+__sfr __at (Z180_IO_BASE + 0x0A) CSIO_CNTR;     /* CSI/O control/status register              */
+__sfr __at (Z180_IO_BASE + 0x0B) CSIO_TRDR;     /* CSI/O transmit/receive data register       */
+
 __sfr __at (0xE0)                ESCC_CTRL_A;   /* ESCC Channel A control register            */
 __sfr __at (0xE1)                ESCC_DATA_A;   /* ESCC Channel A data register               */
 __sfr __at (0xE2)                ESCC_CTRL_B;   /* ESCC Channel B control register            */
diff --git a/Kernel/dev/devsd.c b/Kernel/dev/devsd.c
new file mode 100644 (file)
index 0000000..d5ecb43
--- /dev/null
@@ -0,0 +1,373 @@
+/*-----------------------------------------------------------------------*/
+/* Fuzix SD card driver                                                  */
+/* 2014-12-28 Will Sowerbutts                                            */
+/*                                                                       */
+/* Based on UZI-socz80 SD card driver, which was itself based on:        */
+/*   MMCv3/SDv1/SDv2 (in SPI mode) control module  (C)ChaN, 2007         */
+/*  (from http://www.cl.cam.ac.uk/teaching/1011/P31/lib/diskio.c)        */
+/* and http://elm-chan.org/docs/mmc/mmc_e.html                           */
+/*-----------------------------------------------------------------------*/
+
+/* Minor numbers: See comments in devide.c, exactly the same scheme is used here. */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <timer.h>
+#include <devsd.h>
+#include <stdbool.h>
+#include <mbr.h>
+#include "config.h"
+
+#define MAX_SLICES 63
+
+/* keep track of current card type, UZI partition offset */
+static uint8_t  sd_drive_present; /* bitmap */
+static uint8_t  sd_card_type[SD_DRIVE_COUNT];
+static uint32_t sd_partition_start[SD_DRIVE_COUNT];
+static uint8_t  sd_slice_count[SD_DRIVE_COUNT];
+
+/* internal functions */
+static void sd_init_drive(uint8_t drive);
+static int sd_readwrite(uint8_t minor, uint8_t rawflag, bool do_write);
+static int sd_spi_init(uint8_t drive);
+static void sd_spi_release(uint8_t drive);
+static int sd_spi_wait_ready(uint8_t drive);
+static bool sd_spi_transmit_block(uint8_t drive, void *ptr, unsigned int length);
+static bool sd_spi_receive_block(uint8_t drive, void *ptr, unsigned int length);
+static int sd_send_command(uint8_t drive, unsigned char cmd, uint32_t arg);
+static uint32_t sd_get_size_sectors(uint8_t drive);
+static bool sd_read_sector(uint8_t drive, void *ptr, uint32_t lba);
+static bool sd_write_sector(uint8_t drive, void *ptr, uint32_t lba);
+
+int devsd_open(uint8_t minor, uint16_t flags)
+{
+    uint8_t drive;
+    flags; /* not used */
+
+    drive = minor >> 6;
+    minor = minor & 0x3F;
+
+    if(sd_drive_present & (1 << drive) && (minor == 0 || minor < sd_slice_count[drive]))
+        return 0;
+
+    udata.u_error = ENODEV;
+    return -1;
+}
+
+int devsd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag; /* not used */
+    return sd_readwrite(minor, rawflag, false);
+}
+
+int devsd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag; /* not used */
+    return sd_readwrite(minor, rawflag, true);
+}
+
+void devsd_init(void)
+{
+    uint8_t d;
+
+    sd_drive_present = 0;
+    for(d=0; d<SD_DRIVE_COUNT; d++)
+        sd_init_drive(d);
+}
+
+static int sd_readwrite(uint8_t minor, uint8_t rawflag, bool do_write)
+{
+    uint32_t lba;
+    int attempt;
+    void *target;
+    uint8_t drive;
+
+    drive = minor >> 6;
+    minor = minor & 0x3F;
+
+    if(rawflag == 0){
+        target = udata.u_buf->bf_data;
+
+        for(attempt=0; attempt<5; attempt++){
+            lba = udata.u_buf->bf_blk;
+
+            /* minor 0 is the whole drive, without translation */
+            if(minor > 0){
+                lba += sd_partition_start[drive];
+                lba += ((unsigned long)(minor-1) << SLICE_SIZE_LOG2_SECTORS);
+            }
+            if(do_write){
+                if(sd_write_sector(drive, udata.u_buf->bf_data, lba))
+                    return 1;
+            }else{
+                if(sd_read_sector(drive, udata.u_buf->bf_data, lba))
+                    return 1;
+            }
+            kputs("sd: failed, retrying.\n");
+        }
+    }
+
+    udata.u_error = EIO;
+    return -1;
+}
+
+static void sd_init_drive(uint8_t drive)
+{
+    uint32_t sector_count;
+    unsigned char *sector;
+
+    sd_partition_start[drive] = 0;
+    sd_slice_count[drive] = 0;
+
+    kprintf("sd%c: ", 'a'+drive);
+    sd_card_type[drive] = sd_spi_init(drive);
+
+    if(!(sd_card_type[drive] & (~CT_BLOCK))){
+        kprintf("no card found\n");
+        return;
+    }
+    
+    /* read and compute card size */
+    sector_count = sd_get_size_sectors(drive);
+    if(!sector_count){
+        kputs("weird card\n");
+        return;
+    }
+
+    /* read partition table, locate the UZI partition */
+    sector = (unsigned char*)tmpbuf();
+
+    if(!sd_read_sector(drive, sector, 0)){
+        kprintf("failed to read partition table\n");
+        goto failout;
+    }
+
+    /* if we get this far the drive is probably ok */
+    sd_drive_present |= (1 << drive);
+
+    /* look for our partition */
+    parse_partition_table(sector, &sd_partition_start[drive], &sd_slice_count[drive], MAX_SLICES);
+
+failout:
+    brelse((bufptr)sector);
+}
+
+static int sd_spi_init(uint8_t drive)
+{
+    unsigned char n, cmd, card_type, ocr[4];
+    timer_t timer;
+
+    sd_spi_raise_cs(drive);
+
+    sd_spi_clock(drive, false);
+    for (n = 20; n; n--)
+        sd_spi_receive_byte(drive); /* 160 dummy clocks */
+
+    card_type = 0;
+    /* Enter Idle state */
+    if (sd_send_command(drive, CMD0, 0) == 1) {
+        /* initialisation timeout 1 second */
+        timer = set_timer_sec(1);
+        if (sd_send_command(drive, CMD8, (uint32_t)0x1AA) == 1) {    /* SDHC */
+            /* Get trailing return value of R7 resp */
+            for (n = 0; n < 4; n++) ocr[n] = sd_spi_receive_byte(drive);
+            /* The card can work at vdd range of 2.7-3.6V */
+            if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
+                /* Wait for leaving idle state (ACMD41 with HCS bit) */
+                while(!timer_expired(timer) && sd_send_command(drive, ACMD41, (uint32_t)1 << 30));
+                /* Check CCS bit in the OCR */
+                if (!timer_expired(timer) && sd_send_command(drive, CMD58, 0) == 0) {
+                    for (n = 0; n < 4; n++) ocr[n] = sd_spi_receive_byte(drive);
+                    card_type = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;   /* SDv2 */
+                }
+            }
+        } else { /* SDSC or MMC */
+            if (sd_send_command(drive, ACMD41, 0) <= 1) {
+                /* SDv1 */
+                card_type = CT_SD1;
+                cmd = ACMD41;
+            } else {
+                /* MMCv3 */
+                card_type = CT_MMC;
+                cmd = CMD1;
+            }
+            /* Wait for leaving idle state */
+            while(!timer_expired(timer) && sd_send_command(drive, cmd, 0));
+            /* Set R/W block length to 512 */
+            if(timer_expired || sd_send_command(drive, CMD16, 512) != 0)
+                card_type = 0;
+        }
+    }
+    sd_spi_release(drive);
+
+    if (card_type) {
+        sd_spi_clock(drive, true); /* up to 20MHz should be OK */
+        return card_type;
+    }
+
+    return 0; /* failed */
+}
+
+static void sd_spi_release(uint8_t drive)
+{
+    sd_spi_raise_cs(drive);
+    sd_spi_receive_byte(drive);
+}
+
+static int sd_spi_wait_ready(uint8_t drive)
+{
+    unsigned char res;
+    timer_t timer;
+
+    timer = set_timer_ms(100);
+    sd_spi_receive_byte(drive);
+    do{
+        res = sd_spi_receive_byte(drive);
+        if(timer_expired(timer)){
+            kputs("sd_spi_wait_ready: timeout\n");
+            break;
+        }
+    }while ((res != 0xFF));
+
+    return res;
+}
+
+static bool sd_spi_transmit_block(uint8_t drive, void *ptr, unsigned int length)
+{
+    unsigned char reply;
+
+    if(sd_spi_wait_ready(drive) != 0xFF)
+        return false; /* failed */
+
+    sd_spi_transmit_byte(drive, 0xFE);
+    sd_spi_transmit_from_memory(drive, ptr, length);
+    sd_spi_transmit_byte(drive, 0xFF); /* dummy CRC */
+    sd_spi_transmit_byte(drive, 0xFF);
+    reply = sd_spi_receive_byte(drive);
+    if((reply & 0x1f) != 0x05)
+        return false; /* failed */
+    return true; /* hooray! */
+}
+
+static bool sd_spi_receive_block(uint8_t drive, void *ptr, unsigned int length)
+{
+    unsigned int timer;
+    unsigned char b;
+
+    timer = set_timer_ms(250);
+
+    do{
+        b = sd_spi_receive_byte(drive);
+        if(timer_expired(timer)){
+            kputs("sd_spi_receive_block: timeout\n");
+            return false;
+        }
+    }while(b == 0xFF);
+
+    if(b != 0xFE)
+        return false; /* failed */
+
+    return sd_spi_receive_to_memory(drive, ptr, length); /* returns true on success */
+}
+
+static int sd_send_command(uint8_t drive, unsigned char cmd, uint32_t arg)
+{
+    unsigned char n, res, *p;
+
+    if (cmd & 0x80) {   /* ACMD<n> is the command sequense of CMD55-CMD<n> */
+        cmd &= 0x7F;
+        res = sd_send_command(drive, CMD55, 0);
+        if (res > 1) 
+            return res;
+    }
+
+    /* Select the card and wait for ready */
+    sd_spi_raise_cs(drive);
+    sd_spi_lower_cs(drive);
+    if (sd_spi_wait_ready(drive) != 0xFF) 
+        return 0xFF;
+
+    /* Send command packet */
+    sd_spi_transmit_byte(drive, cmd);                        /* Start + Command index */
+#if 0
+    sd_spi_transmit_byte(drive, (unsigned char)(arg >> 24)); /* Argument[31..24] */
+    sd_spi_transmit_byte(drive, (unsigned char)(arg >> 16)); /* Argument[23..16] */
+    sd_spi_transmit_byte(drive, (unsigned char)(arg >> 8));  /* Argument[15..8] */
+    sd_spi_transmit_byte(drive, (unsigned char)arg);         /* Argument[7..0] */
+#else
+    /* sdcc sadly unable to figure this out for itself yet */
+    p = (unsigned char *)&arg;
+    sd_spi_transmit_byte(drive, p[3]);                       /* Argument[31..24] */
+    sd_spi_transmit_byte(drive, p[2]);                       /* Argument[23..16] */
+    sd_spi_transmit_byte(drive, p[1]);                       /* Argument[15..8] */
+    sd_spi_transmit_byte(drive, p[0]);                       /* Argument[7..0] */
+#endif
+    /* there's only a few commands (in native mode) that need correct CRCs */
+    n = 0x01;                                                /* Dummy CRC + Stop */
+    if (cmd == CMD0) n = 0x95;                               /* Valid CRC for CMD0(0) */
+    if (cmd == CMD8) n = 0x87;                               /* Valid CRC for CMD8(0x1AA) */
+    sd_spi_transmit_byte(drive, n);
+
+    /* Receive command response */
+    if (cmd == CMD12) 
+        sd_spi_receive_byte(drive);     /* Skip a stuff byte when stop reading */
+    n = 10;                             /* Wait for a valid response in timeout of 10 attempts */
+    do{
+        res = sd_spi_receive_byte(drive);
+    }while ((res & 0x80) && --n);
+
+    return res;         /* Return with the response value */
+}
+
+static uint32_t sd_get_size_sectors(uint8_t drive)
+{
+    unsigned char csd[16], n;
+    uint32_t sectors = 0;
+
+    if(sd_send_command(drive, CMD9, 0) == 0 && sd_spi_receive_block(drive, csd, 16)){
+        if ((csd[0] >> 6) == 1) {      /* SDC ver 2.00 */
+            sectors = ((uint32_t)csd[9] + (uint32_t)((unsigned int)csd[8] << 8) + 1) << 10;
+        } else {                                       /* SDC ver 1.XX or MMC*/
+            n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
+            sectors = (csd[8] >> 6) + ((unsigned int)csd[7] << 2) + ((unsigned int)(csd[6] & 3) << 10) + 1;
+            sectors = sectors << (n - 9);
+        }
+    }
+    sd_spi_release(drive);
+    return sectors;
+}
+
+static bool sd_read_sector(uint8_t drive, void *ptr, uint32_t lba)
+{
+    bool r = false;
+
+    if(sd_card_type[drive] != CT_NONE){
+        if(!(sd_card_type[drive] & CT_BLOCK))
+            lba = lba << 9; /* multiply by 512 to get byte address */
+
+        if(sd_send_command(drive, CMD17, lba) == 0)
+            r = sd_spi_receive_block(drive, ptr, 512);
+
+        sd_spi_release(drive);
+    }
+
+    return r;
+}
+
+static bool sd_write_sector(uint8_t drive, void *ptr, uint32_t lba)
+{
+    bool r = false;
+
+    if(sd_card_type[drive] != CT_NONE){
+        if(!(sd_card_type[drive] & CT_BLOCK))
+            lba = lba << 9; /* multiply by 512 to get byte address */
+
+        if(sd_send_command(drive, CMD24, lba) == 0)
+            r = sd_spi_transmit_block(drive, ptr, 512);
+
+        sd_spi_release(drive);
+    }
+
+    return r;
+}
diff --git a/Kernel/dev/devsd.h b/Kernel/dev/devsd.h
new file mode 100644 (file)
index 0000000..62a98a4
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef __DEVSD_DOT_H__
+#define __DEVSD_DOT_H__
+
+/* SD Configuration (in config.h)
+
+   Define DEVICE_SD if SD hardware is present on your platform.
+
+   Define SD_DRIVE_COUNT to the number of SD cards your hardware
+   supports (range 1--4).
+
+   Provide the platform-specific SPI functions listed below to
+   drive the SPI bus on your hardware, for an example of how to
+   do this see platform-n8vem-mark4/devsdspi.c
+*/
+
+
+/* public interface */
+int devsd_open(uint8_t minor, uint16_t flags);
+int devsd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int devsd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+void devsd_init(void);
+
+/* platform-specific SPI functions */
+void sd_spi_clock(uint8_t drive, bool go_fast); 
+void sd_spi_raise_cs(uint8_t drive);
+void sd_spi_lower_cs(uint8_t drive);
+void sd_spi_transmit_byte(uint8_t drive, uint8_t byte);
+uint8_t sd_spi_receive_byte(uint8_t drive);
+bool sd_spi_receive_to_memory(uint8_t drive, uint8_t *ptr, unsigned int length);
+bool sd_spi_transmit_from_memory(uint8_t drive, uint8_t *ptr, unsigned int length);
+
+/* Definitions for MMC/SDC command */
+#define CMD0    (0x40+0)    /* GO_IDLE_STATE */
+#define CMD1    (0x40+1)    /* SEND_OP_COND (MMC) */
+#define ACMD41  (0xC0+41)   /* SEND_OP_COND (SDC) */
+#define CMD8    (0x40+8)    /* SEND_IF_COND */
+#define CMD9    (0x40+9)    /* SEND_CSD */
+#define CMD10   (0x40+10)   /* SEND_CID */
+#define CMD12   (0x40+12)   /* STOP_TRANSMISSION */
+#define ACMD13  (0xC0+13)   /* SD_STATUS (SDC) */
+#define CMD16   (0x40+16)   /* SET_BLOCKLEN */
+#define CMD17   (0x40+17)   /* READ_SINGLE_BLOCK */
+#define CMD18   (0x40+18)   /* READ_MULTIPLE_BLOCK */
+#define CMD23   (0x40+23)   /* SET_BLOCK_COUNT (MMC) */
+#define ACMD23  (0xC0+23)   /* SET_WR_BLK_ERASE_COUNT (SDC) */
+#define CMD24   (0x40+24)   /* WRITE_BLOCK */
+#define CMD25   (0x40+25)   /* WRITE_MULTIPLE_BLOCK */
+#define CMD55   (0x40+55)   /* APP_CMD */
+#define CMD58   (0x40+58)   /* READ_OCR */
+
+#define CT_NONE 0x00
+#define CT_MMC 0x01
+#define CT_SD1 0x02
+#define CT_SD2 0x04
+#define CT_SDC (CT_SD1|CT_SD2)
+#define CT_BLOCK 0x08
+
+#endif /* __DEVSD_DOT_H__ */
index e6680a0..decc633 100644 (file)
@@ -1,5 +1,5 @@
-CSRCS += devices.c main.c devtty.c
-DSRCS = ../dev/devide.c ../dev/mbr.c
+CSRCS += devices.c main.c devtty.c devsdspi.c
+DSRCS = ../dev/devide.c ../dev/devsd.c ../dev/mbr.c
 
 ASRCS = crt0.s z180.s mark4.s commonmem.s
 
index 50248e1..f2a1102 100644 (file)
@@ -3,7 +3,7 @@ By Will Sowerbutts <will@sowerbutts.com>
 
 Assumes an N8VEM Mark IV SBC fitted with 512KB RAM, with the RS232 port as
 tty1, and the RS422 port as tty2. Does not yet support any ECB peripheral
-boards.
+boards. On-board IDE and SD mass storage interfaces are both supported.
 
 To build, edit ../Makefile to read:
 
index 2b6ce54..22377c4 100644 (file)
@@ -54,3 +54,6 @@
 #define IDE_REG_BASE       MARK4_IO_BASE
 #define IDE_8BIT_ONLY
 #define IDE_REG_CS1_FIRST
+
+#define DEVICE_SD
+#define SD_DRIVE_COUNT 1
index 2e861aa..ad3c585 100644 (file)
@@ -5,6 +5,7 @@
 #include <devsys.h>
 #include <devtty.h>
 #include <devide.h>
+#include <devsd.h>
 
 struct devsw dev_tab[] =  /* The device driver switch table */
 {
@@ -13,9 +14,9 @@ struct devsw dev_tab[] =  /* The device driver switch table */
   /* 0: /dev/hd                IDE-8 interface */
   {  devide_open, no_close, devide_read, devide_write, no_ioctl },
   /* 1: /dev/sd                SD interface */
-  {  no_open,     no_close,    no_rdwr,   no_rdwr,   no_ioctl },
+  {  devsd_open,  no_close, devsd_read,  devsd_write,  no_ioctl },
   /* 2: /dev/tty       serial ports */
-  {  tty_open,     tty_close,   tty_read,  tty_write,  tty_ioctl },
+  {  tty_open,    tty_close,   tty_read,  tty_write,  tty_ioctl },
   /* 3: /dev/lpr       Unused slot (pad to keep system devices at index 4) */
   {  no_open,     no_close,    no_rdwr,   no_rdwr,   no_ioctl },
   /* 4: /dev/mem etc   System devices (one offs) */
@@ -28,7 +29,7 @@ bool validdev(uint16_t dev)
     /* This is a bit uglier than needed but the right hand side is
        a constant this way */
     if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255)
-       return false;
+        return false;
     else
         return true;
 }
@@ -36,4 +37,5 @@ bool validdev(uint16_t dev)
 void device_init(void)
 {
     devide_init();
+    devsd_init();
 }
diff --git a/Kernel/platform-n8vem-mark4/devsdspi.c b/Kernel/platform-n8vem-mark4/devsdspi.c
new file mode 100644 (file)
index 0000000..ec0d074
--- /dev/null
@@ -0,0 +1,164 @@
+/*-----------------------------------------------------------------------*/
+/* N8VEM Mark IV Z180 CSI/O SPI SD driver                                */
+/* 2014-12-27 Will Sowerbutts                                            */
+/*-----------------------------------------------------------------------*/
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <timer.h>
+#include <stdbool.h>
+#include "config.h"
+#include <z180.h>
+
+#define CSIO_CNTR_TE           (1<<4)   /* transmit enable */
+#define CSIO_CNTR_RE           (1<<5)   /* receive enable */
+#define CSIO_CNTR_END_FLAG     (1<<7)   /* operation completed flag */
+
+#define MARK4_SD_CS            (1<<2)   /* chip select */ 
+#define MARK4_SD_WRITE_PROTECT (1<<4)   /* write protect */ 
+#define MARK4_SD_CARD_DETECT   (1<<5)   /* card detect */
+#define MARK4_SD_INT_ENABLE    (1<<6)   /* interrupt enable */
+#define MARK4_SD_INT_PENDING   (1<<7)   /* interrupt enable */
+
+__sfr __at (MARK4_IO_BASE + 0x09) MARK4_SD;
+
+/* the CSI/O and SD card send bits in opposite orders, so we need to flip them over */
+static uint8_t reverse_byte[256] = {
+    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+
+void sd_spi_clock(uint8_t drive, bool go_fast)
+{
+    unsigned char c;
+    drive; /* not used */
+
+    c = CSIO_CNTR & 0xf8; /* clear low three bits, gives fastest rate (clk/20) */
+    if(!go_fast)
+        c = c | 0x03;     /* set low two bits, clk/160 (can go down to clk/1280, see data sheet) */
+    CSIO_CNTR = c;
+}
+
+void sd_spi_raise_cs(uint8_t drive)
+{
+    drive; /* not used */
+    /* wait for idle */
+    while(CSIO_CNTR & (CSIO_CNTR_TE | CSIO_CNTR_RE));
+    MARK4_SD = MARK4_SD & (~MARK4_SD_CS);
+}
+
+void sd_spi_lower_cs(uint8_t drive)
+{
+    drive; /* not used */
+    /* wait for idle */
+    while(CSIO_CNTR & (CSIO_CNTR_TE | CSIO_CNTR_RE));
+    MARK4_SD = MARK4_SD | MARK4_SD_CS;
+}
+
+void sd_spi_transmit_byte(uint8_t drive, unsigned char byte)
+{
+    unsigned char c;
+    drive; /* not used */
+
+    /* reverse the bits before we busywait */
+    byte = reverse_byte[byte];
+
+    /* wait for any current transmit operation to complete */
+    do{
+        c = CSIO_CNTR;
+    }while(c & CSIO_CNTR_TE);
+
+    /* write the byte and enable transmitter */
+    CSIO_TRDR = byte;
+    CSIO_CNTR = c | CSIO_CNTR_TE;
+}
+
+uint8_t sd_spi_receive_byte(uint8_t drive)
+{
+    unsigned char c;
+    drive; /* not used */
+
+    /* wait for any current transmit or receive operation to complete */
+    do{
+        c = CSIO_CNTR;
+    }while(c & (CSIO_CNTR_TE | CSIO_CNTR_RE));
+
+    /* enable receive operation */
+    CSIO_CNTR = c | CSIO_CNTR_RE;
+
+    /* wait for receive to complete */
+    while(CSIO_CNTR & CSIO_CNTR_RE);
+
+    /* read byte */
+    return reverse_byte[CSIO_TRDR];
+}
+
+bool sd_spi_receive_to_memory(uint8_t drive, uint8_t *ptr, unsigned int length)
+{
+    unsigned char c;
+    drive; /* not used */
+
+    /* wait for any current transmit or receive operation to complete */
+    do{
+        c = CSIO_CNTR;
+    }while(c & (CSIO_CNTR_TE | CSIO_CNTR_RE));
+
+    while(length){
+        /* enable receive operation */
+        CSIO_CNTR = c | CSIO_CNTR_RE;
+
+        /* wait for receive to complete */
+        do{
+            c = CSIO_CNTR;
+        }while(c & CSIO_CNTR_RE);
+
+        /* read byte */
+        *ptr = reverse_byte[CSIO_TRDR];
+
+        /* next byte */
+        ptr++;
+        length--;
+    }
+
+    return true;
+}
+
+bool sd_spi_transmit_from_memory(uint8_t drive, uint8_t *ptr, int length)
+{
+    unsigned char c, b;
+    drive; /* not used */
+
+    while(length){
+        b = reverse_byte[*ptr];
+
+        /* wait for transmit to complete */
+        do{
+            c = CSIO_CNTR;
+        }while(c & CSIO_CNTR_TE);
+
+        /* write the byte and enable transmitter */
+        CSIO_TRDR = b;
+        CSIO_CNTR = c | CSIO_CNTR_TE;
+
+        /* next byte */
+        ptr++;
+        length--;
+    }
+    return true;
+}
index 6d10b67..31c4371 100644 (file)
@@ -33,5 +33,7 @@ swap.rel
 devsys.rel
 platform-n8vem-mark4/devtty.rel
 platform-n8vem-mark4/devide.rel
+platform-n8vem-mark4/devsd.rel
+platform-n8vem-mark4/devsdspi.rel
 platform-n8vem-mark4/mbr.rel
 -e