--- /dev/null
+/****************************************************************************
+ * extdrv/sdmmc.c
+ *
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ * Copyright 2012 Gabriel Huau <contact@huau-gabriel.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include "core/system.h"
+#include "lib/string.h"
+#include "lib/errno.h"
+#include "drivers/gpio.h"
+#include "drivers/ssp.h"
+
+#include "extdrv/sdmmc.h"
+
+
+
+
+/***************************************************************************** */
+/* Support for SD/MMC cards access over SSP/SPI bus */
+/***************************************************************************** */
+
+
+static inline void sdmmc_cs_activate(const struct sdmmc_card* mmc)
+{
+ gpio_clear(mmc->chip_select);
+}
+static inline void sdmmc_cs_release(const struct sdmmc_card* mmc)
+{
+ gpio_set(mmc->chip_select);
+}
+
+/* Wait for MMC Card to be ready.
+ * return -EBUSY on timeout.
+ * Note : The SPI Bus mutex must be held by the calling function.
+ * FIXME : handle timeout with sleep()
+ */
+static int sdmmc_wait_for_ready(const struct sdmmc_card* mmc, uint8_t token)
+{
+ int i = 0;
+ uint8_t val = 0;
+
+ while (i++ < MMC_MAX_TIMEOUT) {
+ val = (uint8_t)spi_transfer_single_frame(mmc->ssp_bus_num, 0xFF);
+ if (token != 0) {
+ if (val == token)
+ return val;
+ } else {
+ if (val != 0)
+ return val;
+ }
+ }
+
+ return -EBUSY;
+}
+
+uint8_t sdmmc_crc7(uint8_t* data, int len)
+{
+ int i, idx;
+ uint8_t crc = 0, val = 0;
+
+ for (idx = 0; idx < len; idx++) {
+ val = data[idx];
+ for (i = 0; i < 8; i++) {
+ crc <<= 1;
+ if ((val & 0x80) ^ (crc & 0x80)) {
+ crc ^= 0x09;
+ }
+ val <<= 1;
+ }
+ }
+ crc = (crc << 1) | 1;
+ return crc;
+}
+
+
+/* Send a command to the card.
+ * The len parameter indicates the response type (and is the length of the buffer) :
+ * R1 : len = 0
+ * R1b : len = -1
+ * R2 : len = 1
+ * R3 or R7 : len = 4
+ * Note : The SPI Bus mutex must be held by the calling function.
+ * Return R1 or (R1 | 0x0100) on timeout.
+ */
+static int sdmmc_send_command(const struct sdmmc_card* mmc, uint8_t index, uint32_t arg, uint8_t* buf, int len)
+{
+ uint8_t mmc_command[MMC_CMD_SIZE];
+ int i = 0;
+ uint8_t r1 = 0;
+
+ if ((len > 0) && (buf == NULL)) {
+ return -EINVAL;
+ }
+
+ /* Command buffer */
+ mmc_command[0] = 0x40 | index;
+ mmc_command[1] = (arg >> 24) & 0xFF;
+ mmc_command[2] = (arg >> 16) & 0xFF;
+ mmc_command[3] = (arg >> 8) & 0xFF;
+ mmc_command[4] = arg & 0xFF;
+ mmc_command[5] = sdmmc_crc7(mmc_command, 5);
+
+ /* Send command */
+ spi_transfer_multiple_frames(mmc->ssp_bus_num, mmc_command, NULL, MMC_CMD_SIZE, 8);
+
+ /* Get R1 */
+ for (i = 0; i < 8; i++) {
+ r1 = (uint8_t)spi_transfer_single_frame(mmc->ssp_bus_num, 0xFF);
+ if (r1 != 0xFF) {
+ break;
+ }
+ }
+ /* Maybe get R2 ? */
+ if (len == 1) {
+ *buf = (uint8_t)spi_transfer_single_frame(mmc->ssp_bus_num, 0xFF);
+ }
+ /* Error ? Do not wait for the data in case of error */
+ if (r1 > MMC_R1_IN_IDLE_STATE) {
+ return r1;
+ }
+ /* Wait for end of busy state ? (The R1 + busy case) */
+ if (len == -1) {
+ int ret = sdmmc_wait_for_ready(mmc, 0);
+ if (ret == -EBUSY) {
+ return (r1 | 0x0100);
+ }
+ }
+ if (len > 1) {
+ /* Receive response data */
+ spi_transfer_multiple_frames(mmc->ssp_bus_num, NULL, buf, len, 8);
+ }
+
+ return r1;
+}
+
+static int sdmmc_send_app_command(const struct sdmmc_card* mmc, uint8_t index, uint32_t arg)
+{
+ int r1 = 0;
+
+ /* Send APP_CMD (CMD55) first */
+ r1 = sdmmc_send_command(mmc, MMC_APP_CMD, 0, NULL, 0);
+ if (r1 > MMC_R1_IN_IDLE_STATE) {
+ return r1;
+ }
+ return sdmmc_send_command(mmc, index, arg, NULL, 0);
+}
+
+
+int sdmmc_init(struct sdmmc_card* mmc)
+{
+ int r1 = 0, ret = 0;
+ uint8_t buf[MMC_CMD_SIZE];
+
+ config_gpio(&(mmc->chip_select), LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 1);
+ mmc->card_type = MMC_CARDTYPE_UNKNOWN;
+
+ /* Get SPI Bus */
+ spi_get_mutex(mmc->ssp_bus_num);
+ sdmmc_cs_activate(mmc);
+
+ /* Send CMD0 (RESET or GO_IDLE_STATE) */
+ r1 = sdmmc_send_command(mmc, MMC_GO_IDLE_STATE, 0, NULL, 0);
+ if (r1 > MMC_R1_IN_IDLE_STATE) {
+ ret = -EIO; /* This one should have succeded even without a card inserted ... */
+ goto init_release_bus;
+ }
+ msleep(1);
+
+ /* Send CMD8, check the card type
+ * CMD8 is the Send interface Condition Command.
+ * 0x01 is "2.7V to 3.6V"
+ * 0xAA is a "check pattern", which the card must send back.
+ */
+ r1 = sdmmc_send_command(mmc, MMC_SEND_IF_COND, 0x1AA, buf, 4);
+ if (r1 > MMC_R1_IN_IDLE_STATE) {
+ ret = -ENODEV; /* No card inserted */
+ goto init_release_bus;
+ }
+ if (r1 == MMC_R1_IN_IDLE_STATE) {
+ /* Card is SD V2 */
+ if (buf[2] != 0x01 || buf[3] != 0xAA) {
+ ret = -EREMOTEIO;
+ mmc->card_type = MMC_CARDTYPE_UNKNOWN;
+ goto init_release_bus;
+ }
+ mmc->card_type = MMC_CARDTYPE_SDV2_SC;
+ } else {
+ mmc->card_type = MMC_CARDTYPE_SDV1; /* Card is 2.1mm thick SD Card */
+ }
+
+ /* Read OCR (CMD58) */
+ if (mmc->card_type == MMC_CARDTYPE_SDV2_SC) {
+ r1 = sdmmc_send_command(mmc, MMC_READ_OCR, 0, buf, 4);
+ if (r1 > MMC_R1_IN_IDLE_STATE) {
+ ret = -EREMOTEIO;
+ goto init_release_bus;
+ }
+ }
+
+ /* Send first ACMD41 command */
+ r1 = sdmmc_send_app_command(mmc, MMC_SD_SEND_OP_COND, SDHC_SUPPORT_OK);
+ if (r1 > MMC_R1_IN_IDLE_STATE) {
+ ret = -EREMOTEIO;
+ }
+
+init_release_bus:
+ /* Release SPI Bus */
+ sdmmc_cs_release(mmc);
+ spi_release_mutex(mmc->ssp_bus_num);
+
+ return ret;
+}
+
+int sdmmc_init_wait_card_ready(struct sdmmc_card* mmc)
+{
+ int r1 = 0;
+
+ /* Get SPI Bus */
+ spi_get_mutex(mmc->ssp_bus_num);
+ sdmmc_cs_activate(mmc);
+
+ r1 = sdmmc_send_app_command(mmc, MMC_SD_SEND_OP_COND, SDHC_SUPPORT_OK);
+
+ /* Release SPI Bus */
+ sdmmc_cs_release(mmc);
+ spi_release_mutex(mmc->ssp_bus_num);
+
+ return r1;
+}
+
+int sdmmc_init_end(struct sdmmc_card* mmc)
+{
+ int r1 = 0, ret = 0;
+ uint8_t buf[MMC_CMD_SIZE];
+
+ /* Get SPI Bus */
+ spi_get_mutex(mmc->ssp_bus_num);
+ sdmmc_cs_activate(mmc);
+
+ /* Read OCR (CMD58) */
+ if (mmc->card_type == MMC_CARDTYPE_SDV2_SC) {
+ r1 = sdmmc_send_command(mmc, MMC_READ_OCR, 0, buf, 4);
+ if (r1 > MMC_R1_IN_IDLE_STATE) {
+ ret = -EREMOTEIO;
+ goto end_init_release_bus;
+ }
+ mmc->card_type = (buf[0] & 0x40) ? MMC_CARDTYPE_SDV2_HC : MMC_CARDTYPE_SDV2_SC;
+ }
+
+ /* Set block length */
+ if (mmc->card_type == MMC_CARDTYPE_SDV2_HC) {
+ mmc->block_size = MMC_MAX_SECTOR_SIZE;
+ } else {
+ if (mmc->block_size > MMC_MAX_SECTOR_SIZE) {
+ mmc->block_size = MMC_MAX_SECTOR_SIZE;
+ }
+ r1 = sdmmc_send_command(mmc, MMC_SET_BLOCKLEN, mmc->block_size, NULL, 0);
+ if (r1 > MMC_R1_IN_IDLE_STATE) {
+ mmc->card_type = MMC_CARDTYPE_UNKNOWN;
+ }
+ }
+
+end_init_release_bus:
+ /* Release SPI Bus */
+ sdmmc_cs_release(mmc);
+ spi_release_mutex(mmc->ssp_bus_num);
+
+ return ret;
+}
+
+
+/* Read one block of data.
+ * Return -ENODEV on error, -EBUSY on timeout, or CRC on success
+ */
+int sdmmc_read_block(const struct sdmmc_card* mmc, uint32_t block_number, uint8_t* buffer)
+{
+ uint16_t crc = 0xFFFF;
+ int ret = 0;
+
+ if ((buffer == NULL) || (mmc->card_type == MMC_CARDTYPE_UNKNOWN)) {
+ return -EINVAL;
+ }
+
+ /* Garbage for the SPI out data */
+ memset(buffer, 0xFF, mmc->block_size);
+
+ /* Get SPI Bus */
+ spi_get_mutex(mmc->ssp_bus_num);
+ sdmmc_cs_activate(mmc);
+
+ ret = sdmmc_send_command(mmc, MMC_READ_SINGLE_BLOCK, block_number, NULL, 0);
+ if (ret != MMC_R1_NO_ERROR) {
+ ret = -ENODEV;
+ goto read_release;
+ }
+
+ /* Wait for start of Data */
+ ret = sdmmc_wait_for_ready(mmc, MMC_START_DATA_BLOCK_TOCKEN);
+ if (ret != MMC_START_DATA_BLOCK_TOCKEN) {
+ ret = -EBUSY;
+ goto read_release;
+ }
+
+ /* Read data */
+ spi_transfer_multiple_frames(mmc->ssp_bus_num, NULL, buffer, mmc->block_size, 8);
+ /* Read CRC */
+ spi_transfer_multiple_frames(mmc->ssp_bus_num, NULL, (uint8_t*)(&crc), 2, 8);
+
+ ret = crc;
+
+read_release:
+ /* Release SPI Bus */
+ sdmmc_cs_release(mmc);
+ spi_release_mutex(mmc->ssp_bus_num);
+ return ret;
+}
+
+
--- /dev/null
+/****************************************************************************
+ * extdrv/sdmmc.h
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ * Copyright 2012 Gabriel Huau <contact@huau-gabriel.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+#ifndef EXTDRV_SDMMC_H
+#define EXTDRV_SDMMC_H
+
+#include "lib/stdint.h"
+#include "core/pio.h"
+
+
+/***************************************************************************** */
+/* Support for SD/MMC cards access over SSP/SPI bus */
+/***************************************************************************** */
+
+
+struct sdmmc_card {
+ uint8_t ssp_bus_num;
+ uint8_t card_type;
+ uint16_t block_size;
+ struct pio chip_select;
+};
+
+int sdmmc_init(struct sdmmc_card* mmc);
+int sdmmc_init_wait_card_ready(struct sdmmc_card* mmc);
+int sdmmc_init_end(struct sdmmc_card* mmc);
+
+int sdmmc_read_block(const struct sdmmc_card* mmc, uint32_t block_number, uint8_t *buffer);
+
+
+/* Card states and Operation modes */
+/* Inactive operation mode */
+#define MMC_OP_MODE_INACTIVE 0
+#define MMC_CARD_STATE_INACTIVE 0
+/* Identification operation mode */
+#define MMC_OP_MODE_IDENTIFICATION 1
+#define MMC_CARD_STATE_IDLE 1
+#define MMC_CARD_STATE_READY 2
+#define MMC_CARD_STATE_IDENTIFICATION 3
+/* Data transfer mode */
+#define MMC_OP_MODE_DATA_TRANSFER 4
+#define MMC_CARD_STATE_STANDBY 4
+#define MMC_CARD_STATE_TRANSFER 5
+#define MMC_CARD_STATE_SENDING_DATA 6
+#define MMC_CARD_STATE_RECEIVE_DATA 7
+#define MMC_CARD_STATE_PROGRAMMING 8
+#define MMC_CARD_STATE_DISCONNECT 9
+
+
+/* Command definitions in SPI bus mode
+ * Response type is R1 unless specified
+ */
+#define MMC_GO_IDLE_STATE 0
+#define MMC_SEND_OP_COND 1
+#define MMC_SWITCH_FUNC 6
+#define MMC_SEND_IF_COND 8 /* R7 (R1 + 4 bytes) */
+#define MMC_SEND_CSD 9
+#define MMC_SEND_CID 10
+#define MMC_STOP_TRANSMISSION 12 /* R1b (busy) */
+#define MMC_SEND_STATUS 13 /* R2 (2 bytes) */
+#define MMC_SET_BLOCKLEN 16
+#define MMC_READ_SINGLE_BLOCK 17
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_WRITE_SINGLE_BLOCK 24
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_PROGRAMM_CSD 27
+/* Write protect commands are unsupported by SDHC and SDXC cards */
+#define MMC_SET_WRITE_PROTECT 28 /* R1b (busy) */
+#define MMC_CLR_WRITE_PROTECT 29 /* R1b (busy) */
+#define MMC_SEND_WRITE_PROTECT 30
+#define ERASE_WR_BLK_START_ADDR 32
+#define ERASE_WR_BLK_END_ADDR 33
+#define MMC_ERASE 38 /* R1b (busy) */
+#define MMC_LOCK_UNLOCK 42
+#define MMC_APP_CMD 55
+#define MMC_GEN_CMD 56
+#define MMC_READ_OCR 58 /* R3 (R1 + 4 bytes of OCR )*/
+#define MMC_CRC_ON_OFF 59
+
+/* Application specific commands supported by SD.
+ * All these commands shall be preceded with APP_CMD (CMD55).
+ */
+#define MMC_SD_SEND_STATUS 13 /* R2 (2 bytes) */
+#define MMC_SD_SEND_NUM_WR_BLOCKS 22
+#define MMC_SD_SET_WR_BLK_ERASE_COUNT 23
+#define MMC_SD_SEND_OP_COND 41
+#define MMC_SD_SET_CLR_CARD_DETECT 42
+#define MMC_SD_SEND_SCR 51
+
+/* HCS bit */
+#define SDHC_SUPPORT_OK (0x01 << 30)
+
+
+/* R1 response bit flag definition */
+#define MMC_R1_NO_ERROR 0x00
+#define MMC_R1_IN_IDLE_STATE (0x01 << 0)
+#define MMC_R1_ERASE_RESET (0x01 << 1)
+#define MMC_R1_ILLEGAL_CMD (0x01 << 2)
+#define MMC_R1_COM_CRC_ERROR (0x01 << 3)
+#define MMC_R1_ERASE_SEQ_ERROR (0x01 << 4)
+#define MMC_R1_ADDRESS_ERROR (0x01 << 5)
+#define MMC_R1_PARAMETER_ERROR (0x01 << 6)
+#define MMC_R1_MASK 0x7F
+
+/* Memory card type definitions */
+#define MMC_CARDTYPE_UNKNOWN 0
+#define MMC_CARDTYPE_MMC 1 /* MMC */
+#define MMC_CARDTYPE_SDV1 2 /* V1.x Standard Capacity SD card */
+#define MMC_CARDTYPE_SDV2_SC 3 /* V2.0 or later Standard Capacity SD card */
+#define MMC_CARDTYPE_SDV2_HC 4 /* V2.0 or later High/eXtended Capacity SD card */
+
+/* The sector size is fixed to 512bytes in most applications. */
+#define MMC_MAX_SECTOR_SIZE 512
+#define MMC_SECTOR_BITS 9
+
+/* Buffer CMD/DATA size */
+#define MMC_CMD_SIZE 6
+#define MMC_DATA_SIZE 512
+
+#define MMC_MAX_TIMEOUT 10000
+
+
+#define MMC_CMD8_CHECK_PATERN 0xAA
+#define MMC_VOLTAGE_SELECT_3V3 0x01
+
+
+#define MMC_START_DATA_BLOCK_TOCKEN 0xFE
+
+#define MMC_IS_WRITE_RESPONSE_TOKEN(x) (((x) & 0x11) == 0x01)
+#define MMC_WRITE_RESPONSE_TOKEN(x) (((x) & 0x0E) >> 1)
+#define MMC_WRITE_RESPONSE_OK 2
+#define MMC_WRITE_RESPONSE_CRC_ERR 5
+#define MMC_WRITE_RESPONSE_WR_ERROR 6
+
+#endif /* EXTDRV_SDMMC_H */
+
+