Paul,
I'm resending the patch, supposedly as plain text. Let me know if you still see a problem.
Thanks, Dave
From fa9dc2154278b428af16899e1688b3e1b1aabcf7 Mon Sep 17 00:00:00 2001
From: David Babich david.babich@se-eng.com Date: Thu, 22 May 2014 16:14:29 -0600 Subject: [PATCH] Add SD card support
Updated initial commit of SD card boot support. This has been tested with SDHC cards booting Puppy linux. SDSC and SDXC cards may not work. SD cards pre 1.01 of the SD spec are not supported if they use byte addressing and/or block addressing with a block size not equal to 512 Bytes. Also, the drivers are written to support boot, but not necessarily for optimal speed. The driver supports read and not write, and only implements functions that are required for boot. Some of the source is refactored FreeBSD code.
Signed-off-by: David Babich david.babich@se-eng.com --- Makefile | 3 +- src/Kconfig | 10 + src/block.c | 33 ++- src/block.h | 2 + src/config.h | 1 + src/hw/bar.h | 65 +++++ src/hw/sd.c | 613 ++++++++++++++++++++++++++++++++++++++++++++ src/hw/sd.h | 275 ++++++++++++++++++++ src/hw/sd_if.c | 413 ++++++++++++++++++++++++++++++ src/hw/sd_if.h | 49 ++++ src/hw/sd_utils.c | 190 ++++++++++++++ src/hw/sd_utils.h | 108 ++++++++ src/hw/sdhc_generic.c | 697 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/hw/sdhc_generic.h | 41 +++ src/hw/sdhci.h | 207 +++++++++++++++ src/post.c | 3 + src/stdbool.h | 29 +++ src/stdint.h | 41 +++ 18 files changed, 2778 insertions(+), 2 deletions(-) create mode 100644 src/hw/bar.h create mode 100644 src/hw/sd.c create mode 100644 src/hw/sd.h create mode 100644 src/hw/sd_if.c create mode 100644 src/hw/sd_if.h create mode 100644 src/hw/sd_utils.c create mode 100644 src/hw/sd_utils.h create mode 100644 src/hw/sdhc_generic.c create mode 100644 src/hw/sdhc_generic.h create mode 100644 src/hw/sdhci.h create mode 100644 src/stdbool.h create mode 100644 src/stdint.h
diff --git a/Makefile b/Makefile index 78b598e..02266ce 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,8 @@ SRCBOTH=misc.c stacks.c output.c string.c x86.c block.c cdrom.c mouse.c kbd.c \ hw/usb-hid.c hw/usb-msc.c hw/usb-uas.c \ hw/blockcmd.c hw/floppy.c hw/ata.c hw/ramdisk.c \ hw/virtio-ring.c hw/virtio-pci.c hw/virtio-blk.c hw/virtio-scsi.c \ - hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c + hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c \ + hw/sdhc_generic.c hw/sd.c hw/sd_if.c hw/sd_utils.c SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c pmm.c romfile.c optionroms.c \ boot.c bootsplash.c jpeg.c bmp.c \ diff --git a/src/Kconfig b/src/Kconfig index a863866..432eca5 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -1,4 +1,5 @@ # Kconfig SeaBIOS configuration +# Copyright (C) 2013-2014 Sage Electronic Engineering, LLC
mainmenu "SeaBIOS Configuration"
@@ -207,6 +208,15 @@ menu "Hardware support" default y help Support floppy drive access. + config SD + depends on DRIVES + bool "SD controller" + default n + help + Support SD card boot. This is a development feature. + Select this option ONLY if you know that your hardware + will support booting from SD card. + If unsure, say n.
config PS2PORT depends on KEYBOARD || MOUSE diff --git a/src/block.c b/src/block.c index 264f376..615edd1 100644 --- a/src/block.c +++ b/src/block.c @@ -2,6 +2,7 @@ // // Copyright (C) 2008,2009 Kevin O'Connor kevin@koconnor.net // Copyright (C) 2002 MandrakeSoft S.A. +// Copyright (C) 2013 Sage Electronic Engineering, LLC // // This file may be distributed under the terms of the GNU LGPLv3 license.
@@ -13,6 +14,7 @@ #include "hw/blockcmd.h" // cdb_* #include "hw/rtc.h" // rtc_read #include "hw/virtio-blk.h" // process_virtio_blk_op +#include "hw/sd_if.h" // process_sd_op #include "malloc.h" // malloc_low #include "output.h" // dprintf #include "stacks.h" // stack_hop @@ -20,9 +22,12 @@ #include "string.h" // checksum #include "util.h" // process_floppy_op
+#define MAX_DRIVE_TYPES 4 + u8 FloppyCount VARFSEG; u8 CDCount; -struct drive_s *IDMap[3][BUILD_MAX_EXTDRIVE] VARFSEG; + +struct drive_s *IDMap[MAX_DRIVE_TYPES][BUILD_MAX_EXTDRIVE] VARFSEG; u8 *bounce_buf_fl VARFSEG; struct dpte_s DefaultDPTE VARLOW;
@@ -278,6 +283,27 @@ map_floppy_drive(struct drive_s *drive) } }
+// Map a SD card +void +map_sd_drive(struct drive_s *drive_g) +{ + ASSERT32FLAT(); + struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0); + int sdid = bda->hdcount; + printf("Mapping sd/mmc drive 0x%08x\n", (unsigned int)drive_g); + add_drive(IDMap[EXTTYPE_HD], &bda->hdcount, drive_g); + + // Setup disk geometry translation. + setup_translation(drive_g); + + //@NOTE: This step appears to be unnecessary for booting from the sd card as the pchs info does not get used... + drive_g->pchs.head = drive_g->lchs.head; + drive_g->pchs.cylinder = drive_g->lchs.cylinder; + drive_g->pchs.sector = drive_g->lchs.sector; + + // Fill "fdpt" structure. + fill_fdpt(drive_g, sdid); +}
/**************************************************************** * Return status functions @@ -395,6 +421,11 @@ process_op(struct disk_op_s *op) ret = call32(_cfunc32flat_process_scsi_op , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); break; + case DTYPE_SD: ; + extern void _cfunc32flat_process_sd_op(void); + ret = call32(_cfunc32flat_process_sd_op + , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); + break; default: ret = DISK_RET_EPARAM; break; diff --git a/src/block.h b/src/block.h index 5d0afb5..ee20218 100644 --- a/src/block.h +++ b/src/block.h @@ -86,6 +86,7 @@ struct drive_s { #define DTYPE_ESP_SCSI 0x81 #define DTYPE_MEGASAS 0x82 #define DTYPE_PVSCSI 0x83 +#define DTYPE_SD 0x90
#define MAXDESCSIZE 80
@@ -115,6 +116,7 @@ int getDriveId(u8 exttype, struct drive_s *drive); void map_floppy_drive(struct drive_s *drive); void map_hd_drive(struct drive_s *drive); void map_cd_drive(struct drive_s *drive); +void map_sd_drive(struct drive_s *drive); struct bregs; void __disk_ret(struct bregs *regs, u32 linecode, const char *fname); void __disk_ret_unimplemented(struct bregs *regs, u32 linecode diff --git a/src/config.h b/src/config.h index d705615..73fbffe 100644 --- a/src/config.h +++ b/src/config.h @@ -99,6 +99,7 @@ #define DEBUG_HDL_pmm 1 #define DEBUG_HDL_pcibios 9 #define DEBUG_HDL_apm 9 +#define DEBUG_HDL_SD 6
#define DEBUG_unimplemented 2 #define DEBUG_invalid 3 diff --git a/src/hw/bar.h b/src/hw/bar.h new file mode 100644 index 0000000..171525b --- /dev/null +++ b/src/hw/bar.h @@ -0,0 +1,65 @@ + /***************************************************************************** + * + * Copyright (c) 2012-2014 Sage Electronic Engineering. All rights reserved. + * + * Software License Agreement + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * Sage Electronic Engineering SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR + * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + *****************************************************************************/ + +#ifndef __BAR_H +#define __BAR_H + +#include <stdint.h> + +// write functions +static inline void barWrite8( uint32_t bar, uint32_t offset, uint8_t value ) +{ + volatile uint8_t* pReg = (volatile uint8_t*)(bar | ( offset & 0xFF ) ); + *pReg = value; +} + +static inline void barWrite16( uint32_t bar, uint32_t offset, uint16_t value ) +{ + volatile uint16_t* pReg = (volatile uint16_t*)(bar | ( offset & 0xFF ) ); + *pReg = value; +} + +static inline void barWrite32( uint32_t bar, uint32_t offset, uint32_t value ) +{ + volatile uint32_t* pReg = (volatile uint32_t*)(bar | ( offset & 0xFF ) ); + *pReg = value; +} + +// read functions +static inline uint8_t barRead8( uint32_t bar, uint32_t offset ) +{ + volatile uint8_t* pReg = (volatile uint8_t*)(bar | ( offset & 0xFF ) ); + return( *pReg ); +} + +static inline uint16_t barRead16( uint32_t bar, uint32_t offset ) +{ + volatile uint16_t* pReg = (volatile uint16_t*)(bar | ( offset & 0xFF ) ); + return( *pReg ); +} + +static inline uint32_t barRead32( uint32_t bar, uint32_t offset ) +{ + volatile uint32_t* pReg = (volatile uint32_t*)(bar | ( offset & 0xFF ) ); + return( *pReg ); +} + +#endif /* __BAR_H */ diff --git a/src/hw/sd.c b/src/hw/sd.c new file mode 100644 index 0000000..ac4c3b3 --- /dev/null +++ b/src/hw/sd.c @@ -0,0 +1,613 @@ + /***************************************************************************** + * + * Copyright (c) 2012-2014 Sage Electronic Engineering. All rights reserved. + * + * Software License Agreement + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * Sage Electronic Engineering SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR + * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + *****************************************************************************/ + +#include <stdint.h> +#include <stdbool.h> +#include "string.h" +#include "util.h" +#include "malloc.h" +#include "output.h" +#include "config.h" +#include "sd.h" + +// Host controller access functions cmd/data and acmd +static bool sdHostXfer( sdHc_t* pHost, sdXfer_t* pXfer ); +static bool sdAppSpecificHostXfer( sdHc_t* pHost, sdXfer_t* pXfer ); + +// Card control functions +static bool sdSelectDeselectCard( sdCard_t* pCard ); + +// Card intialization functions +static bool sdIdle( sdCard_t* pCard ); +static bool sdSendIfCond( sdCard_t* pCard ); +static bool sdSendOpCond( sdCard_t* pCard ); +static bool sdSendCSD( sdCard_t* pCard ); +static bool sdAllSendCID( sdCard_t* pCard ); +static bool sdSendRelativeAddr( sdCard_t* pCard ); +static bool sdCardIdentificationMode( sdCard_t* pCard ); + + +/** + * @brief sdHostXfer - execute the host controller callback to perform + * a transfer from the host to the card and read responses and + * data from the card + * + * @param sdHc_t* pHost - pointer to the host controller abstraction structure + * @param sdXfer_t* pXfer - pointer to the host style transfer structure + * + * @return bool - true if successful, false otherwise + */ +static bool sdHostXfer( sdHc_t* pHost, sdXfer_t* pXfer ) +{ + bool status = false; + + dprintf( DEBUG_HDL_SD, + "SD card: Sending CMD%u with arg1 0x%08x\n", pXfer->cmdIdx, pXfer->arg1 ); + + status = pHost->sdhcCmd( pHost, pXfer ); + if( status ) + { + switch( pXfer->rspType ) + { + case eRsp136: + dprintf( DEBUG_HDL_SD, + "SD card: Response 136: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + pXfer->response[0], + pXfer->response[1], + pXfer->response[2], + pXfer->response[3] ); + break; + + case eRsp48: + dprintf( DEBUG_HDL_SD, "SD card: Response 48: 0x%08x\n", + pXfer->response[0] ); + break; + + case eRsp48_busy: + dprintf( DEBUG_HDL_SD, "SD card: Response 48 with busy signal: 0x%08x\n", + pXfer->response[0] ); + break; + + case eRspNone: + default: + break; + + } + } + else + { + dprintf( DEBUG_HDL_SD, "SD card: Failed to respond to CMD%u\n", pXfer->cmdIdx ); + } + + return status; +} + +/** + * @brief sdAppSpecificHostXfer - this function performs the necessary steps to + * allow the sd card to accept application specific commands (namely, it + * sends CMD55 prior to sending the ACMD<XX>. CMD55 puts the card into + * application specific mode. + * + * @param sdHc_t* pHost - pointer to the host controller abstraction structure + * @param sdXfer_t* pXfer - pointer to the host style transfer structure + * + * @return bool - true if successful, false otherwise + */ +static bool sdAppSpecificHostXfer( sdHc_t* pHost, sdXfer_t* pXfer ) +{ + bool status = false; + sdXfer_t xfer; + + memset( &xfer, 0, sizeof( sdXfer_t ) ); + xfer.cmdIdx = MMC_APP_CMD55; + xfer.arg1 = 0; + xfer.cmdType = eCmdNormal; + xfer.rspType = eRsp48; + xfer.xferType = eWrXfer; + xfer.pData = NULL; + + // send CMD55 to place card in APP specific mode + status = sdHostXfer( pHost, &xfer ); + if( status ) + { + // send APP specific cmd + status = sdHostXfer( pHost, pXfer ); + } + + return status; +} + +/** + * @brief sdSelectDeselectCard - this function impelements CMD7 SELECT/DESELECT_CARD + * to toggle a card between standby and transfer state + * + * @param sdCard_t* pCard - pointer to the sd card abstraction structure + * + * @return bool - true if successful, false otherwise + */ +static bool sdSelectDeselectCard( sdCard_t* pCard ) +{ + bool status = false; + sdXfer_t xfer; + + // set up a transaction structure + memset( &xfer, 0, sizeof( sdXfer_t ) ); + xfer.cmdIdx = MMC_SELECT_CARD_CMD7; + xfer.arg1 = (uint32_t)(pCard->rca) << 16; + xfer.cmdType = eCmdNormal; + xfer.rspType = eRsp48_busy; + xfer.xferType = eWrXfer; + xfer.pData = NULL; + + // invoke the host controller callback + status = sdHostXfer( pCard->pHost, &xfer ); + + if(status) + { + pCard->isSelected = pCard->isSelected == true ? false : true; + } + + return status; +} + +/** + * @brief sdReadSingleBlock - read a single block from the card + * + * @param sdCard_t* pCard - pointer to the sd card abstraction structure + * @param uint8_t* pData - pointer to the data buffer to read into + * @param uint32_t addr - logical block address to read + * + * @return bool - true on success false otherwise + */ +bool sdReadSingleBlock( sdCard_t* pCard, uint8_t* pData, uint32_t addr ) +{ + bool status = false; + sdXfer_t xfer; + + if( !pCard->isSelected ) + { + // select the card (CMD7) + sdSelectDeselectCard( pCard ); + } + + // set up a transaction structure + memset( &xfer, 0, sizeof( sdXfer_t ) ); + xfer.cmdIdx = MMC_READ_SINGLE_BLOCK_CMD17; + xfer.arg1 = addr; + xfer.cmdType = eCmdNormal; + xfer.rspType = eRsp48; + xfer.xferType = eRdXfer; + xfer.pData = pData; + dprintf( DEBUG_HDL_SD, "---- pData address: 0x%08x ----\n", (uint32_t)pData); + + // invoke the host controller callback + status = sdHostXfer( pCard->pHost, &xfer ); + + return status; +} + +bool sdReadMultipleBlock( sdCard_t* pCard ) +{ + bool status = false; + // stub + return status; +} + +bool sdStopTransmission( sdCard_t* pCard ) +{ + bool status = false; + // stub + return status; +} + +/** + * @brief sdIdle - this function implements the CMD0 GO_IDLE_MODE, + * which resets the sd card and prepares it for intalization + * + * @param sdCard_t* pCard - pointer to the sd card abstraction structure + * + * @return bool - true if successful, false otherwise + */ +static bool sdIdle( sdCard_t* pCard ) +{ + bool status = false; + sdXfer_t xfer; + + // set up a transaction structure + memset( &xfer, 0, sizeof( sdXfer_t ) ); + xfer.cmdIdx = MMC_GO_IDLE_STATE_CMD0; + xfer.arg1 = 0; + xfer.cmdType = eCmdNormal; + xfer.rspType = eRspNone; + xfer.xferType = eWrXfer; + xfer.pData = NULL; + + // invoke the host controller callback + status = sdHostXfer( pCard->pHost, &xfer ); + + return status; +} + +/** + * @brief sdSendIfCond - this function executes CMD8 SEND_IF_COND, to determine + * whether or not the host controller is compatible with the sd card. + * + * @param sdCard_t* pCard - pointer to the sd card abstraction structure + * + * @return bool - true if successful, false otherwise + */ +static bool sdSendIfCond( sdCard_t* pCard ) +{ + bool status = false; + sdXfer_t xfer; + uint32_t cycleTries = 1; + + /* Send CMD8 to determine whether or not the host controller's voltage setting + * is correct for the card + * NOTE: it should always be correct for SD cards, as there is really only one + * voltage range currently supported as of 2013. This may change in the future + * though. If this fails, the card is not an SD, SDCH or SDXC card and the card + * is unusable, or the host controller is not setup correctly for the card. + */ + // set up a transaction structure + memset( &xfer, 0, sizeof( sdXfer_t ) ); + xfer.cmdIdx = SD_SEND_IF_COND_CMD8; + + // voltage range 2.7 to 3.6 v and test pattern = 0xAA + xfer.arg1 = SD_VOLTAGE_RANGE_270_360 | SD_IF_COND_ECHO; + xfer.cmdType = eCmdNormal; + xfer.rspType = eRsp48; + xfer.xferType = eWrXfer; + xfer.pData = NULL; + + // check the echo response + while( cycleTries-- ) + { + if( sdHostXfer( pCard->pHost, &xfer ) ) + { + if( xfer.response[0] == xfer.arg1 ) + { + status = true; + break; + } + usleep(100); + } + } + if( !status ) + { + dprintf( 6, "SD: card not present or not supported in the present operating conditions\n" ); + } + return status; +} + +/** + * @brief sdSendOpCond - this function executes the ACMD41 SEND_OP_COND, this function + * transitions the card from the idle state to the ready state. This transition + * requires a minimum of one second to complete, and is required for sdxc and sdhc + * card enumeration. + * + * @param sdCard_t* pCard - pointer to the sd card abstraction structure + * + * @return bool - true if successful, false otherwise + */ +static bool sdSendOpCond( sdCard_t* pCard ) +{ + bool status = false; + sdXfer_t xfer; + uint32_t cycleTries = SD_STATE_CHANGE_ATTEMPTS; + + // Send Application specific command ACMD41 in inquiry mode + // (no voltage bits set) to read the OCR + memset( &xfer, 0, sizeof( sdXfer_t ) ); + xfer.cmdIdx = SD_APP_SEND_OP_COND_CMD41; + xfer.cmdType = eCmdNormal; + xfer.rspType = eRsp48; + xfer.xferType = eWrXfer; + xfer.pData = NULL; + + // setup the voltages based on what is supported by the host + xfer.arg1 |= pCard->pHost->cardCapabilities.cap1 & SDHCI_CAN_VDD_180 ? VDD_S18A : 0; + xfer.arg1 |= pCard->pHost->cardCapabilities.cap1 & SDHCI_CAN_VDD_300 ? VDD_RANGE_27_30 : 0; + xfer.arg1 |= pCard->pHost->cardCapabilities.cap1 & SDHCI_CAN_VDD_330 ? VDD_RANGE_30_33 : 0; + + // normal sd cards ignore the HCS bit, so set it for hcxc types + xfer.arg1 |= HCS; + + while( cycleTries-- ) + { + status = sdAppSpecificHostXfer( pCard->pHost, &xfer ); + if( status ) + { + // initialization takes 1 second to complete, and we query the busy bit + // in the response to know when to stop + if( xfer.response[0] & OCR_DONE_BSY_N ) + { + pCard->ocr = xfer.response[0]; + pCard->xchcCard = (HCS == (pCard->ocr & HCS)); + break; + } + else + { + udelay(100); + } + } + } + return status; +} + +/** + * @brief sdSendCSD - request the values from CSD register + * + * @param sdCard_t* pCard - pointer to the sd card abstraction structure + * + * @return bool - true if successful, false otherwise + */ +static bool sdSendCSD( sdCard_t* pCard ) +{ + bool status = false; + sdXfer_t xfer; + uint32_t cycleTries = SD_TRY_AGAIN; + + memset( &xfer, 0, sizeof( sdXfer_t ) ); + xfer.cmdIdx = MMC_SEND_CSD_CMD9; + xfer.arg1 = (uint32_t)(pCard->rca) << 16; + xfer.cmdType = eCmdNormal; + xfer.rspType = eRsp136; + xfer.xferType = eWrXfer; + xfer.pData = NULL; + while( cycleTries-- ) + { + status = sdHostXfer( pCard->pHost, &xfer ); + if( status ) + { + // The Host Response places bits [127:8] in bits [119:0] of the response (hence the shift) + memmove( xfer.response, (void*)(((uint8_t*)(xfer.response)) - 1), sizeof(xfer.response ) ); + pCard->csd[0] = xfer.response[3]; + pCard->csd[1] = xfer.response[2]; + pCard->csd[2] = xfer.response[1]; + pCard->csd[3] = xfer.response[0]; + decode_csd_sd( pCard->csd, &pCard->decode.csdDecode ); + break; + } + + } + return status; +} + +/** + * @brief sdAllSendCID - this function executes CMD2 ALL_SEND_CID, this transitions + * the card from the ready state to the identification state, and allows the + * card to transmit the contents of the CID register + * + * @param sdCard_t* pCard - pointer to the sd card abstraction structure + * + * @return bool - true if successful, false otherwise + */ +static bool sdAllSendCID( sdCard_t* pCard ) +{ + bool status = false; + sdXfer_t xfer; + uint32_t cycleTries = SD_TRY_AGAIN; + + memset( &xfer, 0, sizeof( sdXfer_t ) ); + xfer.cmdIdx = MMC_ALL_SEND_CID_CMD2; + xfer.cmdType = eCmdNormal; + xfer.rspType = eRsp136; + xfer.xferType = eWrXfer; + xfer.pData = NULL; + while( cycleTries-- ) + { + status = sdHostXfer( pCard->pHost, &xfer ); + if( status ) + { + // The Host Response places bits [127:8] in bits [119:0] of the response (hence the shift) + memmove( xfer.response, (void*)(((uint8_t*)(xfer.response)) - 1), sizeof(xfer.response ) ); + + // the utility functions swap the result prior to use + pCard->cid[0] = xfer.response[3]; + pCard->cid[1] = xfer.response[2]; + pCard->cid[2] = xfer.response[1]; + pCard->cid[3] = xfer.response[0]; + decode_cid_sd( pCard->cid, &pCard->decode.cidDecode ); + break; + } + + } + return status; + +} + +/** + * @brief sdSendRelativeAddr - this function executes CMD3 - SEND_RELATIVE_ADDRESS, of the + * card initialization sequence. After successful completion, the relative card + * address (RCA) of the card is known, and the SD card enters standby mode from + * card identification mode + * + * @param sdCard_t* pCard - pointer to the sd card abstraction structure + * + * @return bool - true if successful, false otherwise + */ +static bool sdSendRelativeAddr( sdCard_t* pCard ) +{ + bool status = false; + sdXfer_t xfer; + uint32_t cycleTries = SD_TRY_AGAIN; + sdCardState_e currentState = eInvalid; + + memset( &xfer, 0, sizeof( sdXfer_t ) ); + xfer.cmdIdx = MMC_SET_RELATIVE_ADDR_CMD3; + xfer.cmdType = eCmdNormal; + xfer.rspType = eRsp48; + xfer.xferType = eWrXfer; + xfer.pData = NULL; + while( cycleTries-- ) + { + status = sdHostXfer( pCard->pHost, &xfer ); + if( status ) + { + // the response is the R6 response containing the relative card + // address, and current status info + pCard->rca = (uint16_t)(xfer.response[0] >> 16); + if( xfer.response[0] & RCAERROR_MSK ) + { + dprintf( DEBUG_HDL_SD, + "SD card: RCA request responded with error status: 0x%08x\n", + xfer.response[0] & RCAERROR_MSK ); + status = false; + // here there should probably be a full status check + } + + // now the card should be in stby mode (this might not be reflected until + // the next command (the offset is bit 9 : 12 for state info) + currentState = (sdCardState_e)((xfer.response[0] & RCASTATUS_CURRENT_STATE) >> 9); + if( currentState == eStby ) + { + dprintf( DEBUG_HDL_SD, "SD card: Current state is stby: 0x%02x - \n", currentState ); + break; + } + } + } + return status; + +} + +/** + * @brief sdCardIdentificationMode - this function executes the call sequence/state + * machine outlined in section 4.2 of "Physical Layer Simplified Specification + * Version 4.10" + * + * @param sdCard_t* pCard - pointer to the sd card abstraction structure + * + * @return bool - true if successful, false otherwise + */ +static bool sdCardIdentificationMode( sdCard_t* pCard ) +{ + bool status = false; + uint32_t cycleTries = NUM_INIT_ATTEMPTS; + + ASSERT32FLAT(); + while( cycleTries-- ) + { + // CMD8 - SEND_IF_COND + if( !(status = sdSendIfCond( pCard )) ) + continue; + + // ACMD41 - SEND_OP_COND to properly initialize SDHC and SDXC cards... + if( !(status = sdSendOpCond( pCard )) ) + continue; + + // If the voltage needs to change based on the response for ACMD41, change it using CMD11 + // @TODO: implement CMD11 voltage switch if necessary... probably don't need to support this + // for a while + + // Else Send CMD2 to get the CID, the card should be in identification state + if( !(status = sdAllSendCID( pCard )) ) + continue; + + // Send CMD3 to get the relative card address (RCA) used for broad cast commands + if( !(status = sdSendRelativeAddr( pCard )) ) + continue; + + // Send CMD9 to get the CSD register info + if( !(status = sdSendCSD( pCard )) ) + continue; + + // the card will now be in standby state, and is ready for data transfers + if( status == true ) + { + dprintf( DEBUG_HDL_SD, "SD card: Initialization of sd card is complete:\n" ); + dprintf( DEBUG_HDL_SD, " - currrent card state: standby\n"); + dprintf( DEBUG_HDL_SD, " - RCA: 0x%04x\n", pCard->rca ); + dprintf( DEBUG_HDL_SD, " - OCR: 0x%08x\n", pCard->ocr ); + dprintf( DEBUG_HDL_SD, + " - CID: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + pCard->cid[0], pCard->cid[1], pCard->cid[2], pCard->cid[3] ); + dprintf( DEBUG_HDL_SD, + " - CSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + pCard->csd[0], pCard->csd[1], pCard->csd[2], pCard->csd[3] ); + dprintf( DEBUG_HDL_SD, " - card type is "); + if( pCard->xchcCard ) + { + dprintf( DEBUG_HDL_SD, "SDXC/SDHC\n" ); + } + else + { + dprintf( DEBUG_HDL_SD, "Normal SD\n" ); + } + + + break; + } + } + return status; +} + +/** + * @brief sdCardBusInit - public function to initialize the SD card-bus. + * This function is called by the host controller after the host + * controller has completed its initial "pessimistic" configuration. + * This function implements the steps in the sequence diagram of figure + * 4-1 of the "Physical Layer Simplified Specification Version 4.10" of the + * SD card specification documents. + * + * @param sdHc_t* hc - pointer to the host controller structure + * + * @return bool - status, true = success, false = failure of the card bus initialization + */ +bool sdCardBusInit( sdHc_t* pHc ) +{ + bool status = false; + + // allocate the card + sdCard_t* pCard = malloc_fseg( sizeof( sdCard_t ) ); + if( !pCard ) + { + dprintf( DEBUG_HDL_SD, "SD: card failed to allocate\n" ); + return status; + } + dprintf( DEBUG_HDL_SD, "SD: pCard address: 0x%08x\n", (uint32_t)pCard); + + // set the host pointer + pCard->pHost = pHc; + + // Set Idle Mode with CMD0 + status = sdIdle( pCard ); + + if( status ) + { + // execute the initialization/identification procedure + pCard->cardInitialized = sdCardIdentificationMode( pCard ); + } + + if( !pCard->cardInitialized ) + { + dprintf( DEBUG_HDL_SD, "SD: Card init failed, check card...\n"); + status = pCard->cardInitialized; + free( pCard ); + } + else + { + // If we get here, the card is in standby mode, so give a + // reference to the host controller + pHc->pCard = pCard; + } + return status; +} diff --git a/src/hw/sd.h b/src/hw/sd.h new file mode 100644 index 0000000..bcf4d94 --- /dev/null +++ b/src/hw/sd.h @@ -0,0 +1,275 @@ + /***************************************************************************** + * + * Copyright (c) 2012-2014 Sage Electronic Engineering. All rights reserved. + * + * Software License Agreement + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * Sage Electronic Engineering SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR + * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + *****************************************************************************/ + +#ifndef __SD_H +#define __SD_H + +#include <stdint.h> +#include <stdbool.h> +#include "sd_utils.h" + +/** @file sd.h */ + +#include "sdhci.h" + +/** @brief sd host capabilities registers */ +typedef struct +{ + uint32_t cap1; + uint32_t cap2; +}capabilities_t; + +typedef enum +{ + eCmdNormal = SDHCI_CMD_TYPE_NORMAL, + eCmdSuspend = SDHCI_CMD_TYPE_SUSPEND, + eCmdResume = SDHCI_CMD_TYPE_RESUME, + eCmdAbort = SDHCI_CMD_TYPE_ABORT +}sdCmdType_e; + +typedef enum +{ + eRspNone = SDHCI_CMD_RESP_NONE, + eRsp136 = SDHCI_CMD_RESP_LONG, + eRsp48 = SDHCI_CMD_RESP_SHORT, + eRsp48_busy = SDHCI_CMD_RESP_SHORT_BUSY +}sdRspType_e; + +typedef enum +{ + eSdSpecV_1_00 = 0, + eSdSpecV_2_00, + eSdSpecV_3_00 +}sdSpecVer_e; + +typedef enum +{ + eRdXfer = 0, + eWrXfer +}sdXferType_e; + +typedef struct +{ + bool idxChkEn; + bool crcChkEn; +}sdXferFlags_t; + +/** @brief struct used for host to card and card to host transfers */ +typedef struct +{ + uint8_t cmdIdx; + uint32_t arg1; + sdCmdType_e cmdType; + sdRspType_e rspType; + sdXferType_e xferType; + uint32_t response[4]; + bool rspValid; + void* pData; +}sdXfer_t; + +/** @brief struct to hold decoded CID and CSD registers (see SD Specification) */ +typedef struct +{ + sd_cid_t cidDecode; + sd_csd_t csdDecode; +}sdRegDecode_t; + +// resolve forward declarations +struct sdCard_t; +typedef struct sdCard_t sdCard_t; + +typedef struct sdHc_t +{ + uint32_t barAddress; + capabilities_t cardCapabilities; + uint32_t maxClk; + uint32_t tmoClk; + uint32_t curClk; + uint32_t blkSize; + uint32_t pwrMode; + bool crcCheckEnable; + bool indexCheckEnable; + bool xchcSupported; + bool isInitialized; + uint8_t hostVendorId; + uint8_t hostSpecId; + sdCard_t* pCard; + + // call back to host for sending commands to the card via the host controller interface + bool (*sdhcCmd)( struct sdHc_t* pSdCtrl, sdXfer_t* xfer ); +}sdHc_t; + +/** + * SD Memory Card Registers + */ +typedef struct sdCard_t +{ + sdHc_t* pHost; + uint32_t cid[4]; + uint16_t rca; + uint16_t dsr; + uint32_t csd[4]; + uint32_t scr[2]; + uint32_t ocr; + uint32_t ssr[16]; + uint32_t csr; + sdRegDecode_t decode; + bool xchcCard; + bool cardInitialized; + bool isSelected; +}sdCard_t; + +#define BLOCK_SIZE8 512 +#define MHZ 1000000 +#define KHZ 1000 +#define BLOCK_MASK 0x00000FFF + +// MMC Card Commands that mostly overlap with the SD specification, some have slightly different meaning or results +#define MMC_GO_IDLE_STATE_CMD0 0 +#define MMC_SEND_OP_COND_CMD1 1 +#define MMC_ALL_SEND_CID_CMD2 2 +#define MMC_SET_RELATIVE_ADDR_CMD3 3 +#define MMC_SET_DSR_CMD4 4 +#define MMC_SWITCH_CMD6 6 +#define MMC_SELECT_CARD_CMD7 7 +#define MMC_SEND_EXT_CSD_CMD8 8 +#define MMC_SEND_CSD_CMD9 9 +#define MMC_SEND_CID_CMD10 10 +#define MMC_STOP_TRANSMISSION_CMD12 12 +#define MMC_SEND_STATUS_CMD13 13 +#define MMC_SET_BLOCKLEN_CMD16 16 +#define MMC_READ_SINGLE_BLOCK_CMD17 17 +#define MMC_READ_MULTIPLE_BLOCK_CMD18 18 +#define MMC_WRITE_SINGLE_BLOCK_CMD24 24 +#define MMC_WRITE_MULTIPLE_BLOCK_CMD25 25 +#define MMC_ERASE_GROUP_START_CMD35 35 +#define MMC_ERASE_GROUP_END_CMD36 36 +#define MMC_ERASE_CMD38 38 +#define MMC_APP_CMD55 55 +#define MMC_SPI_READ_OCR_CMD58 58 +#define MMC_SPI_CRC_ON_OFF_CMD59 59 + +// SD card specific commands +#define SD_SEND_RELATIVE_ADDR_CMD3 3 +#define SD_SWITCH_FUNC_CMD6 6 +#define SD_SEND_IF_COND_CMD8 8 +#define SD_APP_SET_BUS_WIDTH_CMD6 6 +#define SD_ERASE_WR_BLK_START_CMD32 32 +#define SD_ERASE_WR_BLK_END_CMD33 33 +#define SD_APP_SEND_OP_COND_CMD41 41 +#define SD_APP_SEND_SCR_CMD51 51 + +// other useful defines +#define SD_VOLTAGE_RANGE_270_360 0x00000100 +#define SD_IF_COND_ECHO 0x000000AA +#define SD_STATE_CHANGE_ATTEMPTS 1000 +#define SD_TRY_AGAIN 10 + + +// ACMD41 bit shifts and masks +#define OCR_DONE_BSY_N (1 << 31) +#define HCS_SHIFT 30 +#define HCS (1 << HCS_SHIFT) +#define XPC_SHIFT 28 +#define XPC (1 << XPC_SHIFT) +#define S18R_SHIFT 24 +#define S18R (1 << S18R_SHIFT) +#define OCR_SHIFT 8 +#define OCR (0xFF << OCR_SHIFT) + +// OCR register voltage ranges +#define VDD_27_28 (1 << 15) +#define VDD_28_29 (1 << 16) +#define VDD_29_30 (1 << 17) +#define VDD_30_31 (1 << 18) +#define VDD_31_32 (1 << 19) +#define VDD_32_33 (1 << 20) +#define VDD 33_34 (1 << 21) +#define VDD_34_35 (1 << 22) +#define VDD_35_36 (1 << 23) +#define VDD_S18A (1 << 24) + + +#define VDD_MASK ( VDD_27_28 | VDD_28_29 | VDD_29_30 | VDD_30_31 | VDD_31_32 | VDD_32_33 | VDD 33_34 | VDD_34_35 | VDD_35_36 ) +#define VDD_RANGE_27_30 ( VDD_27_28 | VDD_28_29 | VDD_29_30 ) +#define VDD_RANGE_30_33 ( VDD_30_31 | VDD_31_32 | VDD_32_33 ) +#define VDD_RANGE_33_36 ( VDD 33_34 | VDD_34_35 | VDD_35_36 ) + +#define CARD_UHS_II_STATUS (1 << 29) +#define CARD_CAPACITY_STATUS (1 << 30) +#define CARD_POWER_UP_BUSY (1 << 31) + +//! Physical Layer Simplified Specification Version 4.10 "Card Status" bits from Table 4-41 +#define SDCARD_OUT_OF_RANGE (1 << 31) +#define SDCARD_ADDRESS_ERROR (1 << 30) +#define SDCARD_BLOCK_LEN_ERROR (1 << 29) +#define SDCARD_ERASE_SEQ_ERROR (1 << 28) +#define SDCARD_ERASE_PARAM (1 << 27) +#define SDCARD_WP_VIOLATION (1 << 26) +#define SDCARD_CARD_IS_LOCKED (1 << 25) +#define SDCARD_LOCK_UNLOCK_FAILED (1 << 24) +#define SDCARD_COM_CRC_ERROR (1 << 23) +#define SDCARD_ILLEGAL_COMMAND (1 << 22) +#define SDCARD_CARD_ECC_FAILED (1 << 21) +#define SDCARD_CC_ERROR (1 << 20) +#define SDCARD_ERROR (1 << 19) +#define SDCARD_CSD_OVERWRITE (1 << 16) +#define SDCARD_WP_ERASE_SKIP (1 << 15) +#define SDCARD_CARD_ECC_DISABLED (1 << 14) +#define SDCARD_ERASE_RESET (1 << 13) +#define SDCARD_CURRENT_STATE (0xF << 9) +#define SDCARD_READY_FOR_DATA (1 << 8) +#define SDCARD_APP_CMD (1 << 5) +#define SDCARD_AKE_SEQ_ERROR (1 << 3) + +// RCA RESPONSE STATUS BITS +#define RCASTATUS_COM_CRC_ERROR (1 << 15) +#define RCASTATUS_ILLEGAL_COMMAND (1 << 14) +#define RCASTATUS_ERROR (1 << 13) +#define RCASTATUS_CURRENT_STATE (SDCARD_CURRENT_STATE) +#define RCASTATUS_READY_FOR_DATA (SDCARD_READY_FOR_DATA) +#define RCASTATUS_APP_CMD (SDCARD_APP_CMD) +#define RCASTATUS_AKE_SEQ_ERROR (SDCARD_AKE_SEQ_ERROR) + +#define RCAERROR_MSK (RCASTATUS_COM_CRC_ERROR | RCASTATUS_ILLEGAL_COMMAND | RCASTATUS_ERROR) + +typedef enum +{ + eIdle = 0, + eReady, + eIdent, + eStby, + eTran, + eData, + eRcv, + ePrg, + eDis, + eInvalid +}sdCardState_e; + +#define NUM_INIT_ATTEMPTS 2 + +bool sdCardBusInit( sdHc_t* pHc ); +bool sdReadSingleBlock( sdCard_t* pCard, uint8_t* pData, uint32_t addr ); +bool sdReadMultipleBlock( sdCard_t* pCard ); +bool sdStopTransmission( sdCard_t* pCard ); + +#endif // __SD_H diff --git a/src/hw/sd_if.c b/src/hw/sd_if.c new file mode 100644 index 0000000..0d00b3c --- /dev/null +++ b/src/hw/sd_if.c @@ -0,0 +1,413 @@ + /***************************************************************************** + * + * Copyright (c) 2012-2014 Sage Electronic Engineering. All rights reserved. + * + * Software License Agreement + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * Sage Electronic Engineering SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR + * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + *****************************************************************************/ + +#include "stacks.h" +#include "string.h" +#include "malloc.h" +#include "util.h" +#include "output.h" +#include "biosvar.h" +#include "pci.h" +#include "pci_ids.h" +#include "std/disk.h" +#include "bar.h" +#include "sdhci.h" +#include "sdhc_generic.h" +#include "sd_if.h" + +sdDiskIf_t* g_pDev; + +static int sd_disk_read( struct disk_op_s* op ); +static int sd_disk_read_aligned( struct disk_op_s* op ); + +/** + * @brief sd_disk_init - finalize the SeaBIOS drive initialization and register the drive + * + * @param sdDiskIf_t* pSdIf - pointer to sd disk interface structure + * + * @return none + */ +static void sd_disk_init( sdDiskIf_t* pSdIf ) +{ + pSdIf->drive.blksize = pSdIf->pHostCtrl->blkSize; + pSdIf->drive.type = DTYPE_SD; + pSdIf->drive.cntl_id = 0; // @TODO: Presently only one SD card is supported at a time! + pSdIf->drive.removable = false; + pSdIf->drive.translation = TRANSLATION_LBA; + // shift by 9 (divide by 512 block size) + pSdIf->drive.sectors = pSdIf->pHostCtrl->pCard->decode.csdDecode.capacity >> 9; + dprintf( DEBUG_HDL_SD, "SD: num sectors: %d\n", (uint32_t)pSdIf->drive.sectors ); + + // generate host vendor/spec string + pSdIf->desc = znprintf( MAXDESCSIZE, + "SD Card Vendor ID: %d", + pSdIf->pHostCtrl->hostVendorId ); + + pSdIf->bootPriority = bootprio_find_pci_device( (struct pci_device*)pSdIf->pPci ); + dprintf( DEBUG_HDL_SD, "SD card boot priority: 0x%08x\n", pSdIf->bootPriority ); + + // register the device as a hard disk + boot_add_hd(&pSdIf->drive, pSdIf->desc, (int)pSdIf->bootPriority ); +} + +/** + * @brief sd_host_setup - setup the host controller driver, if the host + * is successfully initialized, setup the sd card initialization + * + * @param sdDiskIf_t* pSdIf - pointer to sd disk interface structure + * + * @return bool true if the card was successfully initialized and prepared for boot + * fasle otherwise + */ +static bool sd_host_setup( sdDiskIf_t* pSdIf) +{ + bool status = false; + + // perform the sd card initialization sequence + if( sdhc_init( pSdIf->pHostCtrl ) ) + { + // if the card passes initialization and goes to standby mode, it is ready for boot, so setup the disk info + if( sdCardBusInit( pSdIf->pHostCtrl ) ) + { + sd_disk_init( pSdIf ); + + // the card is now enumerated, prepare it for boot (operational mode) + sdhc_prepBoot( pSdIf->pHostCtrl ); + status = true; + } + } + return status; +} + +/** + * @brief sd_card_detect - check to see if the card detect indicates that a card is present + * if there is a card, setup the host and intialize the underlying card + * + * @param sdDiskIf_t* pSdIf - pointer to sd disk interface structure + * + * @return bool status of the card detect bit + */ +static bool sd_card_detect( sdDiskIf_t* pSdIf ) +{ + bool status = false; + uint32_t regVal32 = 0; + + // check to see if the card is present + regVal32 = barRead32(pSdIf->pHostCtrl->barAddress, SDHCI_PRESENT_STATE); + status = (regVal32 & SDHCI_CARD_PRESENT) == SDHCI_CARD_PRESENT ? true : false; + + // if the card is present, register it in the boot sequence + if( status ) + { + status = sd_host_setup( pSdIf ); + } + + return status; +} + + +/** + * @brief sd_config_setup - setup the sd host controller driver and allocate resources. + * + * @param struct pci_device* pci - pointer to the sdhci controller pci device. + * + * @return none + */ +static void sd_config_setup(struct pci_device* pci) +{ + dprintf(6, "sd_config_setup: 0x%04x\n", pci->bdf ); + + // allocate the pci to sd interface structure + g_pDev = (sdDiskIf_t*)malloc_fseg( sizeof(*g_pDev) ); + if( !g_pDev ) + { + warn_noalloc(); + return; + } + memset( g_pDev, 0, sizeof( *g_pDev ) ); + + // allocate the host controller + g_pDev->pHostCtrl = (sdHc_t*)malloc_fseg( sizeof(*g_pDev->pHostCtrl) ); + if( !(g_pDev->pHostCtrl) ) + { + warn_noalloc(); + free(g_pDev); + return; + } + memset( g_pDev->pHostCtrl, 0, sizeof( *g_pDev->pHostCtrl) ); + g_pDev->pHostCtrl->isInitialized = false; + + // assign the pci device and set up the host controller + g_pDev->pPci = pci; + + // setup bar0 + g_pDev->pHostCtrl->barAddress = pci_config_readl(g_pDev->pPci->bdf, 0x10) & 0xFFFFFF00; + + // check for card detect + if( !sd_card_detect(g_pDev) ) + { + dprintf( DEBUG_HDL_SD, "No SD card detected\n"); + free( g_pDev->pHostCtrl ); + free( g_pDev ); + } + + else + { + dprintf( DEBUG_HDL_SD, "SD card is inserted\n"); + + // initialize bounce buffer + if( create_bounce_buf() < 0 ) + { + warn_noalloc(); + free( g_pDev); + } + } +} + + +/** + * @brief sd_scan - seabios function to scan for the sd conroller on the pci bus + * + * @param none + * + * @return none + */ +static void sd_scan(void) +{ + struct pci_device *pci = NULL; + dprintf(6, "SD: Scanning for SD Host controllers\n" ); + + // Scan PCI bus for sd_mmc host controllers + foreachpci(pci) + { + if( pci->class != PCI_CLASS_SYSTEM_SDHCI ) + { + continue; + } + + if( pci->prog_if != 1 ) + { + continue; + } + dprintf(6, "Found PCI SDHCI controller\n"); + + // setup the sd host controller hardware + sd_config_setup(pci); + } +} + + + +/** + * @brief sd_setup - seabios function + * + * @param none + * + * @return none + */ +void sd_setup( void ) +{ + ASSERT32FLAT(); + if( CONFIG_SD ) + { + dprintf(3, "init SD drives\n"); + sd_scan(); + } +} + +/** + * @brief sd_cmd_data - seabios entry point for command/data disk transactions + * + * @param struct disk_op_s *op - pointer to the disk operations structure + * @param uint16_t blocksize - the size of the blocks for the block device transactions + * + * @return int disk operation status + */ +int sd_cmd_data( struct disk_op_s *op, void *cdbcmd, uint16_t blocksize ) +{ + int retVal = DISK_RET_EPARAM; + dprintf(3, "sd_cmd_data\n"); + + switch( op->command ) + { + case CMD_READ: + retVal = sd_disk_read( op ); + break; + case CMD_WRITE: + break; + case CMD_RESET: + case CMD_ISREADY: + case CMD_FORMAT: + case CMD_VERIFY: + case CMD_SEEK: + retVal = DISK_RET_SUCCESS; + break; + default: + retVal = DISK_RET_EPARAM; + break; + } + return retVal; +} + + +/** + * @brief sd_disk_read_aligned - read into an aligned buffer a block of data from the sd card + * + * @param struct disk_op_s* op - pointer to the disk operation request + * + * @return int - disk operation status + */ +static int sd_disk_read_aligned( struct disk_op_s* op ) +{ + int retVal = DISK_RET_SUCCESS; + sdDiskIf_t* pSdIf = GET_GLOBAL( g_pDev ); + uint16_t i = 0; + uint8_t* curPosition = (uint8_t*)op->buf_fl; + + for( i = 0; i < op->count; i++ ) + { + if( !sdReadSingleBlock( pSdIf->pHostCtrl->pCard, curPosition, (uint32_t)(op->lba + i)) ) + { + dprintf( DEBUG_HDL_SD, "SD Read Fail\n"); + retVal = DISK_RET_EPARAM; + break; + } + else + { + dprintf( DEBUG_HDL_SD, "sd disk %s, lba %6x, count %3x, buf %p, rc %d\n", + "read", (u32)op->lba + i, op->count - i, curPosition, retVal); + curPosition += BLOCK_SIZE8; + } + } + dprintf( DEBUG_HDL_SD, "return from read retval = %u\n", retVal ); + return retVal; +} + +/** + * @brief sd_disk_read - if the requested buffer is word aligned, performs the disk read operation + * + * @param struct disk_op_s* op - pointer to the disk operation request + * + * @return int - disk operation status + */ +static int sd_disk_read( struct disk_op_s* op ) +{ + int retVal = DISK_RET_EPARAM; + struct disk_op_s localOp; + uint8_t* alignedBuf = NULL; + uint8_t* curPosition = NULL; + uint16_t i = 0; + + // check if the callers buffer is word aligned, if so use it directly + if( ( (uint32_t)op->buf_fl & 1 ) == 0 ) + { + dprintf( DEBUG_HDL_SD, "sd read: buffer already alligned\n"); + retVal = sd_disk_read_aligned( op ); + } + else + { + dprintf( DEBUG_HDL_SD, "sd read: unaligned buffer, performing realligend read\n"); + // get access to an aligned buffer for the disk operation + localOp = *op; + alignedBuf = GET_GLOBAL( bounce_buf_fl ); + curPosition = op->buf_fl; + + // execute the aligned to unaligned access one operation at a time + localOp.buf_fl = alignedBuf; + localOp.count = 1; + + for( i = 0; i < op->count; i++ ) + { + retVal = sd_disk_read_aligned( &localOp ); + if( retVal ) + { + dprintf( DEBUG_HDL_SD, " - aligned read fail\n"); + break; + } + memcpy_fl( curPosition, alignedBuf, BLOCK_SIZE8 ); + curPosition += BLOCK_SIZE8; + localOp.lba++; + } + + } + return retVal; +} + +/** + * @brief process_sd_op - entry point for disk io operations for sea bios + * + * @param struct disk_op_s *op - disk io functions + * + * @return int - disk return value from disk.h + * + * @NOTE: The macro VISIBLE32FLAT is indicitave of a call to 32 bit mode from 16-bit mode it forces the compiler + * to prepend the <function name> with <_cfunc32flat_><funciton name>, so in this case if you were to perform + * a search for the caller of process_sd_op, you may not find it unless you search for _cfunc32flat_process_sd_op + */ + +int VISIBLE32FLAT process_sd_op( struct disk_op_s *op ) +{ + int retVal = DISK_RET_EPARAM; + + // ensure the configuration exists + if (!CONFIG_SD) + return 0; + + dprintf( DEBUG_HDL_SD, "Executing SD disk transaction: %d\r\n", op->command ); + // execute a command + switch( op->command ) + { + case CMD_READ: + dprintf( DEBUG_HDL_SD, + "SD CMD_READ: lba: 0x%08x%08x\n", (uint32_t)(op->lba >> 32), (uint32_t)(op->lba) ); + dprintf( DEBUG_HDL_SD, " op->count = %d\n", op->count ); + retVal = sd_disk_read( op ); + break; + case CMD_WRITE: + break; + case CMD_RESET: + case CMD_ISREADY: + case CMD_FORMAT: + case CMD_VERIFY: + case CMD_SEEK: + retVal = DISK_RET_SUCCESS; + break; + default: + op->count = 0; + retVal = DISK_RET_EPARAM; + break; + + } + return retVal; +} + +void SD_DEBUG( const char* func, unsigned int line ) +{ + volatile uint8_t exit = 0; + volatile uint32_t i = 0; + + dprintf( DEBUG_HDL_SD, "%s, %d\n", func, line ); + + while( !exit ) + { + i++; + } +} diff --git a/src/hw/sd_if.h b/src/hw/sd_if.h new file mode 100644 index 0000000..af2e626 --- /dev/null +++ b/src/hw/sd_if.h @@ -0,0 +1,49 @@ + /***************************************************************************** + * + * Copyright (c) 2012-2014 Sage Electronic Engineering. All rights reserved. + * + * Software License Agreement + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * Sage Electronic Engineering SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR + * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + *****************************************************************************/ + +#ifndef __SD_IF_H +#define __SD_IF_H + +#include <stdint.h> +#include "block.h" +#include "config.h" +#include "pci.h" +#include "sd.h" + +/** @file sd_if.h*/ + +// SeaBIOS to SD driver interface (to allow portability for reuse of sd driver + +typedef struct +{ + struct drive_s drive; + int32_t bootPriority; + const char* desc; + struct pci_device* pPci; + sdHc_t* pHostCtrl; +}sdDiskIf_t; + +void sd_setup (void ); +int sd_cmd_data( struct disk_op_s *op, void *cdbcmd, uint16_t blocksize ); +int process_sd_op( struct disk_op_s *op ); +void SD_DEBUG(const char* func, unsigned int line); +#define SD_STOP( ) SD_DEBUG( __FUNCTION__, __LINE__ ) +#endif // __SD_IF_H diff --git a/src/hw/sd_utils.c b/src/hw/sd_utils.c new file mode 100644 index 0000000..6920cb1 --- /dev/null +++ b/src/hw/sd_utils.c @@ -0,0 +1,190 @@ +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * Copyright (C) 2013-2104 Sage Electronic Engineering, LLC + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + */ + +/* + * NOTE: This is refactored code from FreeBSD modified to be OS agnostic and + * independent for use in bootloading. + */ + +#include "config.h" +#include "string.h" +#include "output.h" +#include "sd_utils.h" + +static uint32_t get_bits(uint32_t *bits, int bit_len, int start, int size); + +static const int exp[8] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 +}; + +static const int mant[16] = { + 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 +}; + +static const int cur_min[8] = { + 500, 1000, 5000, 10000, 25000, 35000, 60000, 100000 +}; + +static const int cur_max[8] = { + 1000, 5000, 10000, 25000, 35000, 45000, 800000, 200000 +}; + +static uint32_t get_bits(uint32_t *bits, int bit_len, int start, int size) +{ + const int i = (bit_len / 32) - (start / 32) - 1; + const int shift = start & 31; + uint32_t retval = bits[i] >> shift; + if (size + shift > 32) + retval |= bits[i - 1] << (32 - shift); + return (retval & ((1llu << size) - 1)); +} + +void decode_cid_sd(uint32_t *raw_cid, sd_cid_t* cid) +{ + int i; + + /* There's no version info, so we take it on faith */ + memset(cid, 0, sizeof(*cid)); + cid->mid = get_bits(raw_cid, 128, 120, 8); + cid->oid = get_bits(raw_cid, 128, 104, 16); + for (i = 0; i < 5; i++) + cid->pnm[i] = get_bits(raw_cid, 128, 96 - i * 8, 8); + cid->pnm[5] = 0; + cid->prv = get_bits(raw_cid, 128, 56, 8); + cid->psn = get_bits(raw_cid, 128, 24, 32); + cid->mdt_year = get_bits(raw_cid, 128, 12, 8) + 2000; + cid->mdt_month = get_bits(raw_cid, 128, 8, 4); +} + +void decode_csd_sd(uint32_t *raw_csd, sd_csd_t* csd) +{ + int v; + int m; + int e; + int tst; + + memset(csd, 0, sizeof(*csd)); + csd->csd_structure = v = get_bits(raw_csd, 128, 126, 2); + dprintf( DEBUG_HDL_SD, "CSD Register Info:\n" ); + if (v == 0) { + dprintf( DEBUG_HDL_SD, " CSD Version 1.0\n"); + m = get_bits(raw_csd, 128, 115, 4); + e = get_bits(raw_csd, 128, 112, 3); + + tst = get_bits(raw_csd, 128, 112, 8 ); + dprintf( DEBUG_HDL_SD, " RAW TAAC: 0x%02x\n", (uint8_t)tst ); + + csd->tacc = (exp[e] * mant[m] + 9) / 10; + csd->nsac = get_bits(raw_csd, 128, 104, 8) * 100; + m = get_bits(raw_csd, 128, 99, 4); + e = get_bits(raw_csd, 128, 96, 3); + csd->tran_speed = exp[e] * 10000 * mant[m]; + csd->ccc = get_bits(raw_csd, 128, 84, 12); + csd->read_bl_len = 1 << get_bits(raw_csd, 128, 80, 4); + csd->read_bl_partial = get_bits(raw_csd, 128, 79, 1); + csd->write_blk_misalign = get_bits(raw_csd, 128, 78, 1); + csd->read_blk_misalign = get_bits(raw_csd, 128, 77, 1); + csd->dsr_imp = get_bits(raw_csd, 128, 76, 1); + csd->vdd_r_curr_min = cur_min[get_bits(raw_csd, 128, 59, 3)]; + csd->vdd_r_curr_max = cur_max[get_bits(raw_csd, 128, 56, 3)]; + csd->vdd_w_curr_min = cur_min[get_bits(raw_csd, 128, 53, 3)]; + csd->vdd_w_curr_max = cur_max[get_bits(raw_csd, 128, 50, 3)]; + m = get_bits(raw_csd, 128, 62, 12); + e = get_bits(raw_csd, 128, 47, 3); + csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len; + csd->erase_blk_en = get_bits(raw_csd, 128, 46, 1); + csd->erase_sector = get_bits(raw_csd, 128, 39, 7) + 1; + csd->wp_grp_size = get_bits(raw_csd, 128, 32, 7); + csd->wp_grp_enable = get_bits(raw_csd, 128, 31, 1); + csd->r2w_factor = 1 << get_bits(raw_csd, 128, 26, 3); + csd->write_bl_len = 1 << get_bits(raw_csd, 128, 22, 4); + csd->write_bl_partial = get_bits(raw_csd, 128, 21, 1); + } else if (v == 1) { + dprintf( DEBUG_HDL_SD, "CSD Version 2.0\n"); + m = get_bits(raw_csd, 128, 115, 4); + e = get_bits(raw_csd, 128, 112, 3); + + tst = get_bits(raw_csd, 128, 112, 8 ); + dprintf( DEBUG_HDL_SD, " RAW TAAC: 0x%02x\n", (uint8_t)tst ); + + csd->tacc = (exp[e] * mant[m] + 9) / 10; + csd->nsac = get_bits(raw_csd, 128, 104, 8) * 100; + m = get_bits(raw_csd, 128, 99, 4); + e = get_bits(raw_csd, 128, 96, 3); + csd->tran_speed = exp[e] * 10000 * mant[m]; + csd->ccc = get_bits(raw_csd, 128, 84, 12); + csd->read_bl_len = 1 << get_bits(raw_csd, 128, 80, 4); + csd->read_bl_partial = get_bits(raw_csd, 128, 79, 1); + csd->write_blk_misalign = get_bits(raw_csd, 128, 78, 1); + csd->read_blk_misalign = get_bits(raw_csd, 128, 77, 1); + csd->dsr_imp = get_bits(raw_csd, 128, 76, 1); + csd->capacity = ((uint64_t)get_bits(raw_csd, 128, 48, 22) + 1) * + 512 * 1024; + dprintf( DEBUG_HDL_SD, " C_SIZE: 0x%08x\n", get_bits(raw_csd, 128, 48, 22) ); + csd->erase_blk_en = get_bits(raw_csd, 128, 46, 1); + csd->erase_sector = get_bits(raw_csd, 128, 39, 7) + 1; + csd->wp_grp_size = get_bits(raw_csd, 128, 32, 7); + csd->wp_grp_enable = get_bits(raw_csd, 128, 31, 1); + csd->r2w_factor = 1 << get_bits(raw_csd, 128, 26, 3); + csd->write_bl_len = 1 << get_bits(raw_csd, 128, 22, 4); + csd->write_bl_partial = get_bits(raw_csd, 128, 21, 1); + } else + { + dprintf( DEBUG_HDL_SD, "unknown SD CSD version\n"); + } + + dprintf( DEBUG_HDL_SD, " READ_BL_LEN: %d\n", csd->read_bl_len ); + + dprintf( DEBUG_HDL_SD, " CAPACITY: 0x%08x%08x\n", (uint32_t)( csd->capacity >> 32), (uint32_t)(csd->capacity) ); +} diff --git a/src/hw/sd_utils.h b/src/hw/sd_utils.h new file mode 100644 index 0000000..56d0d97 --- /dev/null +++ b/src/hw/sd_utils.h @@ -0,0 +1,108 @@ +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * Copyright (C) 2013-2104 Sage Electronic Engineering, LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + */ + +/* + * NOTE: This is refactored code from FreeBSD modified to be OS agnostic and + * independent for use in bootloading. + */ + +#ifndef __SD_UTILS_H +#define __SD_UTILS_H + +#include <stdint.h> + +/* CSD -- decoded structure */ +typedef struct +{ + uint32_t mid; + char pnm[8]; + uint32_t psn; + uint16_t oid; + uint16_t mdt_year; + uint8_t mdt_month; + uint8_t prv; + uint8_t fwrev; +}sd_cid_t; + +typedef struct +{ + uint8_t csd_structure; + uint8_t spec_vers; + uint16_t ccc; + uint16_t tacc; + uint32_t nsac; + uint32_t r2w_factor; + uint32_t tran_speed; + uint32_t read_bl_len; + uint32_t write_bl_len; + uint32_t vdd_r_curr_min; + uint32_t vdd_r_curr_max; + uint32_t vdd_w_curr_min; + uint32_t vdd_w_curr_max; + uint32_t wp_grp_size; + uint32_t erase_sector; + uint64_t capacity; + unsigned int read_bl_partial:1, + read_blk_misalign:1, + write_bl_partial:1, + write_blk_misalign:1, + dsr_imp:1, + erase_blk_en:1, + wp_grp_enable:1; +}sd_csd_t; + + +void decode_cid_sd(uint32_t *raw_cid, sd_cid_t* cid); +void decode_csd_sd(uint32_t *raw_csd, sd_csd_t* csd); + +#endif diff --git a/src/hw/sdhc_generic.c b/src/hw/sdhc_generic.c new file mode 100644 index 0000000..6c675e1 --- /dev/null +++ b/src/hw/sdhc_generic.c @@ -0,0 +1,697 @@ + /***************************************************************************** + * + * Copyright (c) 2012-2014 Sage Electronic Engineering. All rights reserved. + * + * Software License Agreement + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * Sage Electronic Engineering SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR + * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + *****************************************************************************/ + +#include <stdint.h> +#include <stdbool.h> +#include "string.h" +#include "malloc.h" +#include "util.h" +#include "output.h" +#include "biosvar.h" +#include "pci.h" +#include "pci_ids.h" +#include "std/disk.h" +#include "bar.h" +#include "sdhci.h" +#include "sd.h" +#include "sdhc_generic.h" + +// host controller functions +static void sdhc_readResponse( sdHc_t* pSdCtrl, sdXfer_t* pXfer ); +static bool sdhc_pollIntrStatus( sdHc_t* pSdCtrl, sdXfer_t* pXfer, uint32_t intrNum, uint32_t pollCnt, uint32_t uSecTmo ); +static void sdhc_getVerInfo( sdHc_t* pSdCtrl ); +static void sdhc_setClock( sdHc_t* pSdCtrl, uint32_t clockVal ); +static void sdhc_setPower( sdHc_t* pSdCtrl, uint32_t pwrMode ); +static bool sdhc_reset( sdHc_t* pSdCtrl, uint8_t resetFlags ); +static void sdhc_readBlock( sdHc_t* pSdCtrl, uint8_t* pBuf, uint32_t count ); + +// callback function for performing command/response transactions +static bool sdhc_Cmd( sdHc_t* pSdCtrl, sdXfer_t* pXfer ); + + +/** + * @brief sdhc_readResponse - helper function to read different response types + * based on a transfer request + * + * @param sdHc_t* pSdCtrl - pointer to the host controller abstraction structure + * @param sdXfer_t* pXfer - pointer to the host style transfer structure + * + * @return none + */ +static void sdhc_readResponse( sdHc_t* pSdCtrl, sdXfer_t* pXfer ) +{ + memset( pXfer->response, 0, sizeof( pXfer->response[0] * 4 ) ); + pXfer->rspValid = false; + + switch( pXfer->rspType ) + { + case eRsp136: + pXfer->response[0] = barRead32(pSdCtrl->barAddress, SDHCI_RESPONSE ); + pXfer->response[1] = barRead32(pSdCtrl->barAddress, SDHCI_RESPONSE + 0x04 ); + pXfer->response[2] = barRead32(pSdCtrl->barAddress, SDHCI_RESPONSE + 0x08 ); + pXfer->response[3] = barRead32(pSdCtrl->barAddress, SDHCI_RESPONSE + 0x0C ); + pXfer->rspValid = true; + break; + + case eRsp48: + pXfer->response[0] = barRead32(pSdCtrl->barAddress, SDHCI_RESPONSE ); + pXfer->rspValid = true; + break; + + case eRsp48_busy: + pXfer->response[0] = barRead32(pSdCtrl->barAddress, SDHCI_RESPONSE + 0x0C ); + pXfer->rspValid = true; + break; + + case eRspNone: + default: + break; + } +} + +/** + * @brief sdhc_pollIntrStatus - interrupt status polling routine + * + * @param sdHc_t* pSdCtrll - pointer to the host controller struct + * @param uint32_t intrNum - the requested interrupt number(s) to check + * @param uint32_t pollCnt - the number of iterations to poll + * @param uint32_t uSecTmo - microsecond delay per polling iteration + * + * @return bool - true if the requested interrupt was set during this + * request cycle, false otherwise + */ +static bool sdhc_pollIntrStatus( sdHc_t* pSdCtrl, sdXfer_t* pXfer, uint32_t intrNum, uint32_t pollCnt, uint32_t uSecTmo ) +{ + bool status = false; + uint32_t regVal32= 0; + + // check at least once + if( !pollCnt ) + { + pollCnt = 1; + } + + // check for the interrupt flag + while( pollCnt-- ) + { + regVal32 = barRead32(pSdCtrl->barAddress, SDHCI_INT_STATUS ); + // check that all requested interrupts were set + if( ( intrNum & regVal32 ) == intrNum ) + { + // in the case of response to command, save the response + if( intrNum == SDHCI_INT_RESPONSE ) + { + sdhc_readResponse( pSdCtrl, pXfer); + } + + // clear the interrupt flag(s) + // @NOTE: some interrupts will not be cleared by writing them to 0 + regVal32 &= ~intrNum; + barWrite32( pSdCtrl->barAddress, SDHCI_INT_STATUS, regVal32 ); + status = true; + dprintf( DEBUG_HDL_SD, "SD: requested interrupt occured: %u\n", intrNum ); + break; + } + else if( regVal32 & SDHCI_INT_ERROR_MASK ) + { + // notify debug of timeout error + if( regVal32 & SDHCI_INT_TIMEOUT ) + { + dprintf( DEBUG_HDL_SD, "SD: ERROR Timeout\n" ); + } + // reset the card on fatal errors + else + { + dprintf( DEBUG_HDL_SD, + "SD: ERROR interrupt occured, clearing interrupt and resetting card\n" ); + sdhc_reset( pSdCtrl, SDHCI_RESET_CMD | SDHCI_RESET_DATA ); + } + barWrite32( pSdCtrl->barAddress, SDHCI_INT_STATUS, ~SDHCI_INT_ERROR_MASK ); + status = false; + break; + } + + udelay( uSecTmo ); + } + // in the case of errors, reset the card and clear out the error interrupts + dprintf( DEBUG_HDL_SD, "SD: Current interrupt status register: 0x%08x\n", regVal32 ); + + return status; +} + +/** + * @brief sdhc_getVerInfo - read the host controller vendor specific id and host + * sd specification supported by the controller + * + * @param sdHc_t* pSdCtrll - pointer to the host controller struct + * + * @return none + */ +static void sdhc_getVerInfo( sdHc_t* pSdCtrl ) +{ + uint16_t regVal16 = 0; + + regVal16 = barRead16( pSdCtrl->barAddress, SDHCI_HOST_VERSION ); + pSdCtrl->hostVendorId = + (uint8_t)( ( regVal16 & SDHCI_VENDOR_VER_MASK ) >> SDHCI_VENDOR_VER_SHIFT ); + pSdCtrl->hostSpecId = + (uint8_t)( ( regVal16 & SDHCI_SPEC_VER_MASK ) >> SDHCI_SPEC_VER_SHIFT ); +} + +/** + * @brief sdhc_setPower - setup the power state of the host controller based on its + * reported capabilities. The first time this is called, the power is setup + * in the highest condition to support all types of cards. After card enumeration + * the voltage can be lowered. + * + * @param sdHc_t* pSdCtrll - pointer to the host controller abstraction structure + * @param uint32_t pwrMode - the requested voltage setting for the power + * + * @return none + */ +static void sdhc_setPower( sdHc_t* pSdCtrl, uint32_t pwrMode ) +{ + uint8_t pwr = 0; + + pSdCtrl->pwrMode = pwrMode; + + /* Turn off the POWER. */ + barWrite8(pSdCtrl->barAddress, SDHCI_POWER_CONTROL, pwr); + + if (pwrMode == 0) + return; + + /* Set voltage. */ + switch( pwrMode ) + { + case SDHCI_CAN_VDD_180: + pwr |= SDHCI_POWER_180; + break; + case SDHCI_CAN_VDD_300: + pwr |= SDHCI_POWER_300; + break; + case SDHCI_CAN_VDD_330: + pwr |= SDHCI_POWER_330; + break; + } + + barWrite8(pSdCtrl->barAddress, SDHCI_POWER_CONTROL, pwr); + /* Turn on the POWER. */ + pwr |= SDHCI_POWER_ON; + barWrite8(pSdCtrl->barAddress, SDHCI_POWER_CONTROL, pwr); +} + +/** + * @brief sdhc_setClock - set the clock of the host controller. This function executes the + * clock divisor algorithm described in the "SD Host Controller Simplified + * Specification Version 3.00" to generate the highest clock frequency that the + * card host can handle based on the requested clock. + * + * @param sdHc_t* pSdCtrll - pointer to the host controller abstraction structure + * @param uint32_t clockVal - desired clock frequency + * + * @return none. + */ +static void sdhc_setClock( sdHc_t* pSdCtrl, uint32_t clockVal ) +{ + uint32_t res = 0; + uint16_t clk = 0; + int32_t timeout = 0; + + pSdCtrl->curClk = clockVal; + + // disable the clock + barWrite16(pSdCtrl->barAddress, SDHCI_CLOCK_CONTROL, 0 ); + + // calculate the highest possible frequency <= the maximum clock frequency reported by the card + res = pSdCtrl->maxClk; + for (clk = 1; clk < 256; clk <<= 1) + { + if( res <= clockVal ) + { + break; + } + res >>= 1; + } + + // adjust the Divider, 1:1 is 0x00, 2:1 is 0x01, 256:1 is 0x80 ... + clk >>= 1; + clk <<= SDHCI_DIVIDER_SHIFT; + clk |= SDHCI_CLOCK_INT_EN; + barWrite16( pSdCtrl->barAddress, SDHCI_CLOCK_CONTROL, clk); + + // Wait up to 10 ms until it stabilizes + timeout = 10; + while( !((clk = barRead16(pSdCtrl->barAddress, SDHCI_CLOCK_CONTROL) ) + & SDHCI_CLOCK_INT_STABLE) ) + { + if( timeout == 0 ) + { + dprintf( DEBUG_HDL_SD, "SD: card internal clock never stabilized\n"); + break; + } + timeout--; + udelay(1000); + } + + if( timeout > 0) + { + // clock is stable so enable the clock signal for the card bus + dprintf( DEBUG_HDL_SD, "SD: card internal clock stabilized at %u Hz\n", pSdCtrl->curClk ); + clk |= SDHCI_CLOCK_CARD_EN; + barWrite16( pSdCtrl->barAddress, SDHCI_CLOCK_CONTROL, clk ); + } +} + +/** + * @brief sdhc_reset - issue the SD reset sequence described in the SD Host Controller Spec V3.00 + * + * @param sdHc_t* pSdCtrl - pointer to the host controller struct + * @param uint8_t resetFlags - a logical OR mask of the three possible reset types: + * SDHCI_RESET_ALL - reset entire host controller + * SDHCI_RESET_CMD - reset command circuit + * SDHCI_RESET_DATA - reset data & dma circuit + * + * @return bool - true if the request did not timeout, false otherwise + */ +static bool sdhc_reset( sdHc_t* pSdCtrl, uint8_t resetFlags ) +{ + uint8_t resetResult = 0; + uint32_t timeout = 0; + bool status = false; + + // send the reset command + barWrite8( (uint32_t)pSdCtrl->barAddress, SDHCI_SOFTWARE_RESET, resetFlags ); + + // wait for all of the requested reset flags to clear until the timeout occurs + timeout = 10; + while( ( resetResult = barRead8( pSdCtrl->barAddress, SDHCI_SOFTWARE_RESET) & resetFlags ) ) + { + if( timeout == 0 ) + { + dprintf( DEBUG_HDL_SD, + "SDHC Reset Timeout for reset request type: 0x%02x\n", resetFlags ); + } + timeout--; + udelay(100); + } + if( timeout > 0) + { + dprintf( DEBUG_HDL_SD, + "SDHC Reset Successful for reset request type: 0x%02x\n", resetFlags ); + status = true; + } + return status; +} + +/** + * @brief sdhc_Cmd - This is the call-back entry point for the card bus driver to access + * the bus via the host controller. This function must be registered with the + * card-bus driver in order for it to be able to function properly. It is the + * entry point for both command and data transfers. + * + * @param sdHc_t* pSdCtrl - pointer to the host controller abstraction + * @param sdXfer_t* pXfer - pointer to the command/data transfer, filled out by the + * card-bus driver. + * @return bool true on success, false otherwise + */ +static bool sdhc_Cmd( sdHc_t* pSdCtrl, sdXfer_t* pXfer ) +{ + uint32_t curState = 0; + uint32_t stateMsk = 0; + uint8_t regVal8 = 0; + uint8_t tmo = 10; + uint16_t mode = 0; + bool status = false; + uint32_t intFlags = 0; + + // setup the state mask for the transfer + stateMsk = SDHCI_CMD_INHIBIT; + if( pXfer->rspType == eRsp48_busy ) + { + stateMsk |= SDHCI_DAT_INHIBIT; + } + + // wait for the state mask to clear + while( curState & stateMsk ) + { + if( tmo == 0 ) + { + dprintf( DEBUG_HDL_SD, + "SD: unable to access cmd bus, its always busy\n"); + break; + } + tmo--; + udelay(1000); + curState = barRead32( pSdCtrl->barAddress, SDHCI_PRESENT_STATE ); + } + + if( tmo > 0 ) + { + //Set command argument + barWrite32(pSdCtrl->barAddress, SDHCI_ARGUMENT, pXfer->arg1); + + if( pXfer->pData && pXfer->xferType == eRdXfer ) + { + //Set data transfer mode for reading data + mode = barRead16( pSdCtrl->barAddress, SDHCI_TRANSFER_MODE ); + mode |= ( SDHCI_TRNS_READ ); + mode &= ~( SDHCI_TRNS_MULTI | SDHCI_TRNS_DMA ); + barWrite16(pSdCtrl->barAddress, SDHCI_TRANSFER_MODE, mode ); + } + else + { + //Set data transfer mode for commanding + mode = barRead16( pSdCtrl->barAddress, SDHCI_TRANSFER_MODE ); + mode &= ~( SDHCI_TRNS_READ | SDHCI_TRNS_MULTI | SDHCI_TRNS_DMA ); + barWrite16(pSdCtrl->barAddress, SDHCI_TRANSFER_MODE, mode ); + } + + // build the command transaction type + regVal8 = (pSdCtrl->crcCheckEnable) ? SDHCI_CMD_CRC : 0; + regVal8 |= (pSdCtrl->indexCheckEnable) ? SDHCI_CMD_INDEX : 0; + regVal8 |= (pXfer->pData != NULL) ? SDHCI_CMD_DATA : 0; + regVal8 |= (uint8_t)(pXfer->cmdType); + regVal8 |= (uint8_t)(pXfer->rspType); + barWrite8( pSdCtrl->barAddress, SDHCI_COMMAND_FLAGS, regVal8 ); + + // initiate the transfer + barWrite8(pSdCtrl->barAddress, SDHCI_COMMAND, pXfer->cmdIdx); + + tmo = 10; + if( pXfer->rspType != eRspNone ) + { + intFlags = SDHCI_INT_RESPONSE; + if( (pXfer->pData) && (pXfer->xferType == eRdXfer) ) + { + // wait for the transfer to be complete in the case of a read command + intFlags |= SDHCI_INT_DATA_AVAIL; + } + + while( !sdhc_pollIntrStatus( pSdCtrl, pXfer, intFlags, 100, 1000) ) + { + if( tmo == 0 ) + { + dprintf( DEBUG_HDL_SD, "SD: failed to receive response to command\n"); + break; + } + tmo--; + } + + // if this is a read block transaction break out here to get the data + if( (pXfer->pData) && (pXfer->xferType == eRdXfer) ) + { + sdhc_readBlock( pSdCtrl, pXfer->pData, BLOCK_SIZE8 ); + } + } + + } + + status = (tmo > 0); + return status; +} + +/** + * @brief sdhc_readBlock - read a block from the SD card, this function is only + * executed if the data pointer and read flag are set in the sdhc_Cmd + * request + * + * @param sdHc_t* pSdCtrl - pointer to the host controller abstraction + * @param uint8_t* pBuf - buffer to fill in with data (typically a block) + * @param uint32_t count - number of bytes to read in the transaction + * + * @return none + */ +static void sdhc_readBlock( sdHc_t* pSdCtrl, uint8_t* pBuf, uint32_t count ) +{ + uint32_t lim = 0; + uint32_t dataReg = 0; + uint8_t* pBufLocal = NULL; + + pBufLocal = pBuf; + + //lim = min(BLOCK_SIZE8, count); + lim = BLOCK_SIZE8; + + // ensure that there is data available + //usleep(10000); + // @NOTE: This usleep call was to throttle transactions during development, + // it can be commented back in to throttle block read transactions + // while modifying the driver + if( barRead32( pSdCtrl->barAddress, SDHCI_PRESENT_STATE ) & SDHCI_DATA_AVAILABLE ) + { + dprintf( DEBUG_HDL_SD, "SD: reading %d bytes of data\n", lim ); + + // calculate the number of 32 bit values to read by converting the limit to + // dwords (additional bytes handled later) + lim >>= 2; + while( lim > 0 ) + { + dataReg = barRead32(pSdCtrl->barAddress, SDHCI_BUFFER); + pBufLocal[0] = (uint8_t)(dataReg); + pBufLocal[1] = (uint8_t)(dataReg >> 8); + pBufLocal[2] = (uint8_t)(dataReg >> 16); + pBufLocal[3] = (uint8_t)(dataReg >> 24); + pBufLocal += 4; + lim--; + } + + // handle the remainder + lim = count & 0x03; + if( lim > 0 ) + { + dataReg = barRead32(pSdCtrl->barAddress, SDHCI_BUFFER); + while( lim > 0 ) + { + *(pBufLocal++) = (uint8_t)dataReg; + dataReg >>= 8; + lim--; + } + } + } +#if( CONFIG_DEBUG_LEVEL > 9 ) + hexdump( pBuf, BLOCK_SIZE8 ); +#endif + +} + +/** + * @brief sdhc_prepBoot - post initialization function to perform changes + * to the operating mode of the card prior to boot, and to + * take it out of enumeration mode. + * + * @param sdHc_t* pSdCtrl - pointer to the host controller struct + * + * @return none + */ +void sdhc_prepBoot( sdHc_t* pSdCtrl ) +{ + // boost the clock speed for boot + //@TODO: should check if this speed is supported first, (most newer cards do) + sdhc_setClock( pSdCtrl, 25 * MHZ ); + + //@TODO: do other changes to card operating mode here +} + +/** + * @brief sdhc_isInitialized - check to see if the host controller is initialized + * + * @param sdHc_t* pSdCtrl - pointerto the host controller sturcture + * + * @return bool true if host initialized, false otherwise + */ +bool sdhc_isInitialized( sdHc_t* pSdCtrl ) +{ + return pSdCtrl->isInitialized; +} + +/** + * @brief sdhc_init - performs the minimum necessary steps outlined in the SD + * host controller specification to prepare the sd card/host + * controller for use + * + * @param sdHc_t* pSdCtrl - pointer to the host controller struct + * + * @return bool true if reset succeeded, false otherwise + */ +bool sdhc_init( sdHc_t* pSdCtrl ) +{ + uint32_t regVal32 = 0; + uint8_t regVal8 = 0; + + // reset the the host controller + if( sdhc_reset( pSdCtrl, SDHCI_RESET_ALL ) ) + { + // read the capabilities register + pSdCtrl->cardCapabilities.cap1 = + barRead32( pSdCtrl->barAddress, SDHCI_CAPABILITIES ); + pSdCtrl->cardCapabilities.cap2 = + barRead32( pSdCtrl->barAddress, (SDHCI_CAPABILITIES + 4) ); + + regVal8 = barRead8(pSdCtrl->barAddress, SDHCI_HOST_CONTROL ); + dprintf( DEBUG_HDL_SD, "SD: Host Control register: 0x%02x\n", regVal8 ); + + // check the power + regVal8 = barRead8(pSdCtrl->barAddress, SDHCI_POWER_CONTROL ); + if( !(regVal8 & SDHCI_POWER_ON) ) + { + dprintf( DEBUG_HDL_SD, "SD: card currently not powered, power on to"); + // setup the power for the card + if( pSdCtrl->cardCapabilities.cap1 & SDHCI_CAN_VDD_330 ) + { + sdhc_setPower( pSdCtrl, SDHCI_CAN_VDD_330 ); + dprintf( DEBUG_HDL_SD, ": 3.3V\n"); + } + else if( pSdCtrl->cardCapabilities.cap1 & SDHCI_CAN_VDD_300 ) + { + sdhc_setPower( pSdCtrl, SDHCI_CAN_VDD_300 ); + dprintf( DEBUG_HDL_SD, ": 3.0V\n"); + } + else if( pSdCtrl->cardCapabilities.cap1 & SDHCI_CAN_VDD_180 ) + { + sdhc_setPower( pSdCtrl, SDHCI_CAN_VDD_180 ); + dprintf( DEBUG_HDL_SD, ": 1.8V\n"); + } + } + else + { + dprintf(6, "SD: card bus is powered on\n"); + } + + // determine the base clock frequency reported by the card + pSdCtrl->maxClk = + (pSdCtrl->cardCapabilities.cap1 & SDHCI_CLOCK_BASE_MASK) + >> SDHCI_CLOCK_BASE_SHIFT; + if( pSdCtrl->maxClk == 0 ) + { + dprintf( DEBUG_HDL_SD, + "SD: no base clock frequency specified by card capabilities\n"); + + // @TODO: If the clock was not set, need to set it ? + } + else + { + pSdCtrl->maxClk *= MHZ; + dprintf( DEBUG_HDL_SD, "SD: base clock frequency %u Hz\n", pSdCtrl->maxClk ); + } + + // setup the cards internal clock to always be normal speed mode to blanket support all card types + // the sd spec defines normal speed mode as 25MHz, and enumeration at 400KHz + sdhc_setClock( pSdCtrl, 400000 ); + + // determine the base timeout frequency + pSdCtrl->tmoClk = (pSdCtrl->cardCapabilities.cap1 & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; + if( pSdCtrl->tmoClk == 0 ) + { + dprintf( DEBUG_HDL_SD, "SD: no timeout clock frequency specified by card capabilities\n"); + } + else + { + // if the units are specified in MHz adjust the frequency to reflect that + pSdCtrl->tmoClk = + (pSdCtrl->cardCapabilities.cap1 & SDHCI_TIMEOUT_CLK_UNIT) + ? pSdCtrl->tmoClk * MHZ : pSdCtrl->tmoClk * KHZ; + dprintf( DEBUG_HDL_SD, "SD: timeout frequency clock %u\n", pSdCtrl->tmoClk ); + + // test max timeout + barWrite8( pSdCtrl->barAddress, SDHCI_TIMEOUT_CONTROL, 0x0E ); + } + + // the "always supported" block size is 512, so force it + regVal32 = barRead32( pSdCtrl->barAddress, SDHCI_BLOCK_SIZE ); + + // mask off the block bits + pSdCtrl->blkSize &= BLOCK_MASK; + if( pSdCtrl->blkSize == 0 ) + { + dprintf( DEBUG_HDL_SD, "SD: no block size set...\n"); + } + else + { + dprintf( DEBUG_HDL_SD, "SD: current block size: %u\n", pSdCtrl->blkSize ); + } + + // if necessary set the block size to the default + if( pSdCtrl->blkSize != BLOCK_SIZE8 ) + { + pSdCtrl->blkSize = BLOCK_SIZE8; + dprintf( DEBUG_HDL_SD, " - setting new block size to 512 bytes\n"); + + // clear the current block size bits + regVal32 &= ~BLOCK_MASK; + regVal32 |= pSdCtrl->blkSize; + barWrite32( pSdCtrl->barAddress, SDHCI_BLOCK_SIZE, regVal32 ); + + // check that the new value was written + regVal32 = barRead32( pSdCtrl->barAddress, SDHCI_BLOCK_SIZE ) & BLOCK_MASK; + if( regVal32 != BLOCK_SIZE8 ) + { + dprintf( DEBUG_HDL_SD, " - set new block size failed!"); + //@TODO: Probably should fail now? + } + else + { + dprintf( DEBUG_HDL_SD, " - new block size set to: %u\n", pSdCtrl->blkSize ); + } + } + + // test reset after config + sdhc_reset( pSdCtrl, SDHCI_RESET_CMD | SDHCI_RESET_DATA ); + + // setup the interrupts + barWrite32( pSdCtrl->barAddress, SDHCI_INT_ENABLE, SDHCI_INT_BUS_POWER | + SDHCI_INT_DATA_END_BIT | + SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | + SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | + SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | + SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | + SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE | + SDHCI_INT_ACMD12ERR ); + + // and signals + barWrite32( pSdCtrl->barAddress, SDHCI_SIGNAL_ENABLE, SDHCI_INT_BUS_POWER | + SDHCI_INT_DATA_END_BIT | + SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | + SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | + SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | + SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | + SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE | + SDHCI_INT_ACMD12ERR ); + + regVal32 = barRead32( pSdCtrl->barAddress, SDHCI_INT_ENABLE ); + dprintf(6, "SD: interrupts enabled to: 0x%08x\n", regVal32 ); + + regVal32 = barRead32( pSdCtrl->barAddress, SDHCI_INT_STATUS ); + dprintf(6, "SD: Current interrupt status: 0x%08x\n", regVal32 ); + + regVal32 = barRead32( pSdCtrl->barAddress, SDHCI_PRESENT_STATE ); + dprintf(6, "SD: Present State: 0x%08x\n", regVal32 ); + + // record the vendor and sd spec info of the host controller + sdhc_getVerInfo( pSdCtrl ); + + // setup the callback(s) for the underlying card bus + pSdCtrl->sdhcCmd = &sdhc_Cmd; + + pSdCtrl->isInitialized = true; + } + return( pSdCtrl->isInitialized ); +} diff --git a/src/hw/sdhc_generic.h b/src/hw/sdhc_generic.h new file mode 100644 index 0000000..4797cc6 --- /dev/null +++ b/src/hw/sdhc_generic.h @@ -0,0 +1,41 @@ + /***************************************************************************** + * + * Copyright (c) 2012-2014 Sage Electronic Engineering. All rights reserved. + * + * Software License Agreement + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * Sage Electronic Engineering SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR + * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + *****************************************************************************/ + +#ifndef __SDHC_GENERIC_H +#define __SDHC_GENERIC_H + +/** @file sdhc_generic.h */ +/* + * @brief SD PCI host controller driver header file. This driver is intended + * to be a generic driver for use with booting from SD cards. It + * only supports the minimum controls necessary to boot. + */ +#include <stdint.h> +#include "block.h" +#include "config.h" +#include "sd.h" + + +bool sdhc_init( sdHc_t* pSdCtrl ); +void sdhc_prepBoot( sdHc_t* pSdCtrl ); +bool sdhc_isInitialized( sdHc_t* pSdCtrl ); + +#endif /* __SDHC_GENERIC_H */ diff --git a/src/hw/sdhci.h b/src/hw/sdhci.h new file mode 100644 index 0000000..93ecd67 --- /dev/null +++ b/src/hw/sdhci.h @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2008 Alexander Motin mav@FreeBSD.org + * Copyright (C) 2013-2014 Sage Electronic Engineering, LLC + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * NOTE: This is from FreeBSD + */ +/* + * PCI registers + */ + + + + +#define PCI_SDHCI_IFPIO 0x00 +#define PCI_SDHCI_IFDMA 0x01 +#define PCI_SDHCI_IFVENDOR 0x02 + +#define PCI_SLOT_INFO 0x40 /* 8 bits */ +#define PCI_SLOT_INFO_SLOTS(x) (((x >> 4) & 7) + 1) +#define PCI_SLOT_INFO_FIRST_BAR(x) ((x) & 7) + +/* + * RICOH specific PCI registers + */ +#define SDHC_PCI_MODE_KEY 0xf9 +#define SDHC_PCI_MODE 0x150 +#define SDHC_PCI_MODE_SD20 0x10 +#define SDHC_PCI_BASE_FREQ_KEY 0xfc +#define SDHC_PCI_BASE_FREQ 0xe1 + +/* + * Controller registers + */ + +#define SDHCI_DMA_ADDRESS 0x00 + +#define SDHCI_BLOCK_SIZE 0x04 +#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) + +#define SDHCI_BLOCK_COUNT 0x06 + +#define SDHCI_ARGUMENT 0x08 + +#define SDHCI_TRANSFER_MODE 0x0C +#define SDHCI_TRNS_DMA 0x01 +#define SDHCI_TRNS_BLK_CNT_EN 0x02 +#define SDHCI_TRNS_ACMD12 0x04 +#define SDHCI_TRNS_READ 0x10 +#define SDHCI_TRNS_MULTI 0x20 + +#define SDHCI_COMMAND_FLAGS 0x0E +#define SDHCI_CMD_RESP_NONE 0x00 +#define SDHCI_CMD_RESP_LONG 0x01 +#define SDHCI_CMD_RESP_SHORT 0x02 +#define SDHCI_CMD_RESP_SHORT_BUSY 0x03 +#define SDHCI_CMD_RESP_MASK 0x03 +#define SDHCI_CMD_CRC 0x08 +#define SDHCI_CMD_INDEX 0x10 +#define SDHCI_CMD_DATA 0x20 +#define SDHCI_CMD_TYPE_NORMAL 0x00 +#define SDHCI_CMD_TYPE_SUSPEND 0x40 +#define SDHCI_CMD_TYPE_RESUME 0x80 +#define SDHCI_CMD_TYPE_ABORT 0xc0 +#define SDHCI_CMD_TYPE_MASK 0xc0 + +#define SDHCI_COMMAND 0x0F + +#define SDHCI_RESPONSE 0x10 + +#define SDHCI_BUFFER 0x20 + +#define SDHCI_PRESENT_STATE 0x24 +#define SDHCI_CMD_INHIBIT 0x00000001 +#define SDHCI_DAT_INHIBIT 0x00000002 +#define SDHCI_DAT_ACTIVE 0x00000004 +#define SDHCI_DOING_WRITE 0x00000100 +#define SDHCI_DOING_READ 0x00000200 +#define SDHCI_SPACE_AVAILABLE 0x00000400 +#define SDHCI_DATA_AVAILABLE 0x00000800 +#define SDHCI_CARD_PRESENT 0x00010000 +#define SDHCI_CARD_STABLE 0x00020000 +#define SDHCI_CARD_PIN 0x00040000 +#define SDHCI_WRITE_PROTECT 0x00080000 +#define SDHCI_STATE_DAT 0x00700000 +#define SDHCI_STATE_CMD 0x00800000 + +#define SDHCI_HOST_CONTROL 0x28 +#define SDHCI_CTRL_LED 0x01 +#define SDHCI_CTRL_4BITBUS 0x02 +#define SDHCI_CTRL_HISPD 0x04 +#define SDHCI_CTRL_SDMA 0x08 +#define SDHCI_CTRL_ADMA2 0x10 +#define SDHCI_CTRL_ADMA264 0x18 +#define SDHCI_CTRL_CARD_DET 0x40 +#define SDHCI_CTRL_FORCE_CARD 0x80 + +#define SDHCI_POWER_CONTROL 0x29 +#define SDHCI_POWER_ON 0x01 +#define SDHCI_POWER_180 0x0A +#define SDHCI_POWER_300 0x0C +#define SDHCI_POWER_330 0x0E + +#define SDHCI_BLOCK_GAP_CONTROL 0x2A + +#define SDHCI_WAKE_UP_CONTROL 0x2B + + +#define SDHCI_CLOCK_CONTROL 0x2C +#define SDHCI_DIVIDER_SHIFT 8 +#define SDHCI_CLOCK_CARD_EN 0x0004 +#define SDHCI_CLOCK_INT_STABLE 0x0002 +#define SDHCI_CLOCK_INT_EN 0x0001 + +#define SDHCI_TIMEOUT_CONTROL 0x2E + +#define SDHCI_SOFTWARE_RESET 0x2F +#define SDHCI_RESET_ALL 0x01 +#define SDHCI_RESET_CMD 0x02 +#define SDHCI_RESET_DATA 0x04 + +#define SDHCI_INT_STATUS 0x30 +#define SDHCI_INT_ENABLE 0x34 +#define SDHCI_SIGNAL_ENABLE 0x38 +#define SDHCI_INT_RESPONSE 0x00000001 +#define SDHCI_INT_DATA_END 0x00000002 +#define SDHCI_INT_BLOCK_GAP 0x00000004 +#define SDHCI_INT_DMA_END 0x00000008 +#define SDHCI_INT_SPACE_AVAIL 0x00000010 +#define SDHCI_INT_DATA_AVAIL 0x00000020 +#define SDHCI_INT_CARD_INSERT 0x00000040 +#define SDHCI_INT_CARD_REMOVE 0x00000080 +#define SDHCI_INT_CARD_INT 0x00000100 +#define SDHCI_INT_ERROR 0x00008000 +#define SDHCI_INT_TIMEOUT 0x00010000 +#define SDHCI_INT_CRC 0x00020000 +#define SDHCI_INT_END_BIT 0x00040000 +#define SDHCI_INT_INDEX 0x00080000 +#define SDHCI_INT_DATA_TIMEOUT 0x00100000 +#define SDHCI_INT_DATA_CRC 0x00200000 +#define SDHCI_INT_DATA_END_BIT 0x00400000 +#define SDHCI_INT_BUS_POWER 0x00800000 +#define SDHCI_INT_ACMD12ERR 0x01000000 +#define SDHCI_INT_ADMAERR 0x02000000 + +#define SDHCI_INT_NORMAL_MASK 0x00007FFF +#define SDHCI_INT_ERROR_MASK 0xFFFF8000 + +#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \ + SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) +#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ + SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ + SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ + SDHCI_INT_DATA_END_BIT) + +#define SDHCI_ACMD12_ERR 0x3C + +#define SDHCI_CAPABILITIES 0x40 +#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F +#define SDHCI_TIMEOUT_CLK_SHIFT 0 +#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 +#define SDHCI_CLOCK_BASE_MASK 0x00003F00 +#define SDHCI_CLOCK_BASE_SHIFT 8 +#define SDHCI_MAX_BLOCK_MASK 0x00030000 +#define SDHCI_MAX_BLOCK_SHIFT 16 +#define SDHCI_CAN_DO_ADMA2 0x00080000 +#define SDHCI_CAN_DO_HISPD 0x00200000 +#define SDHCI_CAN_DO_DMA 0x00400000 +#define SDHCI_CAN_DO_SUSPEND 0x00800000 +#define SDHCI_CAN_VDD_330 0x01000000 +#define SDHCI_CAN_VDD_300 0x02000000 +#define SDHCI_CAN_VDD_180 0x04000000 +#define SDHCI_CAN_DO_64BIT 0x10000000 + +#define SDHCI_MAX_CURRENT 0x48 + +#define SDHCI_SLOT_INT_STATUS 0xFC + +#define SDHCI_HOST_VERSION 0xFE +#define SDHCI_VENDOR_VER_MASK 0xFF00 +#define SDHCI_VENDOR_VER_SHIFT 8 +#define SDHCI_SPEC_VER_MASK 0x00FF +#define SDHCI_SPEC_VER_SHIFT 0 diff --git a/src/post.c b/src/post.c index 0fdd28e..556ff47 100644 --- a/src/post.c +++ b/src/post.c @@ -2,6 +2,7 @@ // // Copyright (C) 2008-2013 Kevin O'Connor kevin@koconnor.net // Copyright (C) 2002 MandrakeSoft S.A. +// Copyright (C) 2013-2014 Sage Electronic Engineering, LLC // // This file may be distributed under the terms of the GNU LGPLv3 license.
@@ -14,6 +15,7 @@ #include "hw/ata.h" // ata_setup #include "hw/esp-scsi.h" // esp_scsi_setup #include "hw/lsi-scsi.h" // lsi_scsi_setup +#include "hw/sd_if.h" // sd_setup #include "hw/megasas.h" // megasas_setup #include "hw/pvscsi.h" // pvscsi_setup #include "hw/pic.h" // pic_setup @@ -143,6 +145,7 @@ device_hardware_setup(void) floppy_setup(); ata_setup(); ahci_setup(); + sd_setup(); cbfs_payload_setup(); ramdisk_setup(); virtio_blk_setup(); diff --git a/src/stdbool.h b/src/stdbool.h new file mode 100644 index 0000000..71f75b0 --- /dev/null +++ b/src/stdbool.h @@ -0,0 +1,29 @@ + /***************************************************************************** + * + * Copyright (c) 2012-2014 Sage Electronic Engineering. All rights reserved. + * + * Software License Agreement + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * Sage Electronic Engineering SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR + * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + *****************************************************************************/ + +#ifndef __SEABIOS_STDBOOL_H +#define __SEABIOS_STDBOOL_H + +typedef int bool; +#define true 1 +#define false 0 + +#endif /* __SEABIOS_STDBOOL_H */ diff --git a/src/stdint.h b/src/stdint.h new file mode 100644 index 0000000..276119b --- /dev/null +++ b/src/stdint.h @@ -0,0 +1,41 @@ + /***************************************************************************** + * + * Copyright (c) 2012-2014 Sage Electronic Engineering. All rights reserved. + * + * Software License Agreement + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * Sage Electronic Engineering SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR + * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + *****************************************************************************/ + +#ifndef __SEABIOS_STDINT_H +#define __SEABIOS_STDINT_H + +#include "types.h" + +/* minimal stdint types for seabios non-specific portability */ + +typedef u8 uint8_t; +typedef s8 int8_t; + +typedef u16 uint16_t; +typedef s16 int16_t; + +typedef u32 uint32_t; +typedef s32 int32_t; + +typedef u64 uint64_t; +typedef s64 int64_t; + +#endif /* __SEABIOS_STDINT_H */