circuitpython/ports/stm/common-hal/sdioio/SDCard.c

327 lines
11 KiB
C

/*
* 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 <stdbool.h>
#include "shared-bindings/sdioio/SDCard.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/util.h"
#include "supervisor/board.h"
#include "common-hal/microcontroller/Pin.h"
#include "shared-bindings/microcontroller/Pin.h"
STATIC bool reserved_sdio[MP_ARRAY_SIZE(mcu_sdio_banks)];
STATIC bool never_reset_sdio[MP_ARRAY_SIZE(mcu_sdio_banks)];
STATIC const mcu_periph_obj_t *find_pin_function(const mcu_periph_obj_t *table, size_t sz, const mcu_pin_obj_t *pin, int periph_index) {
for (size_t i = 0; i < sz; i++, table++) {
if (periph_index == table->periph_index && pin == table->pin) {
return table;
}
}
return NULL;
}
// match pins to SDIO objects
STATIC int check_pins(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) {
bool sdio_taken = false;
const uint8_t sdio_clock_len = MP_ARRAY_SIZE(mcu_sdio_clock_list);
const uint8_t sdio_command_len = MP_ARRAY_SIZE(mcu_sdio_command_list);
const uint8_t sdio_data0_len = MP_ARRAY_SIZE(mcu_sdio_data0_list);
const uint8_t sdio_data1_len = MP_ARRAY_SIZE(mcu_sdio_data1_list);
const uint8_t sdio_data2_len = MP_ARRAY_SIZE(mcu_sdio_data2_list);
const uint8_t sdio_data3_len = MP_ARRAY_SIZE(mcu_sdio_data3_list);
// Loop over each possibility for clock. Check whether all other pins can
// be used on the same peripheral
for (uint i = 0; i < sdio_clock_len; i++) {
const mcu_periph_obj_t *mcu_sdio_clock = &mcu_sdio_clock_list[i];
if (mcu_sdio_clock->pin != clock) {
continue;
}
int periph_index = mcu_sdio_clock->periph_index;
const mcu_periph_obj_t *mcu_sdio_command = NULL;
if (!(mcu_sdio_command = find_pin_function(mcu_sdio_command_list, sdio_command_len, command, periph_index))) {
continue;
}
const mcu_periph_obj_t *mcu_sdio_data0 = NULL;
if (!(mcu_sdio_data0 = find_pin_function(mcu_sdio_data0_list, sdio_data0_len, data[0], periph_index))) {
continue;
}
const mcu_periph_obj_t *mcu_sdio_data1 = NULL;
if (num_data > 1 && !(mcu_sdio_data1 = find_pin_function(mcu_sdio_data1_list, sdio_data1_len, data[1], periph_index))) {
continue;
}
const mcu_periph_obj_t *mcu_sdio_data2 = NULL;
if (num_data > 2 && !(mcu_sdio_data2 = find_pin_function(mcu_sdio_data2_list, sdio_data2_len, data[2], periph_index))) {
continue;
}
const mcu_periph_obj_t *mcu_sdio_data3 = NULL;
if (num_data > 3 && !(mcu_sdio_data3 = find_pin_function(mcu_sdio_data3_list, sdio_data3_len, data[3], periph_index))) {
continue;
}
if (reserved_sdio[periph_index - 1]) {
sdio_taken = true;
continue;
}
self->clock = mcu_sdio_clock;
self->command = mcu_sdio_command;
self->data[0] = mcu_sdio_data0;
self->data[1] = mcu_sdio_data1;
self->data[2] = mcu_sdio_data2;
self->data[3] = mcu_sdio_data3;
return periph_index;
}
if (sdio_taken) {
mp_raise_ValueError(MP_ERROR_TEXT("Hardware in use, try alternative pins"));
} else {
raise_ValueError_invalid_pin();
}
}
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) {
int periph_index = check_pins(self, clock, command, num_data, data);
SDIO_TypeDef *SDIOx = mcu_sdio_banks[periph_index - 1];
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Configure data pins */
for (int i = 0; i < num_data; i++) {
GPIO_InitStruct.Pin = pin_mask(data[i]->number);
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = self->data[i]->altfn_index;
HAL_GPIO_Init(pin_port(data[i]->port), &GPIO_InitStruct);
}
/* Configure command pin */
GPIO_InitStruct.Alternate = self->command->altfn_index;
GPIO_InitStruct.Pin = pin_mask(command->number);
HAL_GPIO_Init(pin_port(command->port), &GPIO_InitStruct);
/* Configure clock */
GPIO_InitStruct.Alternate = self->clock->altfn_index;
GPIO_InitStruct.Pin = pin_mask(clock->number);
HAL_GPIO_Init(pin_port(clock->port), &GPIO_InitStruct);
__HAL_RCC_SDIO_CLK_ENABLE();
self->handle.Init.ClockDiv = SDIO_TRANSFER_CLK_DIV;
self->handle.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
self->handle.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
self->handle.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
self->handle.Init.BusWide = SDIO_BUS_WIDE_1B;
self->handle.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
self->handle.Instance = SDIOx;
HAL_StatusTypeDef r = HAL_SD_Init(&self->handle);
if (r != HAL_OK) {
mp_raise_ValueError_varg(MP_ERROR_TEXT("SDIO Init Error %d"), (int)r);
}
HAL_SD_CardInfoTypeDef info;
r = HAL_SD_GetCardInfo(&self->handle, &info);
if (r != HAL_OK) {
mp_raise_ValueError_varg(MP_ERROR_TEXT("SDIO GetCardInfo Error %d"), (int)r);
}
self->num_data = 1;
if (num_data == 4) {
if ((r = HAL_SD_ConfigWideBusOperation(&self->handle, SDIO_BUS_WIDE_4B)) == HAL_SD_ERROR_NONE) {
self->handle.Init.BusWide = SDIO_BUS_WIDE_4B;
self->num_data = 4;
} else {
}
}
self->capacity = info.BlockNbr * (info.BlockSize / 512);
self->frequency = 25000000;
reserved_sdio[periph_index - 1] = true;
common_hal_mcu_pin_claim(clock);
common_hal_mcu_pin_claim(command);
for (int i = 0; i < num_data; i++) {
common_hal_mcu_pin_claim(data[i]);
}
return;
}
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(MP_ERROR_TEXT("Buffer must be a multiple of 512 bytes"));
}
}
STATIC void wait_write_complete(sdioio_sdcard_obj_t *self) {
if (self->state_programming) {
HAL_SD_CardStateTypedef st = HAL_SD_CARD_PROGRAMMING;
// This waits up to 60s for programming to complete. This seems like
// an extremely long time, but this is the timeout that micropython's
// implementation uses
for (int i = 0; i < 60000 && st == HAL_SD_CARD_PROGRAMMING; i++) {
st = HAL_SD_GetCardState(&self->handle);
HAL_Delay(1);
};
self->state_programming = false;
}
}
STATIC void check_for_deinit(sdioio_sdcard_obj_t *self) {
if (common_hal_sdioio_sdcard_deinited(self)) {
raise_deinited_error();
}
}
int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) {
check_for_deinit(self);
check_whole_block(bufinfo);
wait_write_complete(self);
self->state_programming = true;
common_hal_mcu_disable_interrupts();
HAL_StatusTypeDef r = HAL_SD_WriteBlocks(&self->handle, bufinfo->buf, start_block, bufinfo->len / 512, 1000);
common_hal_mcu_enable_interrupts();
if (r != HAL_OK) {
return -EIO;
}
return 0;
}
int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) {
check_for_deinit(self);
check_whole_block(bufinfo);
wait_write_complete(self);
common_hal_mcu_disable_interrupts();
HAL_StatusTypeDef r = HAL_SD_ReadBlocks(&self->handle, bufinfo->buf, start_block, bufinfo->len / 512, 1000);
common_hal_mcu_enable_interrupts();
if (r != HAL_OK) {
return -EIO;
}
return 0;
}
bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) {
check_for_deinit(self);
return true;
}
bool common_hal_sdioio_sdcard_deinited(sdioio_sdcard_obj_t *self) {
return self->command == NULL;
}
STATIC void never_reset_mcu_periph(const mcu_periph_obj_t *periph) {
if (periph) {
never_reset_pin_number(periph->pin->port, periph->pin->number);
}
}
STATIC void reset_mcu_periph(const mcu_periph_obj_t *periph) {
if (periph) {
reset_pin_number(periph->pin->port, periph->pin->number);
}
}
void common_hal_sdioio_sdcard_deinit(sdioio_sdcard_obj_t *self) {
if (common_hal_sdioio_sdcard_deinited(self)) {
return;
}
reserved_sdio[self->command->periph_index - 1] = false;
never_reset_sdio[self->command->periph_index - 1] = false;
reset_mcu_periph(self->command);
self->command = NULL;
reset_mcu_periph(self->clock);
self->command = NULL;
for (size_t i = 0; i < MP_ARRAY_SIZE(self->data); i++) {
reset_mcu_periph(self->data[i]);
self->data[i] = NULL;
}
}
void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self) {
if (common_hal_sdioio_sdcard_deinited(self)) {
return;
}
if (never_reset_sdio[self->command->periph_index] - 1) {
return;
}
never_reset_sdio[self->command->periph_index - 1] = true;
never_reset_mcu_periph(self->command);
never_reset_mcu_periph(self->clock);
for (size_t i = 0; i < MP_ARRAY_SIZE(self->data); i++) {
never_reset_mcu_periph(self->data[i]);
}
}
void sdioio_reset() {
for (size_t i = 0; i < MP_ARRAY_SIZE(reserved_sdio); i++) {
if (!never_reset_sdio[i]) {
reserved_sdio[i] = false;
}
}
}