/* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2020 Jeff Epler for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include "mphalport.h" #include "py/mperrno.h" #include "py/runtime.h" #include "common-hal/microcontroller/Pin.h" #include "shared-bindings/sdioio/SDCard.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" #include "supervisor/port.h" #include "supervisor/shared/translate.h" #include "peripherals/broadcom/defines.h" #include "peripherals/broadcom/gpio.h" #include "lib/sdmmc/include/sdmmc_cmd.h" void osal_task_delay(uint32_t msec) { mp_hal_delay_ms(msec); } /*!< Host function to initialize the driver */ STATIC sdmmc_err_t _init(void) { return SDMMC_OK; } /*!< host function to set bus width */ STATIC sdmmc_err_t _set_bus_width(int slot, size_t width) { if (width == 4) { EMMC->CONTROL0_b.HCTL_DWIDTH = true; } else if (width == 8) { EMMC->CONTROL0_b.HCTL_8BIT = true; } return SDMMC_OK; } /*!< host function to get the maximum bus width of a particular slot */ STATIC size_t _get_bus_width(int slot) { return 4; } /*!< host function to set card clock frequency */ STATIC sdmmc_err_t _set_card_clk(int slot, uint32_t freq_khz) { uint32_t base_clock = 125000000; uint32_t frequency = freq_khz * 1000; uint64_t start_ticks = port_get_raw_ticks(NULL); while ((EMMC->STATUS_b.CMD_INHIBIT || EMMC->STATUS_b.DAT_INHIBIT) && (port_get_raw_ticks(NULL) - start_ticks) < 1024) { } if (EMMC->STATUS_b.CMD_INHIBIT || EMMC->STATUS_b.DAT_INHIBIT) { return SDMMC_ERR_TIMEOUT; } EMMC->CONTROL1_b.CLK_EN = false; uint32_t divisor = base_clock / frequency; // + 1 to ensure we round the frequency down. if (base_clock % frequency > 0) { divisor += 1; } // Only even divisors are supported uint32_t register_value = divisor / 2; if (divisor % 2 == 1) { register_value += 1; } // The divisor is stored with bits swapped. EMMC->CONTROL1_b.CLK_FREQ8 = divisor & 0xff; EMMC->CONTROL1_b.CLK_FREQ_MS2 = (divisor >> 8) & 0x3; EMMC->CONTROL1_b.CLK_EN = true; start_ticks = port_get_raw_ticks(NULL); while (!EMMC->CONTROL1_b.CLK_STABLE && (port_get_raw_ticks(NULL) - start_ticks) < 1024) { } if (!EMMC->CONTROL1_b.CLK_STABLE) { return SDMMC_ERR_TIMEOUT; } return SDMMC_OK; } /*!< host function to do a transaction */ STATIC sdmmc_err_t _do_transaction(int slot, sdmmc_command_t *cmdinfo) { if (EMMC->STATUS_b.CMD_INHIBIT) { return SDMMC_ERR_BUSY; } // Clear interrupts and then send the command EMMC->INTERRUPT = 0xffffffff; EMMC->ARG1 = cmdinfo->arg; uint32_t cmd_flags = 0; bool read = (cmdinfo->flags & SCF_CMD_READ) != 0; if (SCF_CMD(cmdinfo->flags) == SCF_CMD_ADTC || SCF_CMD(cmdinfo->flags) == (SCF_CMD_ADTC | SCF_CMD_READ)) { if (EMMC->STATUS_b.DAT_INHIBIT) { return SDMMC_ERR_BUSY; } cmd_flags = EMMC_CMDTM_TM_BLKCNT_EN_Msk | EMMC_CMDTM_CMD_ISDATA_Msk; if (cmdinfo->datalen > cmdinfo->blklen) { cmd_flags |= EMMC_CMDTM_TM_MULTI_BLOCK_Msk; if ((cmdinfo->flags & SCF_AUTO_STOP) != 0) { cmd_flags |= 1 << EMMC_CMDTM_TM_AUTO_CMD_EN_Pos; } } if (read) { cmd_flags |= EMMC_CMDTM_TM_DAT_DIR_Msk; } EMMC->BLKSIZECNT = (cmdinfo->datalen / cmdinfo->blklen) << EMMC_BLKSIZECNT_BLKCNT_Pos | cmdinfo->blklen << EMMC_BLKSIZECNT_BLKSIZE_Pos; } uint32_t response_type = EMMC_CMDTM_CMD_RSPNS_TYPE_RESPONSE_48BITS; uint32_t crc = 0; if ((cmdinfo->flags & SCF_RSP_CRC) != 0) { crc |= EMMC_CMDTM_CMD_CRCCHK_EN_Msk; } if ((cmdinfo->flags & SCF_RSP_IDX) != 0) { crc |= EMMC_CMDTM_CMD_IXCHK_EN_Msk; } if ((cmdinfo->flags & SCF_RSP_136) != 0) { response_type = EMMC_CMDTM_CMD_RSPNS_TYPE_RESPONSE_136BITS; } else if ((cmdinfo->flags & SCF_RSP_BSY) != 0) { response_type = EMMC_CMDTM_CMD_RSPNS_TYPE_RESPONSE_48BITS_USING_BUSY; } else if ((cmdinfo->flags & SCF_RSP_PRESENT) == 0) { response_type = EMMC_CMDTM_CMD_RSPNS_TYPE_RESPONSE_NONE; } uint32_t full_cmd = cmd_flags | crc | cmdinfo->opcode << EMMC_CMDTM_CMD_INDEX_Pos | response_type << EMMC_CMDTM_CMD_RSPNS_TYPE_Pos; EMMC->CMDTM = full_cmd; // Wait for an interrupt to indicate completion of the command. uint64_t start_ticks = port_get_raw_ticks(NULL); while (EMMC->INTERRUPT == 0 && (port_get_raw_ticks(NULL) - start_ticks) < (size_t)cmdinfo->timeout_ms) { } if (!EMMC->INTERRUPT_b.CMD_DONE) { cmdinfo->response[0] = 0; // Reset the command circuit EMMC->CONTROL1_b.SRST_CMD = true; // Wait for the bit to clear start_ticks = port_get_raw_ticks(NULL); while (EMMC->CONTROL1_b.SRST_CMD && (port_get_raw_ticks(NULL) - start_ticks) < (size_t)cmdinfo->timeout_ms) { } return SDMMC_ERR_TIMEOUT; } else { EMMC->INTERRUPT = EMMC_INTERRUPT_CMD_DONE_Msk; } // Transfer the data. // TODO: Use DMA if (cmd_flags != 0) { if (read) { for (size_t i = 0; i < cmdinfo->datalen / sizeof(uint32_t); i++) { start_ticks = port_get_raw_ticks(NULL); while (!EMMC->INTERRUPT_b.READ_RDY && (port_get_raw_ticks(NULL) - start_ticks) < (size_t)cmdinfo->timeout_ms) { } if (!EMMC->INTERRUPT_b.READ_RDY) { return SDMMC_ERR_TIMEOUT; } ((uint32_t *)cmdinfo->data)[i] = EMMC->DATA; } } else { for (size_t i = 0; i < cmdinfo->datalen / sizeof(uint32_t); i++) { start_ticks = port_get_raw_ticks(NULL); while (!EMMC->INTERRUPT_b.WRITE_RDY && (port_get_raw_ticks(NULL) - start_ticks) < (size_t)cmdinfo->timeout_ms) { } if (!EMMC->INTERRUPT_b.WRITE_RDY) { return SDMMC_ERR_TIMEOUT; } EMMC->DATA = ((uint32_t *)cmdinfo->data)[i]; } } uint32_t data_done_mask = EMMC_INTERRUPT_ERR_Msk | EMMC_INTERRUPT_DATA_DONE_Msk; start_ticks = port_get_raw_ticks(NULL); while ((EMMC->INTERRUPT & data_done_mask) == 0 && (port_get_raw_ticks(NULL) - start_ticks) < (size_t)cmdinfo->timeout_ms) { } if (!EMMC->INTERRUPT_b.DATA_DONE) { cmdinfo->response[0] = 0; // Reset the command circuit EMMC->CONTROL1_b.SRST_DATA = true; // Wait for the bit to clear start_ticks = port_get_raw_ticks(NULL); while (EMMC->CONTROL1_b.SRST_DATA && (port_get_raw_ticks(NULL) - start_ticks) < (size_t)cmdinfo->timeout_ms) { } return SDMMC_ERR_TIMEOUT; } } cmdinfo->response[0] = EMMC->RESP0; if (response_type == EMMC_CMDTM_CMD_RSPNS_TYPE_RESPONSE_136BITS) { // Rotate one more byte for some reason. cmdinfo->response[3] = EMMC->RESP3 << 8 | ((EMMC->RESP2 >> 24) & 0xff); cmdinfo->response[2] = EMMC->RESP2 << 8 | ((EMMC->RESP1 >> 24) & 0xff); cmdinfo->response[1] = EMMC->RESP1 << 8 | ((EMMC->RESP0 >> 24) & 0xff); cmdinfo->response[0] <<= 8; } return SDMMC_OK; } /*!< host function to deinitialize the driver, called with the `slot` */ STATIC sdmmc_err_t _deinit(int slot) { return SDMMC_OK; } void common_hal_sdioio_sdcard_construct(sdioio_sdcard_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *command, uint8_t num_data, const mcu_pin_obj_t **data, uint32_t frequency) { if (clock != NULL) { gpio_set_function(clock->number, GPIO_GPFSEL4_FSEL48_SD1_CLK); gpio_set_pull(clock->number, BP_PULL_NONE); self->clock_pin = clock->number; gpio_set_function(command->number, GPIO_GPFSEL4_FSEL49_SD1_CMD); gpio_set_pull(command->number, BP_PULL_UP); self->command_pin = command->number; gpio_set_function(data[0]->number, GPIO_GPFSEL5_FSEL50_SD1_DAT0); gpio_set_pull(data[0]->number, BP_PULL_UP); self->data_pins[0] = data[0]->number; gpio_set_function(data[1]->number, GPIO_GPFSEL5_FSEL51_SD1_DAT1); gpio_set_pull(data[1]->number, BP_PULL_UP); self->data_pins[1] = data[1]->number; gpio_set_function(data[2]->number, GPIO_GPFSEL5_FSEL52_SD1_DAT2); gpio_set_pull(data[2]->number, BP_PULL_UP); self->data_pins[2] = data[2]->number; gpio_set_function(data[3]->number, GPIO_GPFSEL5_FSEL53_SD1_DAT3); gpio_set_pull(data[3]->number, BP_PULL_UP); self->data_pins[3] = data[3]->number; } else { // Switch the sdcard to use the old arasan interface. GPIO->EXTRA_MUX_b.SDIO = GPIO_EXTRA_MUX_SDIO_ARASAN; } self->host_info = (sdmmc_host_t) { .flags = SDMMC_HOST_FLAG_1BIT | SDMMC_HOST_FLAG_4BIT | SDMMC_HOST_FLAG_DEINIT_ARG, .slot = 0, .max_freq_khz = SDMMC_FREQ_HIGHSPEED, .io_voltage = 3.3f, .command_timeout_ms = 0, .init = _init, .set_bus_width = _set_bus_width, .get_bus_width = _get_bus_width, .set_bus_ddr_mode = NULL, .set_card_clk = _set_card_clk, .do_transaction = _do_transaction, .deinit_p = _deinit, }; // Reset the peripheral. EMMC->CONTROL0 = 0; EMMC->CONTROL1_b.SRST_HC = true; // Wait for the bit to clear while (EMMC->CONTROL1_b.SRST_HC) { } // Set max timeout EMMC->CONTROL1 |= EMMC_CONTROL1_CLK_INTLEN_Msk | (0xe << EMMC_CONTROL1_DATA_TOUNIT_Pos); EMMC->IRPT_MASK = 0xffffffff; // Start clocking the card. _set_card_clk(0, 400); sdmmc_err_t err = SDMMC_ERR_INVALID_RESPONSE; size_t tries = 3; while (err == SDMMC_ERR_INVALID_RESPONSE && tries > 0) { err = sdmmc_card_init(&self->host_info, &self->card_info); if (err != SDMMC_OK) { mp_printf(&mp_plat_print, "SD card init failed %d\n", err); } else if (tries < 3) { mp_printf(&mp_plat_print, "SD card init success\n"); } tries--; } self->init = err == SDMMC_OK; self->capacity = self->card_info.csd.capacity; } uint32_t common_hal_sdioio_sdcard_get_count(sdioio_sdcard_obj_t *self) { return self->capacity; } uint32_t common_hal_sdioio_sdcard_get_frequency(sdioio_sdcard_obj_t *self) { return self->frequency; } uint8_t common_hal_sdioio_sdcard_get_width(sdioio_sdcard_obj_t *self) { return self->num_data; } STATIC void check_whole_block(mp_buffer_info_t *bufinfo) { if (bufinfo->len % 512) { mp_raise_ValueError(translate("Buffer length must be a multiple of 512")); } } int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (!self->init) { return -EIO; } check_whole_block(bufinfo); self->state_programming = true; sdmmc_err_t error = sdmmc_write_sectors(&self->card_info, bufinfo->buf, start_block, bufinfo->len / 512); if (error != SDMMC_OK) { mp_printf(&mp_plat_print, "write sectors result %d\n", error); return -EIO; } return 0; } int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (!self->init) { return -EIO; } check_whole_block(bufinfo); sdmmc_err_t error = sdmmc_read_sectors(&self->card_info, bufinfo->buf, start_block, bufinfo->len / 512); if (error != SDMMC_OK) { mp_printf(&mp_plat_print, "read sectors result %d when reading block %d for %d\n", error, start_block, bufinfo->len / 512); return -EIO; } return 0; } bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) { if (!self->init) { return false; } return true; } bool common_hal_sdioio_sdcard_deinited(sdioio_sdcard_obj_t *self) { return self->command_pin == COMMON_HAL_MCU_NO_PIN; } void common_hal_sdioio_sdcard_deinit(sdioio_sdcard_obj_t *self) { reset_pin_number(self->command_pin); reset_pin_number(self->clock_pin); reset_pin_number(self->data_pins[0]); reset_pin_number(self->data_pins[1]); reset_pin_number(self->data_pins[2]); reset_pin_number(self->data_pins[3]); self->command_pin = COMMON_HAL_MCU_NO_PIN; self->clock_pin = COMMON_HAL_MCU_NO_PIN; self->data_pins[0] = COMMON_HAL_MCU_NO_PIN; self->data_pins[1] = COMMON_HAL_MCU_NO_PIN; self->data_pins[2] = COMMON_HAL_MCU_NO_PIN; self->data_pins[3] = COMMON_HAL_MCU_NO_PIN; self->init = false; } void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self) { never_reset_pin_number(self->command_pin); never_reset_pin_number(self->clock_pin); never_reset_pin_number(self->data_pins[0]); never_reset_pin_number(self->data_pins[1]); never_reset_pin_number(self->data_pins[2]); never_reset_pin_number(self->data_pins[3]); }