stm32: Add sdio support
Adds sdio support for the stm32f4 to allow for SD card flash updates without power cycling some boards, e.g. BTT Octopus Pro. Signed-off-by: H. Gregor Molter <gregor.molter@secretlab.de> Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
848a78d1a5
commit
d7bd7f1f4b
|
@ -1,7 +1,8 @@
|
||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python2
|
||||||
# Module supporting uploads Klipper firmware to an SD Card via SPI
|
# Module supporting uploads Klipper firmware to an SD Card via SPI and SDIO
|
||||||
#
|
#
|
||||||
# Copyright (C) 2021 Eric Callahan <arksine.code@gmail.com>
|
# Copyright (C) 2021 Eric Callahan <arksine.code@gmail.com>
|
||||||
|
# Copyright (C) 2022 H. Gregor Molter <gregor.molter@secretlab.de>
|
||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import sys
|
import sys
|
||||||
|
@ -36,7 +37,7 @@ def output(msg):
|
||||||
sys.stdout.write("%s" % (msg,))
|
sys.stdout.write("%s" % (msg,))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
def calc_crc7(data):
|
def calc_crc7(data, with_padding=True):
|
||||||
# G(x) = x^7 + x^3 + 1
|
# G(x) = x^7 + x^3 + 1
|
||||||
# Shift left as we are only calculating a 7 bit CRC
|
# Shift left as we are only calculating a 7 bit CRC
|
||||||
poly = 0b10001001 << 1
|
poly = 0b10001001 << 1
|
||||||
|
@ -47,6 +48,8 @@ def calc_crc7(data):
|
||||||
crc = (crc << 1) ^ poly if crc & 0x80 else crc << 1
|
crc = (crc << 1) ^ poly if crc & 0x80 else crc << 1
|
||||||
# The sdcard protocol likes the crc left justfied with a
|
# The sdcard protocol likes the crc left justfied with a
|
||||||
# padded bit
|
# padded bit
|
||||||
|
if not with_padding:
|
||||||
|
return crc
|
||||||
return crc | 1
|
return crc | 1
|
||||||
|
|
||||||
def calc_crc16(data):
|
def calc_crc16(data):
|
||||||
|
@ -89,11 +92,12 @@ def check_need_convert(board_name, config):
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
#
|
#
|
||||||
# SPI FLash Implementation
|
# SPI / SDIO Flash Implementation
|
||||||
#
|
#
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
SPI_OID = 0
|
SPI_OID = 0
|
||||||
|
SDIO_OID = 0
|
||||||
SPI_MODE = 0
|
SPI_MODE = 0
|
||||||
SD_SPI_SPEED = 400000
|
SD_SPI_SPEED = 400000
|
||||||
# MCU Command Constants
|
# MCU Command Constants
|
||||||
|
@ -114,6 +118,20 @@ SW_SPI_BUS_CMD = "spi_set_software_bus oid=%d " \
|
||||||
SPI_SEND_CMD = "spi_send oid=%c data=%*s"
|
SPI_SEND_CMD = "spi_send oid=%c data=%*s"
|
||||||
SPI_XFER_CMD = "spi_transfer oid=%c data=%*s"
|
SPI_XFER_CMD = "spi_transfer oid=%c data=%*s"
|
||||||
SPI_XFER_RESPONSE = "spi_transfer_response oid=%c response=%*s"
|
SPI_XFER_RESPONSE = "spi_transfer_response oid=%c response=%*s"
|
||||||
|
SDIO_CFG_CMD = "config_sdio oid=%d blocksize=%u"
|
||||||
|
SDIO_BUS_CMD = "sdio_set_bus oid=%d sdio_bus=%s"
|
||||||
|
SDIO_SEND_CMD = "sdio_send_command oid=%c cmd=%c argument=%u wait=%c"
|
||||||
|
SDIO_SEND_CMD_RESPONSE = "sdio_send_command_response oid=%c error=%c " \
|
||||||
|
"response=%*s"
|
||||||
|
SDIO_READ_DATA="sdio_read_data oid=%c cmd=%c argument=%u"
|
||||||
|
SDIO_READ_DATA_RESPONSE="sdio_read_data_response oid=%c error=%c read=%u"
|
||||||
|
SDIO_WRITE_DATA="sdio_write_data oid=%c cmd=%c argument=%u"
|
||||||
|
SDIO_WRITE_DATA_RESPONSE="sdio_write_data_response oid=%c error=%c write=%u"
|
||||||
|
SDIO_READ_DATA_BUFFER="sdio_read_data_buffer oid=%c offset=%u len=%c"
|
||||||
|
SDIO_READ_DATA_BUFFER_RESPONSE="sdio_read_data_buffer_response oid=%c data=%*s"
|
||||||
|
SDIO_WRITE_DATA_BUFFER="sdio_write_data_buffer oid=%c offset=%u data=%*s"
|
||||||
|
SDIO_SET_SPEED="sdio_set_speed oid=%c speed=%u"
|
||||||
|
|
||||||
FINALIZE_CFG_CMD = "finalize_config crc=%d"
|
FINALIZE_CFG_CMD = "finalize_config crc=%d"
|
||||||
|
|
||||||
class SPIFlashError(Exception):
|
class SPIFlashError(Exception):
|
||||||
|
@ -135,6 +153,40 @@ class SPIDirect:
|
||||||
def spi_transfer(self, data):
|
def spi_transfer(self, data):
|
||||||
return self._spi_transfer_cmd.send([self.oid, data])
|
return self._spi_transfer_cmd.send([self.oid, data])
|
||||||
|
|
||||||
|
class SDIODirect:
|
||||||
|
def __init__(self, ser):
|
||||||
|
self.oid = SDIO_OID
|
||||||
|
self._sdio_send_cmd = mcu.CommandQueryWrapper(
|
||||||
|
ser, SDIO_SEND_CMD, SDIO_SEND_CMD_RESPONSE, self.oid)
|
||||||
|
self._sdio_read_data = mcu.CommandQueryWrapper(
|
||||||
|
ser, SDIO_READ_DATA, SDIO_READ_DATA_RESPONSE, self.oid)
|
||||||
|
self._sdio_write_data = mcu.CommandQueryWrapper(
|
||||||
|
ser, SDIO_WRITE_DATA, SDIO_WRITE_DATA_RESPONSE, self.oid)
|
||||||
|
self._sdio_read_data_buffer = mcu.CommandQueryWrapper(
|
||||||
|
ser, SDIO_READ_DATA_BUFFER, SDIO_READ_DATA_BUFFER_RESPONSE,
|
||||||
|
self.oid)
|
||||||
|
self._sdio_write_data_buffer = mcu.CommandWrapper(ser,
|
||||||
|
SDIO_WRITE_DATA_BUFFER)
|
||||||
|
self._sdio_set_speed = mcu.CommandWrapper(ser, SDIO_SET_SPEED)
|
||||||
|
|
||||||
|
def sdio_send_cmd(self, cmd, argument, wait):
|
||||||
|
return self._sdio_send_cmd.send([self.oid, cmd, argument, wait])
|
||||||
|
|
||||||
|
def sdio_read_data(self, cmd, argument):
|
||||||
|
return self._sdio_read_data.send([self.oid, cmd, argument])
|
||||||
|
|
||||||
|
def sdio_write_data(self, cmd, argument):
|
||||||
|
return self._sdio_write_data.send([self.oid, cmd, argument])
|
||||||
|
|
||||||
|
def sdio_read_data_buffer(self, offset, length=32):
|
||||||
|
return self._sdio_read_data_buffer.send([self.oid, offset, length])
|
||||||
|
|
||||||
|
def sdio_write_data_buffer(self, offset, data):
|
||||||
|
return self._sdio_write_data_buffer.send([self.oid, offset, data])
|
||||||
|
|
||||||
|
def sdio_set_speed(self, speed):
|
||||||
|
return self._sdio_set_speed.send([self.oid, speed])
|
||||||
|
|
||||||
|
|
||||||
# FatFs Constants. Enums are implemented as lists. The item's index is its value
|
# FatFs Constants. Enums are implemented as lists. The item's index is its value
|
||||||
DRESULT = ['RES_OK', 'RES_ERROR', 'RES_WRPRT', 'RES_NOTRDY', 'RES_PARERR']
|
DRESULT = ['RES_OK', 'RES_ERROR', 'RES_WRPRT', 'RES_NOTRDY', 'RES_PARERR']
|
||||||
|
@ -154,8 +206,11 @@ SECTOR_SIZE = 512
|
||||||
|
|
||||||
# FAT16/32 File System Support
|
# FAT16/32 File System Support
|
||||||
class FatFS:
|
class FatFS:
|
||||||
def __init__(self, ser):
|
def __init__(self, ser, spi=True):
|
||||||
self.sdcard = SDCardSPI(ser)
|
if spi:
|
||||||
|
self.sdcard = SDCardSPI(ser)
|
||||||
|
else:
|
||||||
|
self.sdcard = SDCardSDIO(ser)
|
||||||
self.disk_status = STA_NO_INIT | STA_NO_DISK
|
self.disk_status = STA_NO_INIT | STA_NO_DISK
|
||||||
self.ffi_callbacks = []
|
self.ffi_callbacks = []
|
||||||
self.ffi_main, self.ffi_lib = fatfs_lib.get_fatfs_ffi()
|
self.ffi_main, self.ffi_lib = fatfs_lib.get_fatfs_ffi()
|
||||||
|
@ -429,6 +484,10 @@ class SDCardFile:
|
||||||
|
|
||||||
SD_COMMANDS = {
|
SD_COMMANDS = {
|
||||||
'GO_IDLE_STATE': 0,
|
'GO_IDLE_STATE': 0,
|
||||||
|
'ALL_SEND_CID': 2,
|
||||||
|
'SET_REL_ADDR': 3,
|
||||||
|
'SET_BUS_WIDTH': 6,
|
||||||
|
'SEL_DESEL_CARD': 7,
|
||||||
'SEND_IF_COND': 8,
|
'SEND_IF_COND': 8,
|
||||||
'SEND_CSD': 9,
|
'SEND_CSD': 9,
|
||||||
'SEND_CID': 10,
|
'SEND_CID': 10,
|
||||||
|
@ -785,6 +844,322 @@ class SDCardSPI:
|
||||||
if err_msgs:
|
if err_msgs:
|
||||||
raise OSError("\n".join(err_msgs))
|
raise OSError("\n".join(err_msgs))
|
||||||
|
|
||||||
|
class SDCardSDIO:
|
||||||
|
def __init__(self, ser):
|
||||||
|
self.sdio = SDIODirect(ser)
|
||||||
|
self.rca = 0
|
||||||
|
self.reactor = ser.get_reactor()
|
||||||
|
self.enable_crc = True
|
||||||
|
self.mutex = self.reactor.mutex()
|
||||||
|
self.initialized = False
|
||||||
|
self.sd_version = 0
|
||||||
|
self.high_capacity = False
|
||||||
|
self.write_protected = False
|
||||||
|
self.total_sectors = 0
|
||||||
|
self.card_info = collections.OrderedDict()
|
||||||
|
|
||||||
|
def init_sd(self):
|
||||||
|
def check_for_ocr_errors(reg):
|
||||||
|
# returns False if an error flag is set
|
||||||
|
return ((reg[0]&0xFD) | (reg[1]&0xFF) |
|
||||||
|
(reg[2]&0xE0) | (reg[3]&0x08)) == 0
|
||||||
|
with self.mutex:
|
||||||
|
if self.initialized:
|
||||||
|
return
|
||||||
|
# Send reset command (CMD0)
|
||||||
|
if not self._send_command('GO_IDLE_STATE', 0):
|
||||||
|
raise OSError(
|
||||||
|
"flash_sdcard: failed to reset SD Card\n"
|
||||||
|
"Note that older (Version 1.0) SD cards can not be\n"
|
||||||
|
"hot swapped. Execute FIRMWARE_RESTART with the card\n"
|
||||||
|
"inserted for successful initialization.")
|
||||||
|
# Check Voltage Range (CMD8). Only Cards meeting the v2.0 spec
|
||||||
|
# support this. V1.0 cards (and MMC) will return illegal command.
|
||||||
|
check_pattern = 0b1010
|
||||||
|
resp = self._send_command_with_response(
|
||||||
|
'SEND_IF_COND', (1 << 8) | check_pattern)
|
||||||
|
resp = resp.strip(b'\xFF')
|
||||||
|
if len(resp) != 4:
|
||||||
|
# CMD8 is illegal, this is a version 1.0 card
|
||||||
|
self.sd_version = 1
|
||||||
|
else:
|
||||||
|
self.sd_version = 2
|
||||||
|
if not (resp[-2] == 1 and resp[-1] == check_pattern):
|
||||||
|
raise OSError("flash_sdcard: SD Card not running in a "
|
||||||
|
"compatible voltage range")
|
||||||
|
if self.sd_version == 2:
|
||||||
|
|
||||||
|
# Init card and come out of idle (ACMD41)
|
||||||
|
# Version 2 Cards may init before checking the OCR
|
||||||
|
# Allow vor LVDS card with 1.8v, too.
|
||||||
|
resp = self._check_command(lambda x: x[0]>>7 == 1,
|
||||||
|
'SD_SEND_OP_COND', 0xC1100000, is_app_cmd=True,
|
||||||
|
ignoreCRC=True)
|
||||||
|
if resp is None:
|
||||||
|
raise OSError("flash_sdcard: SD Card did not come"
|
||||||
|
" out of IDLE after reset")
|
||||||
|
if len(resp) == 4:
|
||||||
|
if self.sd_version == 1:
|
||||||
|
# Check acceptable volatage range for V1 cards
|
||||||
|
if resp[1] != 0xFF:
|
||||||
|
raise OSError("flash_sdcard: card does not support"
|
||||||
|
" 3.3v range")
|
||||||
|
elif self.sd_version == 2:
|
||||||
|
# Determine if this is a high capacity sdcard
|
||||||
|
if resp[0] & 0x40:
|
||||||
|
self.high_capacity = True
|
||||||
|
else:
|
||||||
|
raise OSError("flash_sdcard: Invalid OCR Response")
|
||||||
|
if self.sd_version == 1:
|
||||||
|
# Init card and come out of idle (ACMD41)
|
||||||
|
# Version 1 Cards do this after checking the OCR
|
||||||
|
if not self._check_command(0, 'SD_SEND_OP_COND', 0,
|
||||||
|
is_app_cmd=True):
|
||||||
|
raise OSError("flash_sdcard: SD Card did not come"
|
||||||
|
" out of IDLE after reset")
|
||||||
|
|
||||||
|
# Read out CID information register
|
||||||
|
self._process_cid_reg()
|
||||||
|
|
||||||
|
# Get card's relative address (RCA)
|
||||||
|
resp = self._send_command_with_response('SET_REL_ADDR', 0)
|
||||||
|
# Check if bits 15:13 have some error set
|
||||||
|
if (resp[-2] & 0xe0) != 0:
|
||||||
|
raise OSError("flash_sdcard: set card's "
|
||||||
|
"relative address failed")
|
||||||
|
self.rca = resp[0]<<8 | resp[1]
|
||||||
|
|
||||||
|
# Read out CSD information register
|
||||||
|
self._process_csd_reg()
|
||||||
|
|
||||||
|
# Select the current card
|
||||||
|
if not self._check_command(check_for_ocr_errors, 'SEL_DESEL_CARD',
|
||||||
|
self.rca << 16, tries=1):
|
||||||
|
raise OSError("flash_sdcard: failed to select the card")
|
||||||
|
|
||||||
|
# Set SDIO clk speed to approx. 1 MHz
|
||||||
|
self.sdio.sdio_set_speed(1000000)
|
||||||
|
|
||||||
|
if self._check_command(check_for_ocr_errors, 'SET_BLOCKLEN',
|
||||||
|
SECTOR_SIZE, tries=5):
|
||||||
|
self.initialized = True
|
||||||
|
else:
|
||||||
|
raise OSError("flash_sdcard: failed to set block size")
|
||||||
|
|
||||||
|
|
||||||
|
def deinit(self):
|
||||||
|
with self.mutex:
|
||||||
|
if self.initialized:
|
||||||
|
# Reset the SD Card
|
||||||
|
try:
|
||||||
|
if not self._send_command('GO_IDLE_STATE', 0):
|
||||||
|
logging.info("flash_sdcard: failed to reset SD Card")
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Error resetting SD Card")
|
||||||
|
self.initialized = False
|
||||||
|
self.sd_version = 0
|
||||||
|
self.high_capacity = False
|
||||||
|
self.total_sectors = 0
|
||||||
|
self.card_info.clear()
|
||||||
|
|
||||||
|
def _check_command(self, check_func, cmd, args, is_app_cmd=False, tries=15,
|
||||||
|
ignoreCRC=False):
|
||||||
|
func = self._send_app_cmd_with_response if is_app_cmd else \
|
||||||
|
self._send_command_with_response
|
||||||
|
while True:
|
||||||
|
resp, rt = func(cmd, args, get_rt=True, ignoreCRC=ignoreCRC)
|
||||||
|
#logging.info("flash_sdcard: Check cmd %s, response: %s"
|
||||||
|
# % (cmd, repr(resp)))
|
||||||
|
if resp and check_func(resp):
|
||||||
|
return resp
|
||||||
|
tries -= 1
|
||||||
|
if tries < 1:
|
||||||
|
return None
|
||||||
|
self.reactor.pause(rt + .1)
|
||||||
|
|
||||||
|
def _send_command(self, cmd, args, wait=0):
|
||||||
|
cmd_code = SD_COMMANDS[cmd]
|
||||||
|
argument = 0
|
||||||
|
if isinstance(args, int) or isinstance(args, long):
|
||||||
|
argument = args & 0xFFFFFFFF
|
||||||
|
elif isinstance(args, list) and len(args) == 4:
|
||||||
|
argument = ((args[0] << 24) & 0xFF000000) | \
|
||||||
|
((args[1] << 16) & 0x00FF0000) | \
|
||||||
|
((args[2] << 8) & 0x0000FF00) | \
|
||||||
|
((args[3] << 0) & 0x000000FF)
|
||||||
|
else:
|
||||||
|
raise OSError("flash_sdcard: Invalid SD Card Command argument")
|
||||||
|
params = self.sdio.sdio_send_cmd(cmd_code, argument, wait)
|
||||||
|
#logging.debug(f'_send_command({cmd=}, {args=}, {wait=}) -> '
|
||||||
|
# 'CMD: {cmd_code} ARG: {argument} -> {params=}')
|
||||||
|
if (wait == 0):
|
||||||
|
# Just return the error code if no response was requested
|
||||||
|
return params['error'] == 0
|
||||||
|
return params
|
||||||
|
|
||||||
|
def _send_command_with_response(self, cmd, args, check_error=True,
|
||||||
|
ignoreCRC=False, get_rt=False):
|
||||||
|
# Wait for a short response
|
||||||
|
params = self._send_command(cmd, args, wait=1)
|
||||||
|
response = params['response']
|
||||||
|
if check_error:
|
||||||
|
if params['error'] != 0:
|
||||||
|
if ignoreCRC and params['error'] != 4:
|
||||||
|
response = []
|
||||||
|
if get_rt:
|
||||||
|
return bytearray(response), params['#receive_time']
|
||||||
|
else:
|
||||||
|
return bytearray(response)
|
||||||
|
|
||||||
|
def _send_app_cmd_with_response(self, cmd, args,
|
||||||
|
ignoreCRC=False, get_rt=False):
|
||||||
|
# CMD55 tells the SD Card that the next command is an
|
||||||
|
# Application Specific Command.
|
||||||
|
self._send_command_with_response('APP_CMD', self.rca << 16)
|
||||||
|
return self._send_command_with_response(
|
||||||
|
cmd, args, ignoreCRC=ignoreCRC, get_rt=get_rt)
|
||||||
|
|
||||||
|
def _process_cid_reg(self):
|
||||||
|
params = self._send_command('ALL_SEND_CID', 0, wait=2)
|
||||||
|
reg = bytearray(params['response'])
|
||||||
|
if reg is None:
|
||||||
|
raise OSError("flash_sdcard: Error reading CID register")
|
||||||
|
|
||||||
|
cid = collections.OrderedDict()
|
||||||
|
cid['manufacturer_id'] = reg[0]
|
||||||
|
cid['oem_id'] = reg[1:3].decode(encoding='ascii', errors='ignore')
|
||||||
|
cid['product_name'] = reg[3:8].decode(
|
||||||
|
encoding='ascii', errors='ignore')
|
||||||
|
cid['product_revision'] = str(reg[8] >> 4 & 0xFF) + "." \
|
||||||
|
+ str(reg[8] & 0xFF)
|
||||||
|
cid['serial_number'] = "".join(["%02X" % (c,) for c in reg[9:13]])
|
||||||
|
mfg_year = (((reg[13] & 0xF) << 4) | ((reg[14] >> 4) & 0xF)) + 2000
|
||||||
|
mfg_month = reg[14] & 0xF
|
||||||
|
cid['manufacturing_date'] = "%d/%d" % (mfg_month, mfg_year)
|
||||||
|
crc = calc_crc7(reg[:15], with_padding=False)
|
||||||
|
if crc != reg[15]:
|
||||||
|
raise OSError("flash_sdcard: CID crc mismatch: 0x%02X, recd: 0x%02X"
|
||||||
|
% (crc, reg[15]))
|
||||||
|
self.card_info.update(cid)
|
||||||
|
|
||||||
|
def _process_csd_reg(self):
|
||||||
|
params = self._send_command('SEND_CSD', self.rca << 16, wait=2)
|
||||||
|
reg = bytearray(params['response'])
|
||||||
|
if reg is None:
|
||||||
|
raise OSError("flash_sdcard: Error reading CSD register")
|
||||||
|
str_capacity = "Invalid"
|
||||||
|
max_capacity = 0
|
||||||
|
csd_type = (reg[0] >> 6) & 0x3
|
||||||
|
if csd_type == 0:
|
||||||
|
# Standard Capacity (CSD Version 1.0)
|
||||||
|
max_block_len = 2**(reg[5] & 0xF)
|
||||||
|
c_size = ((reg[6] & 0x3) << 10) | (reg[7] << 2) | \
|
||||||
|
((reg[8] >> 6) & 0x3)
|
||||||
|
c_mult = 2**((((reg[9] & 0x3) << 1) | (reg[10] >> 7)) + 2)
|
||||||
|
max_capacity = (c_size + 1) * c_mult * max_block_len
|
||||||
|
str_capacity = "%.1f MiB" % (max_capacity / (1024.0**2))
|
||||||
|
elif csd_type == 1:
|
||||||
|
# High Capacity (CSD Version 2.0)
|
||||||
|
c_size = ((reg[7] & 0x3F) << 16) | (reg[8] << 8) | reg[9]
|
||||||
|
max_capacity = (c_size + 1) * 512 * 1024
|
||||||
|
str_capacity = "%.1f GiB" % (max_capacity / (1024.0**3))
|
||||||
|
else:
|
||||||
|
logging.info("sdcard: Unsupported csd type: %d" % (csd_type))
|
||||||
|
self.write_protected = (reg[14] & 0x30) != 0
|
||||||
|
crc = calc_crc7(reg[:15], with_padding=False)
|
||||||
|
if crc != reg[15]:
|
||||||
|
raise OSError("flash_sdcard: CSD crc mismatch: 0x%02X, recd: 0x%02X"
|
||||||
|
% (crc, reg[15]))
|
||||||
|
self.card_info['capacity'] = str_capacity
|
||||||
|
self.total_sectors = max_capacity // SECTOR_SIZE
|
||||||
|
|
||||||
|
def print_card_info(self, print_func=logging.info):
|
||||||
|
print_func("\nSD Card Information:")
|
||||||
|
print_func("Version: %.1f" % (self.sd_version))
|
||||||
|
print_func("SDHC/SDXC: %s" % (self.high_capacity))
|
||||||
|
print_func("Write Protected: %s" % (self.write_protected))
|
||||||
|
print_func("Sectors: %d" % (self.total_sectors,))
|
||||||
|
for name, val in self.card_info.items():
|
||||||
|
print_func("%s: %s" % (name, val))
|
||||||
|
|
||||||
|
def read_sector(self, sector):
|
||||||
|
buf = None
|
||||||
|
err_msg = "flash_sdcard: read error, sector %d" % (sector,)
|
||||||
|
with self.mutex:
|
||||||
|
if not 0 <= sector < self.total_sectors:
|
||||||
|
err_msg += " out of range"
|
||||||
|
elif not self.initialized:
|
||||||
|
err_msg += ", SD Card not initialized"
|
||||||
|
else:
|
||||||
|
offset = sector
|
||||||
|
if not self.high_capacity:
|
||||||
|
offset = sector * SECTOR_SIZE
|
||||||
|
|
||||||
|
params = self.sdio.sdio_read_data(
|
||||||
|
SD_COMMANDS['READ_SINGLE_BLOCK'], offset)
|
||||||
|
if params['error'] != 0:
|
||||||
|
raise OSError(
|
||||||
|
'Read data failed. Error code=%d' %(params['error'],) )
|
||||||
|
if params['read'] != SECTOR_SIZE:
|
||||||
|
raise OSError(
|
||||||
|
'Read data failed. Expected %d bytes but got %d.' %
|
||||||
|
(SECTOR_SIZE, params['read']) )
|
||||||
|
|
||||||
|
buf = bytearray()
|
||||||
|
offset = 0
|
||||||
|
while SECTOR_SIZE-len(buf)>0:
|
||||||
|
rest = min(SECTOR_SIZE-len(buf), 32)
|
||||||
|
params = self.sdio.sdio_read_data_buffer(
|
||||||
|
offset, length=rest)
|
||||||
|
temp = bytearray(params['data'])
|
||||||
|
if len(temp) == 0:
|
||||||
|
raise OSError("Read zero bytes from buffer")
|
||||||
|
buf += temp
|
||||||
|
offset += len(temp)
|
||||||
|
if buf is None:
|
||||||
|
raise OSError(err_msg)
|
||||||
|
return buf
|
||||||
|
|
||||||
|
def write_sector(self, sector, data):
|
||||||
|
with self.mutex:
|
||||||
|
if not 0 <= sector < self.total_sectors:
|
||||||
|
raise OSError(
|
||||||
|
"flash_sdcard: write error, sector number %d invalid"
|
||||||
|
% (sector))
|
||||||
|
if not self.initialized:
|
||||||
|
raise OSError("flash_sdcard: write error, SD Card not"
|
||||||
|
" initialized")
|
||||||
|
outbuf = bytearray(data)
|
||||||
|
if len(outbuf) > SECTOR_SIZE:
|
||||||
|
raise OSError("sd_card: Cannot write sector larger"
|
||||||
|
" than %d bytes"
|
||||||
|
% (SECTOR_SIZE))
|
||||||
|
elif len(outbuf) < SECTOR_SIZE:
|
||||||
|
outbuf += bytearray([0] * (SECTOR_SIZE - len(outbuf)))
|
||||||
|
offset = sector
|
||||||
|
if not self.high_capacity:
|
||||||
|
offset = sector * SECTOR_SIZE
|
||||||
|
|
||||||
|
CHUNKSIZE = 32
|
||||||
|
for i in range(0, SECTOR_SIZE, CHUNKSIZE):
|
||||||
|
self.sdio.sdio_write_data_buffer(i, outbuf[i:i+CHUNKSIZE])
|
||||||
|
params = self.sdio.sdio_write_data(
|
||||||
|
SD_COMMANDS['WRITE_BLOCK'], offset)
|
||||||
|
if (params['error'] != 0) or (params['write'] != SECTOR_SIZE):
|
||||||
|
raise OSError(
|
||||||
|
"flash_sdcard: Error writing to sector %d"% (sector,))
|
||||||
|
|
||||||
|
status = self._send_command_with_response(
|
||||||
|
'SEND_STATUS', self.rca << 16)
|
||||||
|
if len(status) != 4:
|
||||||
|
raise OSError("flash_sdcard: Failed to get status response"
|
||||||
|
" after write: %s" % (repr(status),))
|
||||||
|
if ((status[3]>>1) & 0x0F) != 0:
|
||||||
|
# Bit 12:9 are not "0" (card is in idle)
|
||||||
|
raise OSError("flash_sdcard: Write error."
|
||||||
|
" Card is not in transfer state: 0x%02X"
|
||||||
|
% (((status[3]>>1) & 0x0F)))
|
||||||
|
|
||||||
SDIO_WARNING = """
|
SDIO_WARNING = """
|
||||||
This board requires a manual reboot to complete the flash process.
|
This board requires a manual reboot to complete the flash process.
|
||||||
If the board's bootloader uses SDIO mode for its SDCard, then a full
|
If the board's bootloader uses SDIO mode for its SDCard, then a full
|
||||||
|
@ -896,7 +1271,7 @@ class MCUConnection:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def configure_mcu(self, printfunc=logging.info):
|
def _configure_mcu_spibus(self, printfunc=logging.info):
|
||||||
# TODO: add commands for buttons? Or perhaps an endstop? We
|
# TODO: add commands for buttons? Or perhaps an endstop? We
|
||||||
# just need to be able to query the status of the detect pin
|
# just need to be able to query the status of the detect pin
|
||||||
cs_pin = self.board_config['cs_pin'].upper()
|
cs_pin = self.board_config['cs_pin'].upper()
|
||||||
|
@ -952,6 +1327,41 @@ class MCUConnection:
|
||||||
raise SPIFlashError(
|
raise SPIFlashError(
|
||||||
"Failed to Initialize SD Card. Is it inserted?")
|
"Failed to Initialize SD Card. Is it inserted?")
|
||||||
|
|
||||||
|
def _configure_mcu_sdiobus(self, printfunc=logging.info):
|
||||||
|
bus = self.board_config['sdio_bus']
|
||||||
|
bus_enums = self.enumerations.get(
|
||||||
|
'sdio_bus', self.enumerations.get('bus'))
|
||||||
|
pin_enums = self.enumerations.get('pin')
|
||||||
|
if bus not in bus_enums:
|
||||||
|
raise SPIFlashError("Invalid SDIO Bus: %s" % (bus,))
|
||||||
|
bus_cmd = SDIO_BUS_CMD % (SDIO_OID, bus)
|
||||||
|
sdio_cfg_cmd = SDIO_CFG_CMD % (SDIO_OID, SECTOR_SIZE)
|
||||||
|
cfg_cmds = [ALLOC_OIDS_CMD % (1,), sdio_cfg_cmd, bus_cmd]
|
||||||
|
for cmd in cfg_cmds:
|
||||||
|
self._serial.send(cmd)
|
||||||
|
config_crc = zlib.crc32('\n'.join(cfg_cmds).encode()) & 0xffffffff
|
||||||
|
self._serial.send(FINALIZE_CFG_CMD % (config_crc,))
|
||||||
|
config = self.get_mcu_config()
|
||||||
|
if not config["is_config"] or config["is_shutdown"]:
|
||||||
|
raise MCUConfigError("Failed to configure MCU")
|
||||||
|
printfunc("Initializing SD Card and Mounting file system...")
|
||||||
|
self.fatfs = FatFS(self._serial,spi=False)
|
||||||
|
self.reactor.pause(self.reactor.monotonic() + .5)
|
||||||
|
try:
|
||||||
|
self.fatfs.mount(printfunc)
|
||||||
|
except OSError:
|
||||||
|
logging.exception("SD Card Mount Failure")
|
||||||
|
raise SPIFlashError(
|
||||||
|
"Failed to Initialize SD Card. Is it inserted?")
|
||||||
|
|
||||||
|
def configure_mcu(self, printfunc=logging.info):
|
||||||
|
if 'spi_bus' in self.board_config:
|
||||||
|
self._configure_mcu_spibus(printfunc=printfunc)
|
||||||
|
elif 'sdio_bus' in self.board_config:
|
||||||
|
self._configure_mcu_sdiobus(printfunc=printfunc)
|
||||||
|
else:
|
||||||
|
raise SPIFlashError("Unknown bus defined in board_defs.py.")
|
||||||
|
|
||||||
def sdcard_upload(self):
|
def sdcard_upload(self):
|
||||||
output("Uploading Klipper Firmware to SD Card...")
|
output("Uploading Klipper Firmware to SD Card...")
|
||||||
input_sha = hashlib.sha1()
|
input_sha = hashlib.sha1()
|
||||||
|
|
|
@ -115,6 +115,8 @@ config HAVE_GPIO_ADC
|
||||||
bool
|
bool
|
||||||
config HAVE_GPIO_SPI
|
config HAVE_GPIO_SPI
|
||||||
bool
|
bool
|
||||||
|
config HAVE_GPIO_SDIO
|
||||||
|
bool
|
||||||
config HAVE_GPIO_I2C
|
config HAVE_GPIO_I2C
|
||||||
bool
|
bool
|
||||||
config HAVE_GPIO_HARD_PWM
|
config HAVE_GPIO_HARD_PWM
|
||||||
|
|
|
@ -5,6 +5,7 @@ src-$(CONFIG_HAVE_GPIO) += initial_pins.c gpiocmds.c stepper.c endstop.c \
|
||||||
trsync.c
|
trsync.c
|
||||||
src-$(CONFIG_HAVE_GPIO_ADC) += adccmds.c
|
src-$(CONFIG_HAVE_GPIO_ADC) += adccmds.c
|
||||||
src-$(CONFIG_HAVE_GPIO_SPI) += spicmds.c thermocouple.c
|
src-$(CONFIG_HAVE_GPIO_SPI) += spicmds.c thermocouple.c
|
||||||
|
src-$(CONFIG_HAVE_GPIO_SDIO) += sdiocmds.c
|
||||||
src-$(CONFIG_HAVE_GPIO_I2C) += i2ccmds.c
|
src-$(CONFIG_HAVE_GPIO_I2C) += i2ccmds.c
|
||||||
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += pwmcmds.c
|
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += pwmcmds.c
|
||||||
bb-src-$(CONFIG_HAVE_GPIO_SPI) := spi_software.c sensor_adxl345.c sensor_angle.c
|
bb-src-$(CONFIG_HAVE_GPIO_SPI) := spi_software.c sensor_adxl345.c sensor_angle.c
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
// Commands for sending messages on an SDIO bus
|
||||||
|
//
|
||||||
|
// Copyright (C) 2022 H. Gregor Molter <gregor.molter@secretlab.de>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
#include <string.h> // memcpy
|
||||||
|
#include "board/gpio.h" // gpio_in_setup
|
||||||
|
#include "board/sdio.h" // sdio_setup
|
||||||
|
#include "basecmd.h" // oid_alloc
|
||||||
|
#include "command.h" // DECL_COMMAND
|
||||||
|
#include "sched.h" // DECL_SHUTDOWN
|
||||||
|
|
||||||
|
struct sdiodev_s {
|
||||||
|
struct sdio_config sdio_config;
|
||||||
|
uint32_t blocksize;
|
||||||
|
uint32_t speed;
|
||||||
|
uint8_t data_buffer[4096];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TIMEOUT_MSEC 500
|
||||||
|
|
||||||
|
void
|
||||||
|
command_config_sdio(uint32_t *args)
|
||||||
|
{
|
||||||
|
struct sdiodev_s *sdio = oid_alloc(args[0], command_config_sdio
|
||||||
|
, sizeof(*sdio));
|
||||||
|
sdio->blocksize = args[1];
|
||||||
|
sdio->speed = 400000; // Initial speed set to ~400khz
|
||||||
|
}
|
||||||
|
DECL_COMMAND(command_config_sdio, "config_sdio oid=%c blocksize=%u");
|
||||||
|
|
||||||
|
struct sdiodev_s *
|
||||||
|
sdiodev_oid_lookup(uint8_t oid)
|
||||||
|
{
|
||||||
|
return oid_lookup(oid, command_config_sdio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
command_sdio_set_bus(uint32_t *args)
|
||||||
|
{
|
||||||
|
struct sdiodev_s *sdio = sdiodev_oid_lookup(args[0]);
|
||||||
|
sdio->sdio_config = sdio_setup(args[1]);
|
||||||
|
}
|
||||||
|
DECL_COMMAND(command_sdio_set_bus, "sdio_set_bus oid=%c sdio_bus=%u");
|
||||||
|
|
||||||
|
void
|
||||||
|
command_sdio_set_speed(uint32_t *args)
|
||||||
|
{
|
||||||
|
struct sdiodev_s *sdio = sdiodev_oid_lookup(args[0]);
|
||||||
|
sdio->speed = args[1];
|
||||||
|
sdio_set_speed(sdio->sdio_config, sdio->speed);
|
||||||
|
}
|
||||||
|
DECL_COMMAND(command_sdio_set_speed, "sdio_set_speed oid=%c speed=%u");
|
||||||
|
|
||||||
|
void
|
||||||
|
command_sdio_send_command(uint32_t *args)
|
||||||
|
{
|
||||||
|
uint8_t oid = args[0];
|
||||||
|
uint8_t cmd = args[1];
|
||||||
|
uint32_t argument = args[2];
|
||||||
|
uint8_t wait = args[3];
|
||||||
|
struct sdiodev_s *sdio = sdiodev_oid_lookup(oid);
|
||||||
|
uint8_t response[16];
|
||||||
|
uint8_t response_len = 0;
|
||||||
|
uint8_t err = sdio_send_command(sdio->sdio_config, cmd, argument, wait
|
||||||
|
, response, &response_len);
|
||||||
|
sendf("sdio_send_command_response oid=%c error=%c response=%*s"
|
||||||
|
, oid, err, response_len, response);
|
||||||
|
}
|
||||||
|
DECL_COMMAND(command_sdio_send_command
|
||||||
|
, "sdio_send_command oid=%c cmd=%c argument=%u wait=%c");
|
||||||
|
|
||||||
|
void
|
||||||
|
command_sdio_read_data(uint32_t *args)
|
||||||
|
{
|
||||||
|
uint8_t oid = args[0];
|
||||||
|
uint8_t cmd = args[1];
|
||||||
|
uint32_t argument = args[2];
|
||||||
|
uint32_t data_len = 0;
|
||||||
|
struct sdiodev_s *sdio = sdiodev_oid_lookup(oid);
|
||||||
|
uint32_t timeout = TIMEOUT_MSEC*sdio->speed/1000;
|
||||||
|
uint8_t err = sdio_prepare_data_transfer(sdio->sdio_config, 1, 1
|
||||||
|
, sdio->blocksize, timeout);
|
||||||
|
if (err == 0) {
|
||||||
|
err = sdio_send_command(sdio->sdio_config, cmd, argument
|
||||||
|
, 1, NULL, NULL);
|
||||||
|
if (err == 0) {
|
||||||
|
data_len = sdio->blocksize;
|
||||||
|
if (data_len <= sizeof(sdio->data_buffer)) {
|
||||||
|
err = sdio_read_data(sdio->sdio_config, sdio->data_buffer
|
||||||
|
, 1, sdio->blocksize);
|
||||||
|
} else {
|
||||||
|
data_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendf("sdio_read_data_response oid=%c error=%c read=%u"
|
||||||
|
, oid, err, data_len);
|
||||||
|
}
|
||||||
|
DECL_COMMAND(command_sdio_read_data
|
||||||
|
, "sdio_read_data oid=%c cmd=%c argument=%u");
|
||||||
|
|
||||||
|
void
|
||||||
|
command_sdio_write_data(uint32_t *args)
|
||||||
|
{
|
||||||
|
uint8_t oid = args[0];
|
||||||
|
uint8_t cmd = args[1];
|
||||||
|
uint32_t argument = args[2];
|
||||||
|
uint32_t data_len = 0;
|
||||||
|
struct sdiodev_s *sdio = sdiodev_oid_lookup(oid);
|
||||||
|
uint32_t timeout = TIMEOUT_MSEC*sdio->speed/1000;
|
||||||
|
uint8_t err = sdio_prepare_data_transfer(sdio->sdio_config, 0, 1
|
||||||
|
, sdio->blocksize, timeout);
|
||||||
|
if (err == 0) {
|
||||||
|
err = sdio_send_command(sdio->sdio_config, cmd, argument
|
||||||
|
, 1, NULL, NULL);
|
||||||
|
if (err == 0) {
|
||||||
|
data_len = sdio->blocksize;
|
||||||
|
if (data_len <= sizeof(sdio->data_buffer)) {
|
||||||
|
err = sdio_write_data(sdio->sdio_config, sdio->data_buffer
|
||||||
|
, 1, sdio->blocksize);
|
||||||
|
} else {
|
||||||
|
data_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendf("sdio_write_data_response oid=%c error=%c write=%u"
|
||||||
|
, oid, err, data_len);
|
||||||
|
}
|
||||||
|
DECL_COMMAND(command_sdio_write_data
|
||||||
|
, "sdio_write_data oid=%c cmd=%c argument=%u");
|
||||||
|
|
||||||
|
void
|
||||||
|
command_sdio_read_data_buffer(uint32_t *args)
|
||||||
|
{
|
||||||
|
uint8_t oid = args[0];
|
||||||
|
uint32_t offset = args[1];
|
||||||
|
uint8_t len = args[2];
|
||||||
|
struct sdiodev_s *sdio = sdiodev_oid_lookup(oid);
|
||||||
|
uint8_t *buf = &(sdio->data_buffer[offset]);
|
||||||
|
|
||||||
|
if (offset + len > sizeof(sdio->data_buffer)) {
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
sendf("sdio_read_data_buffer_response oid=%c data=%*s", oid, len, buf);
|
||||||
|
}
|
||||||
|
DECL_COMMAND(command_sdio_read_data_buffer
|
||||||
|
, "sdio_read_data_buffer oid=%c offset=%u len=%c");
|
||||||
|
|
||||||
|
void
|
||||||
|
command_sdio_write_data_buffer(uint32_t *args)
|
||||||
|
{
|
||||||
|
uint8_t oid = args[0];
|
||||||
|
uint32_t offset = args[1];
|
||||||
|
uint8_t write_data_len = args[2];
|
||||||
|
uint8_t *write_data = command_decode_ptr(args[3]);
|
||||||
|
struct sdiodev_s *sdio = sdiodev_oid_lookup(oid);
|
||||||
|
uint8_t *buf = &(sdio->data_buffer[offset]);
|
||||||
|
|
||||||
|
if (offset + write_data_len <= sizeof(sdio->data_buffer)) {
|
||||||
|
memcpy(buf, write_data, write_data_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DECL_COMMAND(command_sdio_write_data_buffer
|
||||||
|
, "sdio_write_data_buffer oid=%c offset=%u data=%*s");
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef __SDIOCMDS_H
|
||||||
|
#define __SDIOCMDS_H
|
||||||
|
|
||||||
|
#include <stdint.h> // uint8_t
|
||||||
|
|
||||||
|
struct sdiodev_s *sdiodev_oid_lookup(uint8_t oid);
|
||||||
|
|
||||||
|
#endif // sdiocmds.h
|
|
@ -9,6 +9,7 @@ config STM32_SELECT
|
||||||
select HAVE_GPIO_ADC
|
select HAVE_GPIO_ADC
|
||||||
select HAVE_GPIO_I2C if !(MACH_STM32F031 || MACH_STM32H7)
|
select HAVE_GPIO_I2C if !(MACH_STM32F031 || MACH_STM32H7)
|
||||||
select HAVE_GPIO_SPI if !MACH_STM32F031
|
select HAVE_GPIO_SPI if !MACH_STM32F031
|
||||||
|
select HAVE_GPIO_SDIO if MACH_STM32F4
|
||||||
select HAVE_GPIO_HARD_PWM if MACH_STM32F1 || MACH_STM32F4 || MACH_STM32G0 || MACH_STM32H7
|
select HAVE_GPIO_HARD_PWM if MACH_STM32F1 || MACH_STM32F4 || MACH_STM32G0 || MACH_STM32H7
|
||||||
select HAVE_GPIO_BITBANGING if !MACH_STM32F031
|
select HAVE_GPIO_BITBANGING if !MACH_STM32F031
|
||||||
select HAVE_STRICT_TIMING
|
select HAVE_STRICT_TIMING
|
||||||
|
|
|
@ -64,6 +64,8 @@ src-$(CONFIG_MACH_STM32L4) += stm32/stm32h7_adc.c stm32/stm32f0_i2c.c
|
||||||
spi-src-y := stm32/spi.c
|
spi-src-y := stm32/spi.c
|
||||||
spi-src-$(CONFIG_MACH_STM32H7) := stm32/stm32h7_spi.c
|
spi-src-$(CONFIG_MACH_STM32H7) := stm32/stm32h7_spi.c
|
||||||
src-$(CONFIG_HAVE_GPIO_SPI) += $(spi-src-y)
|
src-$(CONFIG_HAVE_GPIO_SPI) += $(spi-src-y)
|
||||||
|
sdio-src-y := stm32/sdio.c
|
||||||
|
src-$(CONFIG_HAVE_GPIO_SDIO) += $(sdio-src-y)
|
||||||
usb-src-$(CONFIG_HAVE_STM32_USBFS) := stm32/usbfs.c
|
usb-src-$(CONFIG_HAVE_STM32_USBFS) := stm32/usbfs.c
|
||||||
usb-src-$(CONFIG_HAVE_STM32_USBOTG) := stm32/usbotg.c
|
usb-src-$(CONFIG_HAVE_STM32_USBOTG) := stm32/usbotg.c
|
||||||
src-$(CONFIG_USBSERIAL) += $(usb-src-y) stm32/chipid.c generic/usb_cdc.c
|
src-$(CONFIG_USBSERIAL) += $(usb-src-y) stm32/chipid.c generic/usb_cdc.c
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include "internal.h" // gpio_peripheral
|
#include "internal.h" // gpio_peripheral
|
||||||
|
|
||||||
// Set the mode and extended function of a pin
|
// Set the mode, extended function and speed of a pin
|
||||||
void
|
void
|
||||||
gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
|
gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
|
||||||
{
|
{
|
||||||
|
@ -16,7 +16,8 @@ gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
|
||||||
gpio_clock_enable(regs);
|
gpio_clock_enable(regs);
|
||||||
|
|
||||||
// Configure GPIO
|
// Configure GPIO
|
||||||
uint32_t mode_bits = mode & 0xf, func = (mode >> 4) & 0xf, od = mode >> 8;
|
uint32_t mode_bits = mode & 0xf, func = (mode >> 4) & 0xf;
|
||||||
|
uint32_t od = (mode >> 8) & 0x1, hs = (mode >> 9) & 0x1;
|
||||||
uint32_t pup = pullup ? (pullup > 0 ? 1 : 2) : 0;
|
uint32_t pup = pullup ? (pullup > 0 ? 1 : 2) : 0;
|
||||||
uint32_t pos = gpio % 16, af_reg = pos / 8;
|
uint32_t pos = gpio % 16, af_reg = pos / 8;
|
||||||
uint32_t af_shift = (pos % 8) * 4, af_msk = 0x0f << af_shift;
|
uint32_t af_shift = (pos % 8) * 4, af_msk = 0x0f << af_shift;
|
||||||
|
@ -33,6 +34,6 @@ gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
|
||||||
// stm32f4 is ~50Mhz at 40pF
|
// stm32f4 is ~50Mhz at 40pF
|
||||||
// stm32g0 is ~30Mhz at 50pF
|
// stm32g0 is ~30Mhz at 50pF
|
||||||
// stm32h7 is ~85Mhz at 50pF
|
// stm32h7 is ~85Mhz at 50pF
|
||||||
uint32_t ospeed = CONFIG_MACH_STM32F0 ? 0x01 : 0x02;
|
uint32_t ospeed = hs ? 0x03 : (CONFIG_MACH_STM32F0 ? 0x01 : 0x02);
|
||||||
regs->OSPEEDR = (regs->OSPEEDR & ~m_msk) | (ospeed << m_shift);
|
regs->OSPEEDR = (regs->OSPEEDR & ~m_msk) | (ospeed << m_shift);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ extern GPIO_TypeDef * const digital_regs[];
|
||||||
#define GPIO_INPUT 0
|
#define GPIO_INPUT 0
|
||||||
#define GPIO_OUTPUT 1
|
#define GPIO_OUTPUT 1
|
||||||
#define GPIO_OPEN_DRAIN 0x100
|
#define GPIO_OPEN_DRAIN 0x100
|
||||||
|
#define GPIO_HIGH_SPEED 0x200
|
||||||
#define GPIO_FUNCTION(fn) (2 | ((fn) << 4))
|
#define GPIO_FUNCTION(fn) (2 | ((fn) << 4))
|
||||||
#define GPIO_ANALOG 3
|
#define GPIO_ANALOG 3
|
||||||
void gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup);
|
void gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup);
|
||||||
|
|
|
@ -0,0 +1,414 @@
|
||||||
|
// SDIO functions on STM32
|
||||||
|
//
|
||||||
|
// Copyright (C) 2022 H. Gregor Molter <gregor.molter@secretlab.de>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
#include "board/io.h" // readb, writeb
|
||||||
|
#include "command.h" // shutdown
|
||||||
|
#include "sdio.h" // sdio_setup
|
||||||
|
#include "internal.h" // gpio_peripheral
|
||||||
|
#include "sched.h" // sched_shutdown
|
||||||
|
#include "generic/armcm_timer.h" // udelay
|
||||||
|
|
||||||
|
struct sdio_info {
|
||||||
|
SDIO_TypeDef *sdio;
|
||||||
|
uint8_t clk_pin, cmd_pin, dat0_pin, dat1_pin, dat2_pin, dat3_pin, function;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SDIO_OK = 0,
|
||||||
|
SDIO_ERROR = 1,
|
||||||
|
SDIO_TIMEOUT = 2,
|
||||||
|
SDIO_CMD_RESPONSE_TIMEOUT = 3,
|
||||||
|
SDIO_CRC_FAIL = 4,
|
||||||
|
SDIO_WRONG_CMD_RESPONSE = 5,
|
||||||
|
SDIO_WRONG_BLOCKSIZE = 6,
|
||||||
|
SDIO_DATA_TIMEOUT = 7,
|
||||||
|
SDIO_READ_OVERRUN = 8,
|
||||||
|
SDIO_WRITE_UNDERRUN = 9,
|
||||||
|
SDIO_NO_DATA_MEMORY = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SDIO_CARDVER_UNKNOWN = 0,
|
||||||
|
SDIO_CARDVER_1X = 1,
|
||||||
|
SDIO_CARDVER_2X = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SDIO_CARDTYPE_UNKNOWN = 0,
|
||||||
|
SDIO_CARDTYPE_SDHC_SDXC = 1,
|
||||||
|
SDIO_CARDTYPE_SDSC = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SDIO_WAIT_NO_RESPONSE = 0,
|
||||||
|
SDIO_WAIT_SHORT_RESPONSE = 1,
|
||||||
|
SDIO_WAIT_LONG_RESPONSE = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
// PINS: CLK -> PC12 , CMD -> PD2,
|
||||||
|
// DAT0 -> PC8, DAT1 -> PC9, DAT2 -> PC10, DAT3 -> PC11
|
||||||
|
DECL_ENUMERATION("sdio_bus", "sdio", 0);
|
||||||
|
DECL_CONSTANT_STR("BUS_PINS_sdio", "PC12,PD2,PC8,PC9,PC10,PC11");
|
||||||
|
|
||||||
|
#define SDIO_FUNCTION GPIO_FUNCTION(12)
|
||||||
|
|
||||||
|
static const struct sdio_info sdio_bus[] = {
|
||||||
|
{ SDIO, GPIO('C', 12), GPIO('D', 2), GPIO('C', 8),
|
||||||
|
GPIO('C', 9), GPIO('C', 10), GPIO('C', 11), SDIO_FUNCTION },
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SDIO_CLK_FREQ 48000000
|
||||||
|
#define SDIO_INIT_CLK 400000
|
||||||
|
#define SDIO_MAX_TIMEOUT 500 // Wait for at least 500ms before a timeout occurs
|
||||||
|
#define CLKCR_CLEAR_MASK (SDIO_CLKCR_CLKDIV | SDIO_CLKCR_PWRSAV | \
|
||||||
|
SDIO_CLKCR_BYPASS | SDIO_CLKCR_WIDBUS | SDIO_CLKCR_NEGEDGE | \
|
||||||
|
SDIO_CLKCR_HWFC_EN)
|
||||||
|
#define DCTRL_CLEAR_MASK (SDIO_DCTRL_DTEN | SDIO_DCTRL_DTDIR | \
|
||||||
|
SDIO_DCTRL_DTMODE | SDIO_DCTRL_DBLOCKSIZE)
|
||||||
|
#define CMD_CLEAR_MASK (SDIO_CMD_CMDINDEX | SDIO_CMD_WAITRESP | \
|
||||||
|
SDIO_CMD_WAITINT | SDIO_CMD_WAITPEND | SDIO_CMD_CPSMEN | \
|
||||||
|
SDIO_CMD_SDIOSUSPEND)
|
||||||
|
#define SDIO_CLOCK_BYPASS_DISABLE 0
|
||||||
|
#define SDIO_CLOCK_EDGE_RISING 0
|
||||||
|
#define SDIO_CLOCK_POWER_SAVE_DISABLE 0
|
||||||
|
#define SDIO_BUS_WIDE_1B 0
|
||||||
|
#define SDIO_BUS_WIDE_4B SDIO_CLKCR_WIDBUS_0
|
||||||
|
#define SDIO_HARDWARE_FLOW_CONTROL_DISABLE 0
|
||||||
|
#define SDIO_HARDWARE_FLOW_CONTROL_ENABLE SDIO_CLKCR_HWFC_EN
|
||||||
|
#define SDIO_CMD_FLAGS (SDIO_STA_CCRCFAIL | SDIO_STA_CTIMEOUT | \
|
||||||
|
SDIO_STA_CMDREND | SDIO_STA_CMDSENT)
|
||||||
|
#define SDIO_STATIC_FLAGS (SDIO_STA_CCRCFAIL | SDIO_STA_DCRCFAIL | \
|
||||||
|
SDIO_STA_CTIMEOUT | SDIO_STA_DTIMEOUT | SDIO_STA_TXUNDERR | \
|
||||||
|
SDIO_STA_RXOVERR | SDIO_STA_CMDREND | SDIO_STA_CMDSENT | \
|
||||||
|
SDIO_STA_DATAEND | SDIO_STA_DBCKEND | SDIO_STA_SDIOIT)
|
||||||
|
|
||||||
|
struct sdio_config
|
||||||
|
sdio_setup(uint32_t bus)
|
||||||
|
{
|
||||||
|
if (bus >= ARRAY_SIZE(sdio_bus))
|
||||||
|
shutdown("Invalid sdio bus");
|
||||||
|
|
||||||
|
// Enable SDIO
|
||||||
|
SDIO_TypeDef *sdio = sdio_bus[bus].sdio;
|
||||||
|
if (!is_enabled_pclock((uint32_t)sdio)) {
|
||||||
|
// Enable clock
|
||||||
|
enable_pclock((uint32_t)sdio);
|
||||||
|
// Initialize pins
|
||||||
|
gpio_peripheral(
|
||||||
|
sdio_bus[bus].dat0_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||||
|
gpio_peripheral(
|
||||||
|
sdio_bus[bus].dat1_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||||
|
gpio_peripheral(
|
||||||
|
sdio_bus[bus].dat2_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||||
|
gpio_peripheral(
|
||||||
|
sdio_bus[bus].dat3_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||||
|
gpio_peripheral(
|
||||||
|
sdio_bus[bus].cmd_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||||
|
gpio_peripheral(
|
||||||
|
sdio_bus[bus].clk_pin, sdio_bus[bus].function|GPIO_HIGH_SPEED, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sdio_config sdio_config = { .sdio = sdio };
|
||||||
|
|
||||||
|
// Setup SDIO with 1 bit width first and slow clock ~400 kHz
|
||||||
|
sdio_set_speed(sdio_config, SDIO_INIT_CLK);
|
||||||
|
|
||||||
|
// Disable clk
|
||||||
|
CLEAR_BIT(sdio->CLKCR, SDIO_CLKCR_CLKEN);
|
||||||
|
// Set power state to _on_
|
||||||
|
sdio->POWER = SDIO_POWER_PWRCTRL;
|
||||||
|
// Wait for 2ms (standard: at least 1ms) to settle
|
||||||
|
udelay(2000);
|
||||||
|
// Enable Clk
|
||||||
|
SET_BIT(sdio->CLKCR, SDIO_CLKCR_CLKEN);
|
||||||
|
|
||||||
|
return sdio_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
sdio_get_cmd_error(struct sdio_config sdio, uint32_t flags)
|
||||||
|
{
|
||||||
|
SDIO_TypeDef *regs = sdio.sdio;
|
||||||
|
|
||||||
|
// wait for a timeout (max. SDIO_MAX_TIMEOUT) in msec.
|
||||||
|
// 8 cycles is the instruction cycles for the loop below.
|
||||||
|
uint32_t sta;
|
||||||
|
uint32_t count = SDIO_MAX_TIMEOUT * (SystemCoreClock / 8U / 1000U);
|
||||||
|
do {
|
||||||
|
if (count-- == 0) {
|
||||||
|
return SDIO_TIMEOUT;
|
||||||
|
}
|
||||||
|
sta = regs->STA;
|
||||||
|
} while ((sta & flags) == 0 || (sta & SDIO_STA_CMDACT) != 0);
|
||||||
|
|
||||||
|
regs->ICR = SDIO_CMD_FLAGS;
|
||||||
|
|
||||||
|
if (sta & SDIO_STA_CTIMEOUT) {
|
||||||
|
return SDIO_CMD_RESPONSE_TIMEOUT;
|
||||||
|
}
|
||||||
|
if (sta & SDIO_STA_CCRCFAIL) {
|
||||||
|
return SDIO_CRC_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDIO_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
sdio_send_command(struct sdio_config sdio_config, uint8_t cmd,
|
||||||
|
uint32_t argument, uint8_t wait, uint8_t *response_data,
|
||||||
|
uint8_t *response_data_len)
|
||||||
|
{
|
||||||
|
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||||
|
uint32_t wait_flags = 0; // valid for SDIO_WAIT_NO_RESPONSE
|
||||||
|
uint32_t sta_flags = (wait == SDIO_WAIT_NO_RESPONSE) ?
|
||||||
|
SDIO_STA_CMDSENT : SDIO_STA_CCRCFAIL | SDIO_STA_CMDREND | \
|
||||||
|
SDIO_STA_CTIMEOUT;
|
||||||
|
|
||||||
|
if (wait == SDIO_WAIT_SHORT_RESPONSE) {
|
||||||
|
wait_flags = SDIO_CMD_WAITRESP_0;
|
||||||
|
} else if (wait == SDIO_WAIT_LONG_RESPONSE) {
|
||||||
|
wait_flags = SDIO_CMD_WAITRESP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: Send command and argument
|
||||||
|
// CMD and State Machine enabled.
|
||||||
|
// Wait for response like specified by wait_flags.
|
||||||
|
uint32_t cmdreg = (cmd & 0x3F) | wait_flags | SDIO_CMD_CPSMEN;
|
||||||
|
sdio->ARG = argument;
|
||||||
|
MODIFY_REG(sdio->CMD, CMD_CLEAR_MASK, cmdreg);
|
||||||
|
|
||||||
|
// Step 2: Wait until response
|
||||||
|
// wait for a timeout (max. SDIO_MAX_TIMEOUT) in msec.
|
||||||
|
// 8 cycles is the instruction cycles for the loop below.
|
||||||
|
uint32_t sta;
|
||||||
|
uint32_t count = SDIO_MAX_TIMEOUT * (SystemCoreClock / 8U / 1000U);
|
||||||
|
do {
|
||||||
|
if (count-- == 0) {
|
||||||
|
return SDIO_TIMEOUT;
|
||||||
|
}
|
||||||
|
sta = sdio->STA;
|
||||||
|
} while ((sta & sta_flags) == 0 || (sta & SDIO_STA_CMDACT) != 0);
|
||||||
|
|
||||||
|
sdio->ICR = SDIO_CMD_FLAGS;
|
||||||
|
|
||||||
|
// Step 3: Store response_data and check for short and long responses
|
||||||
|
// timeout and crc.
|
||||||
|
if (response_data != NULL) {
|
||||||
|
if (wait == SDIO_WAIT_SHORT_RESPONSE) {
|
||||||
|
response_data[0] = (uint8_t) ((sdio->RESP1 >> 24) & 0xFF);
|
||||||
|
response_data[1] = (uint8_t) ((sdio->RESP1 >> 16) & 0xFF);
|
||||||
|
response_data[2] = (uint8_t) ((sdio->RESP1 >> 8) & 0xFF);
|
||||||
|
response_data[3] = (uint8_t) ((sdio->RESP1) & 0xFF);
|
||||||
|
*response_data_len = 4;
|
||||||
|
} else if (wait == SDIO_WAIT_LONG_RESPONSE) {
|
||||||
|
// TODO Inverse?
|
||||||
|
response_data[0] = (uint8_t) ((sdio->RESP1 >> 24) & 0xFF);
|
||||||
|
response_data[1] = (uint8_t) ((sdio->RESP1 >> 16) & 0xFF);
|
||||||
|
response_data[2] = (uint8_t) ((sdio->RESP1 >> 8) & 0xFF);
|
||||||
|
response_data[3] = (uint8_t) ((sdio->RESP1) & 0xFF);
|
||||||
|
response_data[4] = (uint8_t) ((sdio->RESP2 >> 24) & 0xFF);
|
||||||
|
response_data[5] = (uint8_t) ((sdio->RESP2 >> 16) & 0xFF);
|
||||||
|
response_data[6] = (uint8_t) ((sdio->RESP2 >> 8) & 0xFF);
|
||||||
|
response_data[7] = (uint8_t) ((sdio->RESP2) & 0xFF);
|
||||||
|
response_data[8] = (uint8_t) ((sdio->RESP3 >> 24) & 0xFF);
|
||||||
|
response_data[9] = (uint8_t) ((sdio->RESP3 >> 16) & 0xFF);
|
||||||
|
response_data[10] = (uint8_t) ((sdio->RESP3 >> 8) & 0xFF);
|
||||||
|
response_data[11] = (uint8_t) ((sdio->RESP3) & 0xFF);
|
||||||
|
response_data[12] = (uint8_t) ((sdio->RESP4 >> 24) & 0xFF);
|
||||||
|
response_data[13] = (uint8_t) ((sdio->RESP4 >> 16) & 0xFF);
|
||||||
|
response_data[14] = (uint8_t) ((sdio->RESP4 >> 8) & 0xFF);
|
||||||
|
response_data[15] = (uint8_t) ((sdio->RESP4) & 0xFF);
|
||||||
|
*response_data_len = 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wait != SDIO_WAIT_NO_RESPONSE) {
|
||||||
|
// CTIMEOUT and CCRCFAIL check only for short or long responses.
|
||||||
|
if (sta & SDIO_STA_CTIMEOUT) {
|
||||||
|
return SDIO_CMD_RESPONSE_TIMEOUT;
|
||||||
|
}
|
||||||
|
if (sta & SDIO_STA_CCRCFAIL) {
|
||||||
|
return SDIO_CRC_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: For a short response check the response cmd field, too.
|
||||||
|
if (wait == SDIO_WAIT_SHORT_RESPONSE) {
|
||||||
|
if (sdio->RESPCMD != cmd) {
|
||||||
|
return SDIO_WRONG_CMD_RESPONSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDIO_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
sdio_get_dctrl_blocksize(uint32_t value)
|
||||||
|
{
|
||||||
|
switch(value) {
|
||||||
|
case 1: return 0U;
|
||||||
|
case 2: return SDIO_DCTRL_DBLOCKSIZE_0;
|
||||||
|
case 4: return SDIO_DCTRL_DBLOCKSIZE_1;
|
||||||
|
case 8: return SDIO_DCTRL_DBLOCKSIZE_1|SDIO_DCTRL_DBLOCKSIZE_0;
|
||||||
|
case 16: return SDIO_DCTRL_DBLOCKSIZE_2;
|
||||||
|
case 32: return SDIO_DCTRL_DBLOCKSIZE_2|SDIO_DCTRL_DBLOCKSIZE_0;
|
||||||
|
case 64: return SDIO_DCTRL_DBLOCKSIZE_2|SDIO_DCTRL_DBLOCKSIZE_1;
|
||||||
|
case 128: return SDIO_DCTRL_DBLOCKSIZE_2|SDIO_DCTRL_DBLOCKSIZE_1| \
|
||||||
|
SDIO_DCTRL_DBLOCKSIZE_0;
|
||||||
|
case 256: return SDIO_DCTRL_DBLOCKSIZE_3;
|
||||||
|
case 512: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_0;
|
||||||
|
case 1024: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_1;
|
||||||
|
case 2048: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_1| \
|
||||||
|
SDIO_DCTRL_DBLOCKSIZE_0;
|
||||||
|
case 4096: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_2;
|
||||||
|
case 8192: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_2| \
|
||||||
|
SDIO_DCTRL_DBLOCKSIZE_0;
|
||||||
|
case 16384: return SDIO_DCTRL_DBLOCKSIZE_3|SDIO_DCTRL_DBLOCKSIZE_2| \
|
||||||
|
SDIO_DCTRL_DBLOCKSIZE_1;
|
||||||
|
}
|
||||||
|
return SDIO_DCTRL_DBLOCKSIZE_Msk;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
sdio_prepare_data_transfer(struct sdio_config sdio_config, uint8_t read,
|
||||||
|
uint32_t numblocks, uint32_t blocksize, uint32_t timeout)
|
||||||
|
{
|
||||||
|
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||||
|
uint32_t dctrl_blocksize = sdio_get_dctrl_blocksize(blocksize);
|
||||||
|
uint32_t reg = dctrl_blocksize | ((read > 0) ?
|
||||||
|
SDIO_DCTRL_DTDIR : 0U) | SDIO_DCTRL_DTEN;
|
||||||
|
|
||||||
|
if (dctrl_blocksize == SDIO_DCTRL_DBLOCKSIZE_Msk)
|
||||||
|
return SDIO_WRONG_BLOCKSIZE;
|
||||||
|
|
||||||
|
sdio->DCTRL = 0;
|
||||||
|
sdio->DTIMER = timeout;
|
||||||
|
sdio->DLEN = numblocks*blocksize;
|
||||||
|
MODIFY_REG(sdio->DCTRL, DCTRL_CLEAR_MASK, reg);
|
||||||
|
return SDIO_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
sdio_read_data(struct sdio_config sdio_config, uint8_t *data,
|
||||||
|
uint32_t numblocks, uint32_t blocksize)
|
||||||
|
{
|
||||||
|
// Read data by polling
|
||||||
|
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||||
|
uint32_t data_remaining = numblocks*blocksize;
|
||||||
|
uint8_t *buf = data;
|
||||||
|
|
||||||
|
if (data == NULL) {
|
||||||
|
return SDIO_NO_DATA_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((sdio->STA & (SDIO_STA_RXOVERR | SDIO_STA_DCRCFAIL |
|
||||||
|
SDIO_STA_DTIMEOUT | SDIO_STA_DATAEND)) == 0) {
|
||||||
|
if ((sdio->STA & SDIO_STA_RXDAVL) != 0) {
|
||||||
|
uint32_t tmp = sdio->FIFO;
|
||||||
|
for (uint8_t i=0; (i<4) && (data_remaining>0); i++) {
|
||||||
|
*buf = (uint8_t)(tmp & 0xFF);
|
||||||
|
buf++;
|
||||||
|
data_remaining--;
|
||||||
|
tmp >>= 8U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t sta = sdio->STA;
|
||||||
|
sdio->ICR = SDIO_STATIC_FLAGS;
|
||||||
|
|
||||||
|
if (sta & SDIO_STA_DTIMEOUT) {
|
||||||
|
return SDIO_DATA_TIMEOUT;
|
||||||
|
}
|
||||||
|
if (sta & SDIO_STA_DCRCFAIL) {
|
||||||
|
return SDIO_CRC_FAIL;
|
||||||
|
}
|
||||||
|
if (sta & SDIO_STA_RXOVERR) {
|
||||||
|
return SDIO_READ_OVERRUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty FIFO and clear flags again
|
||||||
|
while (((sdio->STA & SDIO_STA_RXDAVL) != 0) && (data_remaining > 0)) {
|
||||||
|
uint32_t tmp = sdio->FIFO;
|
||||||
|
for (uint8_t i=0; (i<4) && (data_remaining>0); i++) {
|
||||||
|
*buf = (uint8_t)(tmp & 0xFF);
|
||||||
|
buf++;
|
||||||
|
data_remaining--;
|
||||||
|
tmp >>= 8U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sdio->ICR = SDIO_STATIC_FLAGS;
|
||||||
|
return SDIO_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
sdio_write_data(struct sdio_config sdio_config, uint8_t *data,
|
||||||
|
uint32_t numblocks, uint32_t blocksize)
|
||||||
|
{
|
||||||
|
// Write data by polling
|
||||||
|
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||||
|
uint32_t data_remaining = numblocks*blocksize;
|
||||||
|
uint8_t *buf = data;
|
||||||
|
|
||||||
|
if (data == NULL) {
|
||||||
|
return SDIO_NO_DATA_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((sdio->STA & (SDIO_STA_TXUNDERR | SDIO_STA_DCRCFAIL |
|
||||||
|
SDIO_STA_DTIMEOUT | SDIO_STA_DATAEND)) == 0) {
|
||||||
|
if ((sdio->STA & SDIO_STA_TXFIFOF) == 0) {
|
||||||
|
uint32_t tmp = 0;
|
||||||
|
for (uint8_t i=0; (i<4) && (data_remaining>0); i++) {
|
||||||
|
tmp |= ((uint32_t)(*buf) << (i<<3));
|
||||||
|
buf++;
|
||||||
|
data_remaining--;
|
||||||
|
}
|
||||||
|
sdio->FIFO = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t sta = sdio->STA;
|
||||||
|
sdio->ICR = SDIO_STATIC_FLAGS;
|
||||||
|
|
||||||
|
if (sta & SDIO_STA_DTIMEOUT) {
|
||||||
|
return SDIO_DATA_TIMEOUT;
|
||||||
|
}
|
||||||
|
if (sta & SDIO_STA_DCRCFAIL) {
|
||||||
|
return SDIO_CRC_FAIL;
|
||||||
|
}
|
||||||
|
if (sta & SDIO_STA_TXUNDERR) {
|
||||||
|
return SDIO_WRITE_UNDERRUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDIO_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sdio_send_cmd(struct sdio_config sdio_config, uint8_t cmd, uint32_t argument,
|
||||||
|
uint8_t wait)
|
||||||
|
{
|
||||||
|
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||||
|
|
||||||
|
sdio->ARG = argument;
|
||||||
|
//CMD and State Machine enabled. Wait for response like specified.
|
||||||
|
uint32_t cmdreg = (cmd & 0x3F) | ((wait & 0xC0)) | SDIO_CMD_CPSMEN;
|
||||||
|
|
||||||
|
MODIFY_REG(sdio->CMD, CMD_CLEAR_MASK, cmdreg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sdio_set_speed(struct sdio_config sdio_config, uint32_t speed)
|
||||||
|
{
|
||||||
|
SDIO_TypeDef *sdio = sdio_config.sdio;
|
||||||
|
|
||||||
|
uint8_t clkdiv = (SDIO_CLK_FREQ/speed)-2;
|
||||||
|
|
||||||
|
uint32_t sdio_confreg = SDIO_CLOCK_EDGE_RISING | \
|
||||||
|
SDIO_CLOCK_BYPASS_DISABLE | SDIO_CLOCK_POWER_SAVE_DISABLE | \
|
||||||
|
SDIO_BUS_WIDE_1B | SDIO_HARDWARE_FLOW_CONTROL_DISABLE | (clkdiv & 0xFF);
|
||||||
|
MODIFY_REG(sdio->CLKCR, CLKCR_CLEAR_MASK, sdio_confreg);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef __STM32_SDIO_H
|
||||||
|
#define __STM32_SDIO_H
|
||||||
|
|
||||||
|
#include <stdint.h> // uint32_t
|
||||||
|
|
||||||
|
struct sdio_config {
|
||||||
|
void *sdio;
|
||||||
|
};
|
||||||
|
struct sdio_config sdio_setup(uint32_t bus);
|
||||||
|
void sdio_send_cmd(struct sdio_config sdio, uint8_t cmd, uint32_t argument
|
||||||
|
, uint8_t wait);
|
||||||
|
uint8_t sdio_send_command(struct sdio_config sdio_config, uint8_t cmd
|
||||||
|
, uint32_t argument, uint8_t wait
|
||||||
|
, uint8_t *response_data
|
||||||
|
, uint8_t *response_data_len);
|
||||||
|
uint8_t sdio_prepare_data_transfer(struct sdio_config sdio_config, uint8_t read
|
||||||
|
, uint32_t numblocks, uint32_t blocksize
|
||||||
|
, uint32_t timeout);
|
||||||
|
uint8_t sdio_read_data(struct sdio_config sdio_config, uint8_t *data
|
||||||
|
, uint32_t numblocks, uint32_t blocksize);
|
||||||
|
uint8_t sdio_write_data(struct sdio_config sdio_config, uint8_t *data
|
||||||
|
, uint32_t numblocks, uint32_t blocksize);
|
||||||
|
void sdio_set_speed(struct sdio_config sdio_config, uint32_t speed);
|
||||||
|
|
||||||
|
#endif // sdio.h
|
|
@ -138,8 +138,8 @@ enable_clock_stm32f446(void)
|
||||||
while (!(PWR->CSR & PWR_CSR_ODSWRDY))
|
while (!(PWR->CSR & PWR_CSR_ODSWRDY))
|
||||||
;
|
;
|
||||||
|
|
||||||
// Enable 48Mhz USB clock
|
// Enable 48Mhz USB clock for USB or for SDIO
|
||||||
if (CONFIG_USB) {
|
if (CONFIG_USB || CONFIG_HAVE_GPIO_SDIO) {
|
||||||
uint32_t ref = (CONFIG_STM32_CLOCK_REF_INTERNAL
|
uint32_t ref = (CONFIG_STM32_CLOCK_REF_INTERNAL
|
||||||
? 16000000 : CONFIG_CLOCK_REF_FREQ);
|
? 16000000 : CONFIG_CLOCK_REF_FREQ);
|
||||||
uint32_t plls_base = 2000000, plls_freq = FREQ_USB * 4;
|
uint32_t plls_base = 2000000, plls_freq = FREQ_USB * 4;
|
||||||
|
@ -153,6 +153,14 @@ enable_clock_stm32f446(void)
|
||||||
;
|
;
|
||||||
|
|
||||||
RCC->DCKCFGR2 = RCC_DCKCFGR2_CK48MSEL;
|
RCC->DCKCFGR2 = RCC_DCKCFGR2_CK48MSEL;
|
||||||
|
} else {
|
||||||
|
// Reset value just in case the booloader modified the default value
|
||||||
|
RCC->DCKCFGR2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set SDIO clk to PLL48CLK
|
||||||
|
if (CONFIG_HAVE_GPIO_SDIO) {
|
||||||
|
MODIFY_REG(RCC->DCKCFGR2, RCC_DCKCFGR2_SDIOSEL, 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue