Initial broadcom port for Raspberry Pi

This targets the 64-bit CPU Raspberry Pis. The BCM2711 on the Pi 4
and the BCM2837 on the Pi 3 and Zero 2W. There are 64-bit fixes
outside of the ports directory for it.

There are a couple other cleanups that were incidental:
* Use const mcu_pin_obj_t instead of omitting the const. The structs
  themselves are const because they are in ROM.
* Use PTR <-> OBJ conversions in more places. They were found when
  mp_obj_t was set to an integer type rather than pointer.
* Optimize submodule checkout because the Pi submodules are heavy
  and unnecessary for the vast majority of builds.

Fixes #4314
This commit is contained in:
Timon 2021-08-05 01:27:54 +02:00 committed by Scott Shawcroft
parent 387a8a46b3
commit a1052d5f73
No known key found for this signature in database
GPG Key ID: 0DFD512649C052DA
176 changed files with 8477 additions and 346 deletions

View File

@ -20,6 +20,7 @@ jobs:
boards-arm: ${{ steps.set-matrix.outputs.boards-arm }}
boards-riscv: ${{ steps.set-matrix.outputs.boards-riscv }}
boards-espressif: ${{ steps.set-matrix.outputs.boards-espressif }}
boards-aarch: ${{ steps.set-matrix.outputs.boards-aarch }}
steps:
- name: Dump GitHub context
env:
@ -28,19 +29,17 @@ jobs:
- uses: actions/checkout@v2.2.0
with:
submodules: false
fetch-depth: 0
- name: Populate selected submodules
run: git submodule update --init extmod/ lib/ tools/
- name: Fetch tags
run: git fetch --recurse-submodules=no https://github.com/adafruit/circuitpython refs/tags/*:refs/tags/*
- name: CircuitPython version
run: |
git describe --dirty --tags
echo >>$GITHUB_ENV CP_VERSION=$(git describe --dirty --tags)
fetch-depth: 1
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Get CP deps
run: python tools/ci_fetch_deps.py test ${{ github.ref }}
- name: CircuitPython version
run: |
git describe --dirty --tags
echo >>$GITHUB_ENV CP_VERSION=$(git describe --dirty --tags)
- name: Install dependencies
run: |
sudo apt-get update
@ -136,19 +135,17 @@ jobs:
- uses: actions/checkout@v2.2.0
with:
submodules: false
fetch-depth: 0
- name: Populate selected submodules
run: git submodule update --init extmod/ lib/ tools/
- name: Fetch tags
run: git fetch --recurse-submodules=no https://github.com/adafruit/circuitpython refs/tags/*:refs/tags/*
- name: CircuitPython version
run: |
git describe --dirty --tags
echo >>$GITHUB_ENV CP_VERSION=$(git describe --dirty --tags)
fetch-depth: 1
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Get CP deps
run: python tools/ci_fetch_deps.py mpy-cross-mac ${{ github.ref }}
- name: CircuitPython version
run: |
git describe --dirty --tags
echo >>$GITHUB_ENV CP_VERSION=$(git describe --dirty --tags)
- name: Install dependencies
run: |
brew install gettext
@ -198,11 +195,9 @@ jobs:
- uses: actions/checkout@v2.2.0
with:
submodules: false
fetch-depth: 0
- name: Populate selected submodules
run: git submodule update --init extmod/
- name: Fetch tags
run: git fetch --recurse-submodules=no https://github.com/adafruit/circuitpython refs/tags/*:refs/tags/*
fetch-depth: 1
- name: Get CP deps
run: python tools/ci_fetch_deps.py docs ${{ github.ref }}
- name: CircuitPython version
run: |
git describe --dirty --tags
@ -271,10 +266,10 @@ jobs:
python-version: 3.8
- uses: actions/checkout@v2.2.0
with:
submodules: true
fetch-depth: 0
- name: Fetch tags
run: git fetch --recurse-submodules=no https://github.com/adafruit/circuitpython refs/tags/*:refs/tags/*
submodules: false
fetch-depth: 1
- name: Get CP deps
run: python tools/ci_fetch_deps.py ${{ matrix.board }} ${{ github.ref }}
- name: Install dependencies
run: |
sudo apt-get install -y gettext
@ -323,10 +318,10 @@ jobs:
python-version: 3.8
- uses: actions/checkout@v2.2.0
with:
submodules: true
fetch-depth: 0
- name: Fetch tags
run: git fetch --recurse-submodules=no https://github.com/adafruit/circuitpython refs/tags/*:refs/tags/*
submodules: false
fetch-depth: 1
- name: Get CP deps
run: python tools/ci_fetch_deps.py ${{ matrix.board }} ${{ github.ref }}
- name: Install dependencies
run: |
sudo apt-get install -y gettext
@ -375,10 +370,10 @@ jobs:
python-version: 3.8
- uses: actions/checkout@v2.2.0
with:
submodules: true
fetch-depth: 0
- name: Fetch tags
run: git fetch --recurse-submodules=no https://github.com/adafruit/circuitpython refs/tags/*:refs/tags/*
submodules: false
fetch-depth: 1
- name: Get CP deps
run: python tools/ci_fetch_deps.py ${{ matrix.board }} ${{ github.ref }}
- name: CircuitPython version
run: git describe --dirty --tags
- uses: actions/cache@v2
@ -446,3 +441,67 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
if: (github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository_owner == 'adafruit') || (github.event_name == 'release' && (github.event.action == 'published' || github.event.action == 'rerequested'))
build-aarch:
runs-on: ubuntu-20.04
needs: test
strategy:
fail-fast: false
matrix:
board: ${{ fromJSON(needs.test.outputs.boards-aarch) }}
if: ${{ needs.test.outputs.boards-aarch != '[]' }}
steps:
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- uses: actions/checkout@v2.2.0
with:
submodules: false
fetch-depth: 1
- name: Get CP deps
run: python tools/ci_fetch_deps.py ${{ matrix.board }} ${{ github.ref }}
- name: Install dependencies
run: |
sudo apt-get install -y gettext mtools
pip install -r requirements-ci.txt -r requirements-dev.txt
wget --no-verbose https://adafruit-circuit-python.s3.amazonaws.com/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz
sudo tar -C /usr --strip-components=1 -xaf gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz
wget --no-verbose https://adafruit-circuit-python.s3.amazonaws.com/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2
sudo tar -C /usr --strip-components=1 -xaf gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2
- name: Install mkfs.fat
run: |
wget https://github.com/dosfstools/dosfstools/releases/download/v4.2/dosfstools-4.2.tar.gz
tar -xaf dosfstools-4.2.tar.gz
cd dosfstools-4.2
./configure
make -j 2
cd src
echo >>$GITHUB_PATH $(pwd)
- name: Versions
run: |
gcc --version
aarch64-none-elf-gcc --version
arm-none-eabi-gcc --version
python3 --version
mkfs.fat --version || true
- name: mpy-cross
run: make -C mpy-cross -j2
- name: Setup build failure matcher
run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/workflows/match-build-fail.json"
- name: build
run: python3 -u build_release_files.py
working-directory: tools
env:
BOARDS: ${{ matrix.board }}
- uses: actions/upload-artifact@v2
with:
name: ${{ matrix.board }}
path: bin/${{ matrix.board }}
- name: Upload to S3
run: "[ -z \"$AWS_ACCESS_KEY_ID\" ] || aws s3 cp bin/ s3://adafruit-circuit-python/bin/ --recursive --no-progress --region us-east-1"
env:
AWS_PAGER: ''
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
if: (github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository_owner == 'adafruit') || (github.event_name == 'release' && (github.event.action == 'published' || github.event.action == 'rerequested'))

View File

@ -18,12 +18,14 @@ jobs:
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2.2.0
with:
submodules: true
fetch-depth: 0
submodules: false
fetch-depth: 1
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Get CP deps
run: python tools/ci_fetch_deps.py website ${{ github.ref }}
- name: Install deps
run: |
pip install -r requirements-dev.txt
@ -31,7 +33,6 @@ jobs:
run: |
gcc --version
python3 --version
- run: git fetch --recurse-submodules=no https://github.com/adafruit/circuitpython refs/tags/*:refs/tags/*
- name: CircuitPython version
run: git describe --dirty --tags
- name: Website

View File

@ -65,12 +65,12 @@ jobs:
which python; python --version; python -c "import cascadetoml"
which python3; python3 --version; python3 -c "import cascadetoml"
- uses: actions/checkout@v2
- uses: actions/checkout@v2.2.0
with:
submodules: true
fetch-depth: 0
- run: git fetch --recurse-submodules=no https://github.com/adafruit/circuitpython refs/tags/*:refs/tags/*
submodules: false
fetch-depth: 1
- name: Get CP deps
run: python tools/ci_fetch_deps.py windows ${{ github.ref }}
- name: CircuitPython version
run: |
git describe --dirty --tags

View File

@ -12,7 +12,7 @@ jobs:
pre-commit:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2.2.0
- uses: actions/setup-python@v1
- name: Install deps
run: |

8
.gitmodules vendored
View File

@ -190,3 +190,11 @@
[submodule "frozen/Adafruit_CircuitPython_APDS9960"]
path = frozen/Adafruit_CircuitPython_APDS9960
url = https://github.com/adafruit/Adafruit_CircuitPython_APDS9960
[submodule "ports/broadcom/peripherals"]
path = ports/broadcom/peripherals
url = https://github.com/adafruit/broadcom-peripherals.git
branch = main-build
[submodule "rpi-firmware"]
path = ports/broadcom/firmware
url = https://github.com/raspberrypi/rpi-firmware.git
branch = master

View File

@ -186,6 +186,8 @@ exclude_patterns = ["**/build*",
"ports/atmel-samd/peripherals",
"ports/atmel-samd/QTouch",
"ports/atmel-samd/tools",
"ports/broadcom/firmware",
"ports/broadcom/peripherals",
"ports/cxd56/mkspk",
"ports/cxd56/spresense-exported-sdk",
"ports/espressif/certificates",

View File

@ -938,8 +938,8 @@ uint16_t bleio_adapter_max_attribute_handle(bleio_adapter_obj_t *adapter) {
void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter) {
gc_collect_root((void **)adapter, sizeof(bleio_adapter_obj_t) / sizeof(size_t));
gc_collect_root((void **)bleio_connections, sizeof(bleio_connections) / sizeof(size_t));
gc_collect_root((void **)adapter, sizeof(bleio_adapter_obj_t) / (sizeof(size_t)));
gc_collect_root((void **)bleio_connections, sizeof(bleio_connections) / (sizeof(size_t)));
}
void bleio_adapter_reset(bleio_adapter_obj_t *adapter) {

View File

@ -30,7 +30,7 @@ import sys
from concurrent.futures import ThreadPoolExecutor
SUPPORTED_PORTS = ['atmel-samd', 'cxd56', 'espressif', 'litex', 'mimxrt10xx', 'nrf', 'raspberrypi', 'stm']
SUPPORTED_PORTS = ['atmel-samd', 'broadcom', 'cxd56', 'espressif', 'litex', 'mimxrt10xx', 'nrf', 'raspberrypi', 'stm']
aliases_by_board = {
"circuitplayground_express": [

View File

@ -440,7 +440,7 @@ STATIC const mp_obj_property_t fat_vfs_label_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&fat_vfs_getlabel_obj,
(mp_obj_t)&fat_vfs_setlabel_obj,
MP_ROM_NONE},
(mp_obj_t)MP_ROM_NONE},
};
#endif

View File

@ -1985,7 +1985,7 @@ static void gen_numname (
if (c > '9') c += 7;
ns[i--] = c;
seq /= 16;
} while (seq);
} while (seq && i > 0);
ns[i] = '~';
/* Append the number to the SFN body */

9
lib/sdmmc/CMakeLists.txt Normal file
View File

@ -0,0 +1,9 @@
idf_component_register(SRCS "sdmmc_cmd.c"
"sdmmc_common.c"
"sdmmc_init.c"
"sdmmc_io.c"
"sdmmc_mmc.c"
"sdmmc_sd.c"
INCLUDE_DIRS include
REQUIRES driver
PRIV_REQUIRES soc)

0
lib/sdmmc/component.mk Normal file
View File

View File

@ -0,0 +1,274 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdio.h>
#include "sdmmc_defs.h"
#include "sdmmc_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Probe and initialize SD/MMC card using given host
*
* @note Only SD cards (SDSC and SDHC/SDXC) are supported now.
* Support for MMC/eMMC cards will be added later.
*
* @param host pointer to structure defining host controller
* @param out_card pointer to structure which will receive information
* about the card when the function completes
* @return
* - SDMMC_OK on success
* - One of the error codes from SDMMC host controller
*/
sdmmc_err_t sdmmc_card_init(const sdmmc_host_t* host,
sdmmc_card_t* out_card);
/**
* @brief Print information about the card to a stream
* @param stream stream obtained using fopen or fdopen
* @param card card information structure initialized using sdmmc_card_init
*/
void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card);
/**
* Write given number of sectors to SD/MMC card
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @param src pointer to data buffer to read data from; data size must be
* equal to sector_count * card->csd.sector_size
* @param start_sector sector where to start writing
* @param sector_count number of sectors to write
* @return
* - SDMMC_OK on success
* - One of the error codes from SDMMC host controller
*/
sdmmc_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
size_t start_sector, size_t sector_count);
/**
* Read given number of sectors from the SD/MMC card
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @param dst pointer to data buffer to write into; buffer size must be
* at least sector_count * card->csd.sector_size
* @param start_sector sector where to start reading
* @param sector_count number of sectors to read
* @return
* - SDMMC_OK on success
* - One of the error codes from SDMMC host controller
*/
sdmmc_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
size_t start_sector, size_t sector_count);
/**
* Read one byte from an SDIO card using IO_RW_DIRECT (CMD52)
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @param function IO function number
* @param reg byte address within IO function
* @param[out] out_byte output, receives the value read from the card
* @return
* - SDMMC_OK on success
* - One of the error codes from SDMMC host controller
*/
sdmmc_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function,
uint32_t reg, uint8_t *out_byte);
/**
* Write one byte to an SDIO card using IO_RW_DIRECT (CMD52)
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @param function IO function number
* @param reg byte address within IO function
* @param in_byte value to be written
* @param[out] out_byte if not NULL, receives new byte value read
* from the card (read-after-write).
* @return
* - SDMMC_OK on success
* - One of the error codes from SDMMC host controller
*/
sdmmc_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function,
uint32_t reg, uint8_t in_byte, uint8_t* out_byte);
/**
* Read multiple bytes from an SDIO card using IO_RW_EXTENDED (CMD53)
*
* This function performs read operation using CMD53 in byte mode.
* For block mode, see sdmmc_io_read_blocks.
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @param function IO function number
* @param addr byte address within IO function where reading starts
* @param dst buffer which receives the data read from card
* @param size number of bytes to read
* @return
* - SDMMC_OK on success
* - SDMMC_ERR_INVALID_SIZE if size exceeds 512 bytes
* - One of the error codes from SDMMC host controller
*/
sdmmc_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
uint32_t addr, void* dst, size_t size);
/**
* Write multiple bytes to an SDIO card using IO_RW_EXTENDED (CMD53)
*
* This function performs write operation using CMD53 in byte mode.
* For block mode, see sdmmc_io_write_blocks.
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @param function IO function number
* @param addr byte address within IO function where writing starts
* @param src data to be written
* @param size number of bytes to write
* @return
* - SDMMC_OK on success
* - SDMMC_ERR_INVALID_SIZE if size exceeds 512 bytes
* - One of the error codes from SDMMC host controller
*/
sdmmc_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
uint32_t addr, const void* src, size_t size);
/**
* Read blocks of data from an SDIO card using IO_RW_EXTENDED (CMD53)
*
* This function performs read operation using CMD53 in block mode.
* For byte mode, see sdmmc_io_read_bytes.
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @param function IO function number
* @param addr byte address within IO function where writing starts
* @param dst buffer which receives the data read from card
* @param size number of bytes to read, must be divisible by the card block
* size.
* @return
* - SDMMC_OK on success
* - SDMMC_ERR_INVALID_SIZE if size is not divisible by 512 bytes
* - One of the error codes from SDMMC host controller
*/
sdmmc_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
uint32_t addr, void* dst, size_t size);
/**
* Write blocks of data to an SDIO card using IO_RW_EXTENDED (CMD53)
*
* This function performs write operation using CMD53 in block mode.
* For byte mode, see sdmmc_io_write_bytes.
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @param function IO function number
* @param addr byte address within IO function where writing starts
* @param src data to be written
* @param size number of bytes to read, must be divisible by the card block
* size.
* @return
* - SDMMC_OK on success
* - SDMMC_ERR_INVALID_SIZE if size is not divisible by 512 bytes
* - One of the error codes from SDMMC host controller
*/
sdmmc_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
uint32_t addr, const void* src, size_t size);
/**
* Enable SDIO interrupt in the SDMMC host
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @return
* - SDMMC_OK on success
* - SDMMC_ERR_NOT_SUPPORTED if the host controller does not support
* IO interrupts
*/
sdmmc_err_t sdmmc_io_enable_int(sdmmc_card_t* card);
/**
* Block until an SDIO interrupt is received
*
* Slave uses D1 line to signal interrupt condition to the host.
* This function can be used to wait for the interrupt.
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @param timeout_ticks time to wait for the interrupt, in RTOS ticks
* @return
* - SDMMC_OK if the interrupt is received
* - SDMMC_ERR_NOT_SUPPORTED if the host controller does not support
* IO interrupts
* - SDMMC_ERR_TIMEOUT if the interrupt does not happen in timeout_ticks
*/
sdmmc_err_t sdmmc_io_wait_int(sdmmc_card_t* card, int timeout_ms);
/**
* Get the data of CIS region of a SDIO card.
*
* You may provide a buffer not sufficient to store all the CIS data. In this
* case, this functions store as much data into your buffer as possible. Also,
* this function will try to get and return the size required for you.
*
* @param card pointer to card information structure previously initialized
* using sdmmc_card_init
* @param out_buffer Output buffer of the CIS data
* @param buffer_size Size of the buffer.
* @param inout_cis_size Mandatory, pointer to a size, input and output.
* - input: Limitation of maximum searching range, should be 0 or larger than
* buffer_size. The function searches for CIS_CODE_END until this range. Set to
* 0 to search infinitely.
* - output: The size required to store all the CIS data, if CIS_CODE_END is found.
*
* @return
* - SDMMC_OK: on success
* - SDMMC_ERR_INVALID_RESPONSE: if the card does not (correctly) support CIS.
* - SDMMC_ERR_INVALID_SIZE: CIS_CODE_END found, but buffer_size is less than
* required size, which is stored in the inout_cis_size then.
* - SDMMC_ERR_NOT_FOUND: if the CIS_CODE_END not found. Increase input value of
* inout_cis_size or set it to 0, if you still want to search for the end;
* output value of inout_cis_size is invalid in this case.
* - and other error code return from sdmmc_io_read_bytes
*/
sdmmc_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size);
/**
* Parse and print the CIS information of a SDIO card.
*
* @note Not all the CIS codes and all kinds of tuples are supported. If you
* see some unresolved code, you can add the parsing of these code in
* sdmmc_io.c and contribute to the IDF through the Github repository.
*
* using sdmmc_card_init
* @param buffer Buffer to parse
* @param buffer_size Size of the buffer.
* @param fp File pointer to print to, set to NULL to print to stdout.
*
* @return
* - SDMMC_OK: on success
* - SDMMC_ERR_NOT_SUPPORTED: if the value from the card is not supported to be parsed.
* - SDMMC_ERR_INVALID_SIZE: if the CIS size fields are not correct.
*/
sdmmc_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,498 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SDMMC_DEFS_H_
#define _SDMMC_DEFS_H_
#include <stdint.h>
#include <limits.h>
/* MMC commands */ /* response type */
#define MMC_GO_IDLE_STATE 0 /* R0 */
#define MMC_SEND_OP_COND 1 /* R3 */
#define MMC_ALL_SEND_CID 2 /* R2 */
#define MMC_SET_RELATIVE_ADDR 3 /* R1 */
#define MMC_SWITCH 6 /* R1B */
#define MMC_SELECT_CARD 7 /* R1 */
#define MMC_SEND_EXT_CSD 8 /* R1 */
#define MMC_SEND_CSD 9 /* R2 */
#define MMC_SEND_CID 10 /* R1 */
#define MMC_READ_DAT_UNTIL_STOP 11 /* R1 */
#define MMC_STOP_TRANSMISSION 12 /* R1B */
#define MMC_SEND_STATUS 13 /* R1 */
#define MMC_SET_BLOCKLEN 16 /* R1 */
#define MMC_READ_BLOCK_SINGLE 17 /* R1 */
#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* R1 */
#define MMC_SET_BLOCK_COUNT 23 /* R1 */
#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */
#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */
#define MMC_APP_CMD 55 /* R1 */
/* SD commands */ /* response type */
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
#define SD_SEND_SWITCH_FUNC 6 /* R1 */
#define SD_SEND_IF_COND 8 /* R7 */
#define SD_READ_OCR 58 /* R3 */
#define SD_CRC_ON_OFF 59 /* R1 */
/* SD application commands */ /* response type */
#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
#define SD_APP_SD_STATUS 13 /* R2 */
#define SD_APP_OP_COND 41 /* R3 */
#define SD_APP_SEND_SCR 51 /* R1 */
/* SD IO commands */
#define SD_IO_SEND_OP_COND 5 /* R4 */
#define SD_IO_RW_DIRECT 52 /* R5 */
#define SD_IO_RW_EXTENDED 53 /* R5 */
/* OCR bits */
#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */
#define MMC_OCR_ACCESS_MODE_MASK 0x60000000 /* bits 30:29 */
#define MMC_OCR_SECTOR_MODE (1<<30)
#define MMC_OCR_BYTE_MODE (1<<29)
#define MMC_OCR_3_5V_3_6V (1<<23)
#define MMC_OCR_3_4V_3_5V (1<<22)
#define MMC_OCR_3_3V_3_4V (1<<21)
#define MMC_OCR_3_2V_3_3V (1<<20)
#define MMC_OCR_3_1V_3_2V (1<<19)
#define MMC_OCR_3_0V_3_1V (1<<18)
#define MMC_OCR_2_9V_3_0V (1<<17)
#define MMC_OCR_2_8V_2_9V (1<<16)
#define MMC_OCR_2_7V_2_8V (1<<15)
#define MMC_OCR_2_6V_2_7V (1<<14)
#define MMC_OCR_2_5V_2_6V (1<<13)
#define MMC_OCR_2_4V_2_5V (1<<12)
#define MMC_OCR_2_3V_2_4V (1<<11)
#define MMC_OCR_2_2V_2_3V (1<<10)
#define MMC_OCR_2_1V_2_2V (1<<9)
#define MMC_OCR_2_0V_2_1V (1<<8)
#define MMC_OCR_1_65V_1_95V (1<<7)
#define SD_OCR_SDHC_CAP (1<<30)
#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */
/* SD mode R1 response type bits */
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */
#define MMC_R1_SWITCH_ERROR (1<<7) /* switch command did not succeed */
/* SPI mode R1 response type bits */
#define SD_SPI_R1_IDLE_STATE (1<<0)
#define SD_SPI_R1_ERASE_RST (1<<1)
#define SD_SPI_R1_ILLEGAL_CMD (1<<2)
#define SD_SPI_R1_CMD_CRC_ERR (1<<3)
#define SD_SPI_R1_ERASE_SEQ_ERR (1<<4)
#define SD_SPI_R1_ADDR_ERR (1<<5)
#define SD_SPI_R1_PARAM_ERR (1<<6)
#define SD_SPI_R1_NO_RESPONSE (1<<7)
#define SDIO_R1_FUNC_NUM_ERR (1<<4)
/* 48-bit response decoding (32 bits w/o CRC) */
#define MMC_R1(resp) ((resp)[0])
#define MMC_R3(resp) ((resp)[0])
#define MMC_R4(resp) ((resp)[0])
#define MMC_R5(resp) ((resp)[0])
#define SD_R6(resp) ((resp)[0])
#define MMC_R1_CURRENT_STATE(resp) (((resp)[0] >> 9) & 0xf)
/* SPI mode response decoding */
#define SD_SPI_R1(resp) ((resp)[0] & 0xff)
#define SD_SPI_R2(resp) ((resp)[0] & 0xffff)
#define SD_SPI_R3(resp) ((resp)[0])
#define SD_SPI_R7(resp) ((resp)[0])
/* SPI mode data response decoding */
#define SD_SPI_DATA_RSP_VALID(resp_byte) (((resp_byte)&0x11)==0x1)
#define SD_SPI_DATA_RSP(resp_byte) (((resp_byte)>>1)&0x7)
#define SD_SPI_DATA_ACCEPTED 0x2
#define SD_SPI_DATA_CRC_ERROR 0x5
#define SD_SPI_DATA_WR_ERROR 0x6
/* RCA argument and response */
#define MMC_ARG_RCA(rca) ((rca) << 16)
#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16)
/* bus width argument */
#define SD_ARG_BUS_WIDTH_1 0
#define SD_ARG_BUS_WIDTH_4 2
/* EXT_CSD fields */
#define EXT_CSD_BUS_WIDTH 183 /* WO */
#define EXT_CSD_HS_TIMING 185 /* R/W */
#define EXT_CSD_REV 192 /* RO */
#define EXT_CSD_STRUCTURE 194 /* RO */
#define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_SEC_COUNT 212 /* RO */
#define EXT_CSD_PWR_CL_26_360 203 /* RO */
#define EXT_CSD_PWR_CL_52_360 202 /* RO */
#define EXT_CSD_PWR_CL_26_195 201 /* RO */
#define EXT_CSD_PWR_CL_52_195 200 /* RO */
#define EXT_CSD_POWER_CLASS 187 /* R/W */
#define EXT_CSD_CMD_SET 191 /* R/W */
#define EXT_CSD_S_CMD_SET 504 /* RO */
/* EXT_CSD field definitions */
#define EXT_CSD_CMD_SET_NORMAL (1U << 0)
#define EXT_CSD_CMD_SET_SECURE (1U << 1)
#define EXT_CSD_CMD_SET_CPSECURE (1U << 2)
/* EXT_CSD_HS_TIMING */
#define EXT_CSD_HS_TIMING_BC 0
#define EXT_CSD_HS_TIMING_HS 1
#define EXT_CSD_HS_TIMING_HS200 2
#define EXT_CSD_HS_TIMING_HS400 3
/* EXT_CSD_BUS_WIDTH */
#define EXT_CSD_BUS_WIDTH_1 0
#define EXT_CSD_BUS_WIDTH_4 1
#define EXT_CSD_BUS_WIDTH_8 2
#define EXT_CSD_BUS_WIDTH_4_DDR 5
#define EXT_CSD_BUS_WIDTH_8_DDR 6
/* EXT_CSD_CARD_TYPE */
/* The only currently valid values for this field are 0x01, 0x03, 0x07,
* 0x0B and 0x0F. */
#define EXT_CSD_CARD_TYPE_F_26M (1 << 0) /* SDR at "rated voltages */
#define EXT_CSD_CARD_TYPE_F_52M (1 << 1) /* SDR at "rated voltages */
#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2) /* DDR, 1.8V or 3.3V I/O */
#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3) /* DDR, 1.2V I/O */
#define EXT_CSD_CARD_TYPE_26M 0x01
#define EXT_CSD_CARD_TYPE_52M 0x03
#define EXT_CSD_CARD_TYPE_52M_V18 0x07
#define EXT_CSD_CARD_TYPE_52M_V12 0x0b
#define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f
/* EXT_CSD MMC */
#define EXT_CSD_MMC_SIZE 512
/* MMC_SWITCH access mode */
#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */
#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits in value */
#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits in value */
#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
/* MMC R2 response (CSD) */
#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
#define MMC_CSD_CSDVER_1_0 1
#define MMC_CSD_CSDVER_2_0 2
#define MMC_CSD_CSDVER_EXT_CSD 3
#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4)
#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */
#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */
#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */
#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */
#define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */
#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \
(MMC_CSD_C_SIZE_MULT((resp))+2))
#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
/* MMC v1 R2 response (CID) */
#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24)
#define MMC_CID_PNM_V1_CPY(resp, pnm) \
do { \
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
(pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
(pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \
(pnm)[7] = '\0'; \
} while (0)
#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8)
#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24)
#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8)
/* MMC v2 R2 response (CID) */
#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8)
#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16)
#define MMC_CID_PNM_V2_CPY(resp, pnm) \
do { \
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
(pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
(pnm)[6] = '\0'; \
} while (0)
#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32)
/* SD R2 response (CSD) */
#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
#define SD_CSD_CSDVER_1_0 0
#define SD_CSD_CSDVER_2_0 1
#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8)
#define SD_CSD_TAAC_1_5_MSEC 0x26
#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8)
#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8)
#define SD_CSD_SPEED_25_MHZ 0x32
#define SD_CSD_SPEED_50_MHZ 0x5a
#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12)
#define SD_CSD_CCC_BASIC (1 << 0) /* basic */
#define SD_CSD_CCC_BR (1 << 2) /* block read */
#define SD_CSD_CCC_BW (1 << 4) /* block write */
#define SD_CSD_CCC_ERASE (1 << 5) /* erase */
#define SD_CSD_CCC_WP (1 << 6) /* write protection */
#define SD_CSD_CCC_LC (1 << 7) /* lock card */
#define SD_CSD_CCC_AS (1 << 8) /*application specific*/
#define SD_CSD_CCC_IOM (1 << 9) /* I/O mode */
#define SD_CSD_CCC_SWITCH (1 << 10) /* switch */
#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1)
#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1)
#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1)
#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1)
#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \
(SD_CSD_C_SIZE_MULT((resp))+2))
#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22)
#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10)
#define SD_CSD_V2_BL_LEN 0x9 /* 512 */
#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3)
#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3)
#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3)
#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3)
#define SD_CSD_VDD_RW_CURR_100mA 0x7
#define SD_CSD_VDD_RW_CURR_80mA 0x6
#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1)
#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */
#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */
#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1)
#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3)
#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4)
#define SD_CSD_RW_BL_LEN_2G 0xa
#define SD_CSD_RW_BL_LEN_1G 0x9
#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1)
#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1)
#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1)
#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1)
#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1)
#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2)
/* SD R2 response (CID) */
#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8)
#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16)
#define SD_CID_PNM_CPY(resp, pnm) \
do { \
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
(pnm)[5] = '\0'; \
} while (0)
#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8)
#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32)
#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12)
/* SCR (SD Configuration Register) */
#define SCR_STRUCTURE(scr) MMC_RSP_BITS((scr), 60, 4)
#define SCR_STRUCTURE_VER_1_0 0 /* Version 1.0 */
#define SCR_SD_SPEC(scr) MMC_RSP_BITS((scr), 56, 4)
#define SCR_SD_SPEC_VER_1_0 0 /* Version 1.0 and 1.01 */
#define SCR_SD_SPEC_VER_1_10 1 /* Version 1.10 */
#define SCR_SD_SPEC_VER_2 2 /* Version 2.00 or Version 3.0X */
#define SCR_DATA_STAT_AFTER_ERASE(scr) MMC_RSP_BITS((scr), 55, 1)
#define SCR_SD_SECURITY(scr) MMC_RSP_BITS((scr), 52, 3)
#define SCR_SD_SECURITY_NONE 0 /* no security */
#define SCR_SD_SECURITY_1_0 1 /* security protocol 1.0 */
#define SCR_SD_SECURITY_1_0_2 2 /* security protocol 1.0 */
#define SCR_SD_BUS_WIDTHS(scr) MMC_RSP_BITS((scr), 48, 4)
#define SCR_SD_BUS_WIDTHS_1BIT (1 << 0) /* 1bit (DAT0) */
#define SCR_SD_BUS_WIDTHS_4BIT (1 << 2) /* 4bit (DAT0-3) */
#define SCR_SD_SPEC3(scr) MMC_RSP_BITS((scr), 47, 1)
#define SCR_EX_SECURITY(scr) MMC_RSP_BITS((scr), 43, 4)
#define SCR_SD_SPEC4(scr) MMC_RSP_BITS((scr), 42, 1)
#define SCR_RESERVED(scr) MMC_RSP_BITS((scr), 34, 8)
#define SCR_CMD_SUPPORT_CMD23(scr) MMC_RSP_BITS((scr), 33, 1)
#define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1)
#define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32)
/* Max supply current in SWITCH_FUNC response (in mA) */
#define SD_SFUNC_I_MAX(status) (MMC_RSP_BITS((uint32_t *)(status), 496, 16))
/* Supported flags in SWITCH_FUNC response */
#define SD_SFUNC_SUPPORTED(status, group) \
(MMC_RSP_BITS((uint32_t *)(status), 400 + (group - 1) * 16, 16))
/* Selected function in SWITCH_FUNC response */
#define SD_SFUNC_SELECTED(status, group) \
(MMC_RSP_BITS((uint32_t *)(status), 376 + (group - 1) * 4, 4))
/* Busy flags in SWITCH_FUNC response */
#define SD_SFUNC_BUSY(status, group) \
(MMC_RSP_BITS((uint32_t *)(status), 272 + (group - 1) * 16, 16))
/* Version of SWITCH_FUNC response */
#define SD_SFUNC_VER(status) (MMC_RSP_BITS((uint32_t *)(status), 368, 8))
#define SD_SFUNC_GROUP_MAX 6
#define SD_SFUNC_FUNC_MAX 15
#define SD_ACCESS_MODE 1 /* Function group 1, Access Mode */
#define SD_ACCESS_MODE_SDR12 0 /* 25 MHz clock */
#define SD_ACCESS_MODE_SDR25 1 /* 50 MHz clock */
#define SD_ACCESS_MODE_SDR50 2 /* UHS-I, 100 MHz clock */
#define SD_ACCESS_MODE_SDR104 3 /* UHS-I, 208 MHz clock */
#define SD_ACCESS_MODE_DDR50 4 /* UHS-I, 50 MHz clock, DDR */
/**
* @brief Extract up to 32 sequential bits from an array of 32-bit words
*
* Bits within the word are numbered in the increasing order from LSB to MSB.
*
* As an example, consider 2 32-bit words:
*
* 0x01234567 0x89abcdef
*
* On a little-endian system, the bytes are stored in memory as follows:
*
* 67 45 23 01 ef cd ab 89
*
* MMC_RSP_BITS will extact bits as follows:
*
* start=0 len=4 -> result=0x00000007
* start=0 len=12 -> result=0x00000567
* start=28 len=8 -> result=0x000000f0
* start=59 len=5 -> result=0x00000011
*
* @param src array of words to extract bits from
* @param start index of the first bit to extract
* @param len number of bits to extract, 1 to 32
* @return 32-bit word where requested bits start from LSB
*/
static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len)
{
uint32_t mask = (len % 32 == 0) ? UINT_MAX : UINT_MAX >> (32 - (len % 32));
size_t word = start / 32;
size_t shift = start % 32;
uint32_t right = src[word] >> shift;
uint32_t left = (len + shift <= 32) ? 0 : src[word + 1] << ((32 - shift) % 32);
return (left | right) & mask;
}
/* SD R4 response (IO OCR) */
#define SD_IO_OCR_MEM_READY (1<<31)
#define SD_IO_OCR_NUM_FUNCTIONS(ocr) (((ocr) >> 28) & 0x7)
#define SD_IO_OCR_MEM_PRESENT (1<<27)
#define SD_IO_OCR_MASK 0x00fffff0
/* CMD52 arguments */
#define SD_ARG_CMD52_READ (0<<31)
#define SD_ARG_CMD52_WRITE (1<<31)
#define SD_ARG_CMD52_FUNC_SHIFT 28
#define SD_ARG_CMD52_FUNC_MASK 0x7
#define SD_ARG_CMD52_EXCHANGE (1<<27)
#define SD_ARG_CMD52_REG_SHIFT 9
#define SD_ARG_CMD52_REG_MASK 0x1ffff
#define SD_ARG_CMD52_DATA_SHIFT 0
#define SD_ARG_CMD52_DATA_MASK 0xff
#define SD_R5_DATA(resp) ((resp)[0] & 0xff)
/* CMD53 arguments */
#define SD_ARG_CMD53_READ (0<<31)
#define SD_ARG_CMD53_WRITE (1<<31)
#define SD_ARG_CMD53_FUNC_SHIFT 28
#define SD_ARG_CMD53_FUNC_MASK 0x7
#define SD_ARG_CMD53_BLOCK_MODE (1<<27)
#define SD_ARG_CMD53_INCREMENT (1<<26)
#define SD_ARG_CMD53_REG_SHIFT 9
#define SD_ARG_CMD53_REG_MASK 0x1ffff
#define SD_ARG_CMD53_LENGTH_SHIFT 0
#define SD_ARG_CMD53_LENGTH_MASK 0x1ff
#define SD_ARG_CMD53_LENGTH_MAX 512
/* Card Common Control Registers (CCCR) */
#define SD_IO_CCCR_START 0x00000
#define SD_IO_CCCR_SIZE 0x100
#define SD_IO_CCCR_FN_ENABLE 0x02
#define SD_IO_CCCR_FN_READY 0x03
#define SD_IO_CCCR_INT_ENABLE 0x04
#define SD_IO_CCCR_INT_PENDING 0x05
#define SD_IO_CCCR_CTL 0x06
#define CCCR_CTL_RES (1<<3)
#define SD_IO_CCCR_BUS_WIDTH 0x07
#define CCCR_BUS_WIDTH_1 (0<<0)
#define CCCR_BUS_WIDTH_4 (2<<0)
#define CCCR_BUS_WIDTH_8 (3<<0)
#define CCCR_BUS_WIDTH_ECSI (1<<5)
#define SD_IO_CCCR_CARD_CAP 0x08
#define CCCR_CARD_CAP_LSC BIT(6)
#define CCCR_CARD_CAP_4BLS BIT(7)
#define SD_IO_CCCR_CISPTR 0x09
#define SD_IO_CCCR_BLKSIZEL 0x10
#define SD_IO_CCCR_BLKSIZEH 0x11
#define SD_IO_CCCR_HIGHSPEED 0x13
#define CCCR_HIGHSPEED_SUPPORT BIT(0)
#define CCCR_HIGHSPEED_ENABLE BIT(1)
/* Function Basic Registers (FBR) */
#define SD_IO_FBR_START 0x00100
#define SD_IO_FBR_SIZE 0x00700
/* Card Information Structure (CIS) */
#define SD_IO_CIS_START 0x01000
#define SD_IO_CIS_SIZE 0x17000
/* CIS tuple codes (based on PC Card 16) */
#define CISTPL_CODE_NULL 0x00
#define CISTPL_CODE_DEVICE 0x01
#define CISTPL_CODE_CHKSUM 0x10
#define CISTPL_CODE_VERS1 0x15
#define CISTPL_CODE_ALTSTR 0x16
#define CISTPL_CODE_CONFIG 0x1A
#define CISTPL_CODE_CFTABLE_ENTRY 0x1B
#define CISTPL_CODE_MANFID 0x20
#define CISTPL_CODE_FUNCID 0x21
#define TPLFID_FUNCTION_SDIO 0x0c
#define CISTPL_CODE_FUNCE 0x22
#define CISTPL_CODE_VENDER_BEGIN 0x80
#define CISTPL_CODE_VENDER_END 0x8F
#define CISTPL_CODE_SDIO_STD 0x91
#define CISTPL_CODE_SDIO_EXT 0x92
#define CISTPL_CODE_END 0xFF
/* Timing */
#define SDMMC_TIMING_LEGACY 0
#define SDMMC_TIMING_HIGHSPEED 1
#define SDMMC_TIMING_MMC_DDR52 2
#include "py/runtime.h"
// Logging macros
// #define ESP_LOGD(tag, string, ...) mp_printf(&mp_plat_print, string "\n" __VA_OPT__(,) __VA_ARGS__)
// #define ESP_LOGV(tag, string, ...) mp_printf(&mp_plat_print, string "\n" __VA_OPT__(,) __VA_ARGS__)
// #define ESP_LOGW(tag, string, ...) mp_printf(&mp_plat_print, string "\n" __VA_OPT__(,) __VA_ARGS__)
// #define ESP_LOGE(tag, string, ...) mp_printf(&mp_plat_print, string "\n" __VA_OPT__(,) __VA_ARGS__)
#define ESP_LOGD(tag, string, ...)
#define ESP_LOGV(tag, string, ...)
#define ESP_LOGW(tag, string, ...)
#define ESP_LOGE(tag, string, ...)
#define TSD_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) )
#endif //_SDMMC_DEFS_H_

View File

@ -0,0 +1,199 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SDMMC_TYPES_H_
#define _SDMMC_TYPES_H_
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
/**
* Decoded values from SD card Card Specific Data register
*/
typedef struct {
int csd_ver; /*!< CSD structure format */
int mmc_ver; /*!< MMC version (for CID format) */
int capacity; /*!< total number of sectors */
int sector_size; /*!< sector size in bytes */
int read_block_len; /*!< block length for reads */
int card_command_class; /*!< Card Command Class for SD */
int tr_speed; /*!< Max transfer speed */
} sdmmc_csd_t;
/**
* Decoded values from SD card Card IDentification register
*/
typedef struct {
int mfg_id; /*!< manufacturer identification number */
int oem_id; /*!< OEM/product identification number */
char name[8]; /*!< product name (MMC v1 has the longest) */
int revision; /*!< product revision */
int serial; /*!< product serial number */
int date; /*!< manufacturing date */
} sdmmc_cid_t;
/**
* Decoded values from SD Configuration Register
*/
typedef struct {
int sd_spec; /*!< SD Physical layer specification version, reported by card */
int bus_width; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */
} sdmmc_scr_t;
/**
* Decoded values of Extended Card Specific Data
*/
typedef struct {
uint8_t power_class; /*!< Power class used by the card */
} sdmmc_ext_csd_t;
/**
* SD/MMC command response buffer
*/
typedef uint32_t sdmmc_response_t[4];
/**
* SD SWITCH_FUNC response buffer
*/
typedef struct {
uint32_t data[512 / 8 / sizeof(uint32_t)]; /*!< response data */
} sdmmc_switch_func_rsp_t;
typedef enum {
SDMMC_OK = 0,
SDMMC_ERR_NOT_SUPPORTED = 1,
SDMMC_ERR_INVALID_RESPONSE = 2,
SDMMC_ERR_TIMEOUT = 3,
SDMMC_ERR_NO_MEM = 4,
SDMMC_ERR_INVALID_SIZE = 5,
SDMMC_ERR_NO_CARD = 6,
SDMMC_ERR_INVALID_ARG = 7,
SDMMC_ERR_BUSY = 8,
} sdmmc_err_t;
/**
* SD/MMC command information
*/
typedef struct {
uint32_t opcode; /*!< SD or MMC command index */
uint32_t arg; /*!< SD/MMC command argument */
sdmmc_response_t response; /*!< response buffer */
void* data; /*!< buffer to send or read into */
size_t datalen; /*!< length of data buffer */
size_t blklen; /*!< block length */
int flags; /*!< see below */
/** @cond */
#define SCF_ITSDONE 0x0001 /*!< command is complete */
#define SCF_CMD(flags) ((flags) & 0x00f0)
#define SCF_CMD_AC 0x0000
#define SCF_CMD_ADTC 0x0010
#define SCF_CMD_BC 0x0020
#define SCF_CMD_BCR 0x0030
#define SCF_CMD_READ 0x0040 /*!< read command (data expected) */
#define SCF_RSP_BSY 0x0100
#define SCF_RSP_136 0x0200
#define SCF_RSP_CRC 0x0400
#define SCF_RSP_IDX 0x0800
#define SCF_RSP_PRESENT 0x1000
/* response types */
#define SCF_RSP_R0 0 /*!< none */
#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
#define SCF_RSP_R1B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136)
#define SCF_RSP_R3 (SCF_RSP_PRESENT)
#define SCF_RSP_R4 (SCF_RSP_PRESENT)
#define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
/* special flags */
#define SCF_WAIT_BUSY 0x2000 /*!< Wait for completion of card busy signal before returning */
#define SCF_AUTO_STOP 0x4000 /*!< Auto stop with command 12 */
/** @endcond */
sdmmc_err_t error; /*!< error returned from transfer */
int timeout_ms; /*!< response timeout, in milliseconds */
} sdmmc_command_t;
/**
* SD/MMC Host description
*
* This structure defines properties of SD/MMC host and functions
* of SD/MMC host which can be used by upper layers.
*/
#define BIT(x) (1 << x)
typedef struct {
uint32_t flags; /*!< flags defining host properties */
#define SDMMC_HOST_FLAG_1BIT BIT(0) /*!< host supports 1-line SD and MMC protocol */
#define SDMMC_HOST_FLAG_4BIT BIT(1) /*!< host supports 4-line SD and MMC protocol */
#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */
#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */
#define SDMMC_HOST_FLAG_DDR BIT(4) /*!< host supports DDR mode for SD/MMC */
#define SDMMC_HOST_FLAG_DEINIT_ARG BIT(5) /*!< host `deinit` function called with the slot argument */
int slot; /*!< slot number, to be passed to host functions */
int max_freq_khz; /*!< max frequency supported by the host */
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
#define SDMMC_FREQ_HIGHSPEED 40000 /*!< SD High speed (limited by clock divider) */
#define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */
#define SDMMC_FREQ_52M 52000 /*!< MMC 52MHz speed */
#define SDMMC_FREQ_26M 26000 /*!< MMC 26MHz speed */
float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */
sdmmc_err_t (*init)(void); /*!< Host function to initialize the driver */
sdmmc_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */
size_t (*get_bus_width)(int slot); /*!< host function to get bus width */
sdmmc_err_t (*set_bus_ddr_mode)(int slot, bool ddr_enable); /*!< host function to set DDR mode */
sdmmc_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */
sdmmc_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */
union {
sdmmc_err_t (*deinit)(void); /*!< host function to deinitialize the driver */
sdmmc_err_t (*deinit_p)(int slot); /*!< host function to deinitialize the driver, called with the `slot` */
};
sdmmc_err_t (*io_int_enable)(int slot); /*!< Host function to enable SDIO interrupt line */
sdmmc_err_t (*io_int_wait)(int slot, int timeout_ms); /*!< Host function to wait for SDIO interrupt line to be active */
int command_timeout_ms; /*!< timeout, in milliseconds, of a single command. Set to 0 to use the default value. */
} sdmmc_host_t;
/**
* SD/MMC card information structure
*/
typedef struct {
sdmmc_host_t host; /*!< Host with which the card is associated */
uint32_t ocr; /*!< OCR (Operation Conditions Register) value */
union {
sdmmc_cid_t cid; /*!< decoded CID (Card IDentification) register value */
sdmmc_response_t raw_cid; /*!< raw CID of MMC card to be decoded
after the CSD is fetched in the data transfer mode*/
};
sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */
uint16_t rca; /*!< RCA (Relative Card Address) */
uint16_t max_freq_khz; /*!< Maximum frequency, in kHz, supported by the card */
uint32_t is_mem : 1; /*!< Bit indicates if the card is a memory card */
uint32_t is_sdio : 1; /*!< Bit indicates if the card is an IO card */
uint32_t is_mmc : 1; /*!< Bit indicates if the card is MMC */
uint32_t num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */
uint32_t log_bus_width : 2; /*!< log2(bus width supported by card) */
uint32_t is_ddr : 1; /*!< Card supports DDR mode */
uint32_t reserved : 23; /*!< Reserved for future expansion */
} sdmmc_card_t;
// OS abstraction layer (copied from TinyUSB)
void osal_task_delay(uint32_t msec);
#endif // _SDMMC_TYPES_H_

501
lib/sdmmc/sdmmc_cmd.c Normal file
View File

@ -0,0 +1,501 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include "sdmmc_common.h"
sdmmc_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
{
if (card->host.command_timeout_ms != 0) {
cmd->timeout_ms = card->host.command_timeout_ms;
} else if (cmd->timeout_ms == 0) {
cmd->timeout_ms = SDMMC_DEFAULT_CMD_TIMEOUT_MS;
}
int slot = card->host.slot;
ESP_LOGV(TAG, "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d timeout=%d",
slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen, cmd->timeout_ms);
sdmmc_err_t err = (*card->host.do_transaction)(slot, cmd);
if (err != 0) {
ESP_LOGD(TAG, "cmd=%d, sdmmc_req_run returned 0x%x", cmd->opcode, err);
return err;
}
ESP_LOGV(TAG, "cmd response %08x %08x %08x %08x err=0x%x state=%d",
cmd->response[0],
cmd->response[1],
cmd->response[2],
cmd->response[3],
cmd->error,
MMC_R1_CURRENT_STATE(cmd->response));
return cmd->error;
}
sdmmc_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
{
sdmmc_command_t app_cmd = {
.opcode = MMC_APP_CMD,
.flags = SCF_CMD_AC | SCF_RSP_R1,
.arg = MMC_ARG_RCA(card->rca),
};
sdmmc_err_t err = sdmmc_send_cmd(card, &app_cmd);
if (err != SDMMC_OK) {
return err;
}
// Check APP_CMD status bit (only in SD mode)
if (!host_is_spi(card) && !(MMC_R1(app_cmd.response) & MMC_R1_APP_CMD)) {
ESP_LOGW(TAG, "card doesn't support APP_CMD");
return SDMMC_ERR_NOT_SUPPORTED;
}
return sdmmc_send_cmd(card, cmd);
}
sdmmc_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card)
{
sdmmc_command_t cmd = {
.opcode = MMC_GO_IDLE_STATE,
.flags = SCF_CMD_BC | SCF_RSP_R0,
};
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (host_is_spi(card)) {
/* To enter SPI mode, CMD0 needs to be sent twice (see figure 4-1 in
* SD Simplified spec v4.10). Some cards enter SD mode on first CMD0,
* so don't expect the above command to succeed.
* SCF_RSP_R1 flag below tells the lower layer to expect correct R1
* response (in SPI mode).
*/
(void) err;
osal_task_delay(SDMMC_GO_IDLE_DELAY_MS);
cmd.flags |= SCF_RSP_R1;
err = sdmmc_send_cmd(card, &cmd);
}
if (err == SDMMC_OK) {
osal_task_delay(SDMMC_GO_IDLE_DELAY_MS);
}
return err;
}
sdmmc_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr)
{
const uint8_t pattern = 0xaa; /* any pattern will do here */
sdmmc_command_t cmd = {
.opcode = SD_SEND_IF_COND,
.arg = (((ocr & SD_OCR_VOL_MASK) != 0) << 8) | pattern,
.flags = SCF_CMD_BCR | SCF_RSP_R7,
};
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
return err;
}
uint8_t response = cmd.response[0] & 0xff;
if (response != pattern) {
ESP_LOGD(TAG, "%s: received=0x%x expected=0x%x", __func__, response, pattern);
return SDMMC_ERR_INVALID_RESPONSE;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
{
sdmmc_err_t err;
sdmmc_command_t cmd = {
.arg = ocr,
.flags = SCF_CMD_BCR | SCF_RSP_R3,
.opcode = SD_APP_OP_COND
};
int nretries = SDMMC_SEND_OP_COND_MAX_RETRIES;
int err_cnt = SDMMC_SEND_OP_COND_MAX_ERRORS;
for (; nretries != 0; --nretries) {
bzero(&cmd, sizeof cmd);
cmd.arg = ocr;
cmd.flags = SCF_CMD_BCR | SCF_RSP_R3;
if (!card->is_mmc) { /* SD mode */
cmd.opcode = SD_APP_OP_COND;
err = sdmmc_send_app_cmd(card, &cmd);
} else { /* MMC mode */
cmd.arg &= ~MMC_OCR_ACCESS_MODE_MASK;
cmd.arg |= MMC_OCR_SECTOR_MODE;
cmd.opcode = MMC_SEND_OP_COND;
err = sdmmc_send_cmd(card, &cmd);
}
if (err != SDMMC_OK) {
if (--err_cnt == 0) {
ESP_LOGD(TAG, "%s: sdmmc_send_app_cmd err=0x%x", __func__, err);
return err;
} else {
ESP_LOGV(TAG, "%s: ignoring err=0x%x", __func__, err);
continue;
}
}
// In SD protocol, card sets MEM_READY bit in OCR when it is ready.
// In SPI protocol, card clears IDLE_STATE bit in R1 response.
if (!host_is_spi(card)) {
if ((MMC_R3(cmd.response) & MMC_OCR_MEM_READY) ||
ocr == 0) {
break;
}
} else {
if ((SD_SPI_R1(cmd.response) & SD_SPI_R1_IDLE_STATE) == 0) {
break;
}
}
osal_task_delay(10);
}
if (nretries == 0) {
return SDMMC_ERR_TIMEOUT;
}
if (ocrp) {
*ocrp = MMC_R3(cmd.response);
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp)
{
assert(ocrp);
sdmmc_command_t cmd = {
.opcode = SD_READ_OCR,
.flags = SCF_CMD_BCR | SCF_RSP_R2
};
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
return err;
}
*ocrp = SD_SPI_R3(cmd.response);
return SDMMC_OK;
}
sdmmc_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_response_t* out_raw_cid)
{
assert(out_raw_cid);
sdmmc_command_t cmd = {
.opcode = MMC_ALL_SEND_CID,
.flags = SCF_CMD_BCR | SCF_RSP_R2
};
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
return err;
}
memcpy(out_raw_cid, &cmd.response, sizeof(sdmmc_response_t));
return SDMMC_OK;
}
sdmmc_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid)
{
assert(out_cid);
assert(host_is_spi(card) && "SEND_CID should only be used in SPI mode");
assert(!card->is_mmc && "MMC cards are not supported in SPI mode");
sdmmc_response_t buf;
sdmmc_command_t cmd = {
.opcode = MMC_SEND_CID,
.flags = SCF_CMD_READ | SCF_CMD_ADTC,
.arg = 0,
.data = &buf[0],
.datalen = sizeof(buf)
};
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
return err;
}
sdmmc_flip_byte_order(buf, sizeof(buf));
return sdmmc_decode_cid(buf, out_cid);
}
sdmmc_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca)
{
assert(out_rca);
sdmmc_command_t cmd = {
.opcode = SD_SEND_RELATIVE_ADDR,
.flags = SCF_CMD_BCR | SCF_RSP_R6
};
/* MMC cards expect us to set the RCA.
* Set RCA to 1 since we don't support multiple cards on the same bus, for now.
*/
uint16_t mmc_rca = 1;
if (card->is_mmc) {
cmd.arg = MMC_ARG_RCA(mmc_rca);
}
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
return err;
}
*out_rca = (card->is_mmc) ? mmc_rca : SD_R6_RCA(cmd.response);
return SDMMC_OK;
}
sdmmc_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd)
{
sdmmc_command_t cmd = {
.opcode = MMC_SET_BLOCKLEN,
.arg = csd->sector_size,
.flags = SCF_CMD_AC | SCF_RSP_R1
};
return sdmmc_send_cmd(card, &cmd);
}
sdmmc_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd)
{
/* The trick with SEND_CSD is that in SPI mode, it acts as a data read
* command, while in SD mode it is an AC command with R2 response.
*/
sdmmc_response_t spi_buf;
const bool is_spi = host_is_spi(card);
sdmmc_command_t cmd = {
.opcode = MMC_SEND_CSD,
.arg = is_spi ? 0 : MMC_ARG_RCA(card->rca),
.flags = is_spi ? (SCF_CMD_READ | SCF_CMD_ADTC | SCF_RSP_R1) :
(SCF_CMD_AC | SCF_RSP_R2),
.data = is_spi ? &spi_buf[0] : 0,
.datalen = is_spi ? sizeof(spi_buf) : 0,
};
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
return err;
}
uint32_t* ptr = cmd.response;
if (is_spi) {
sdmmc_flip_byte_order(spi_buf, sizeof(spi_buf));
ptr = spi_buf;
}
if (card->is_mmc) {
err = sdmmc_mmc_decode_csd(cmd.response, out_csd);
} else {
err = sdmmc_decode_csd(ptr, out_csd);
}
return err;
}
sdmmc_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca)
{
/* Don't expect to see a response when de-selecting a card */
uint32_t response = (rca == 0) ? 0 : SCF_RSP_R1;
sdmmc_command_t cmd = {
.opcode = MMC_SELECT_CARD,
.arg = MMC_ARG_RCA(rca),
.flags = SCF_CMD_AC | response
};
return sdmmc_send_cmd(card, &cmd);
}
sdmmc_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr)
{
size_t datalen = 8;
uint32_t buf[datalen];
sdmmc_command_t cmd = {
.data = buf,
.datalen = datalen,
.blklen = datalen,
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
.opcode = SD_APP_SEND_SCR
};
sdmmc_err_t err = sdmmc_send_app_cmd(card, &cmd);
if (err == SDMMC_OK) {
err = sdmmc_decode_scr(buf, out_scr);
}
return err;
}
sdmmc_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width)
{
sdmmc_command_t cmd = {
.opcode = SD_APP_SET_BUS_WIDTH,
.flags = SCF_RSP_R1 | SCF_CMD_AC,
.arg = (width == 4) ? SD_ARG_BUS_WIDTH_4 : SD_ARG_BUS_WIDTH_1,
};
return sdmmc_send_app_cmd(card, &cmd);
}
sdmmc_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable)
{
assert(host_is_spi(card) && "CRC_ON_OFF can only be used in SPI mode");
sdmmc_command_t cmd = {
.opcode = SD_CRC_ON_OFF,
.arg = crc_enable ? 1 : 0,
.flags = SCF_CMD_AC | SCF_RSP_R1
};
return sdmmc_send_cmd(card, &cmd);
}
sdmmc_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status)
{
sdmmc_command_t cmd = {
.opcode = MMC_SEND_STATUS,
.arg = MMC_ARG_RCA(card->rca),
.flags = SCF_CMD_AC | SCF_RSP_R1
};
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
return err;
}
if (out_status) {
*out_status = MMC_R1(cmd.response);
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count)
{
sdmmc_err_t err = SDMMC_OK;
size_t block_size = card->csd.sector_size;
if ((intptr_t)src % 4 == 0) {
err = sdmmc_write_sectors_dma(card, src, start_block, block_count);
} else {
// SDMMC peripheral needs DMA-capable buffers. Split the write into
// separate single block writes, if needed, and allocate a temporary
// DMA-capable buffer.
uint32_t tmp_buf[block_size / sizeof(uint32_t)];
const uint8_t* cur_src = (const uint8_t*) src;
for (size_t i = 0; i < block_count; ++i) {
memcpy(tmp_buf, cur_src, block_size);
cur_src += block_size;
err = sdmmc_write_sectors_dma(card, tmp_buf, start_block + i, 1);
if (err != SDMMC_OK) {
ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
__func__, err, start_block, i);
break;
}
}
}
return err;
}
sdmmc_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count)
{
if (start_block + block_count > (size_t) card->csd.capacity) {
return SDMMC_ERR_INVALID_SIZE;
}
size_t block_size = card->csd.sector_size;
sdmmc_command_t cmd = {
.flags = SCF_CMD_ADTC | SCF_RSP_R1,
.blklen = block_size,
.data = (void*) src,
.datalen = block_count * block_size,
.timeout_ms = SDMMC_WRITE_CMD_TIMEOUT_MS
};
if (block_count == 1) {
cmd.opcode = MMC_WRITE_BLOCK_SINGLE;
} else {
cmd.opcode = MMC_WRITE_BLOCK_MULTIPLE;
cmd.flags |= SCF_AUTO_STOP;
}
if (card->ocr & SD_OCR_SDHC_CAP) {
cmd.arg = start_block;
} else {
cmd.arg = start_block * block_size;
}
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
}
uint32_t status = 0;
size_t count = 0;
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
err = sdmmc_send_cmd_send_status(card, &status);
if (err != SDMMC_OK) {
return err;
}
if (++count % 10 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
}
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count)
{
sdmmc_err_t err = SDMMC_OK;
size_t block_size = card->csd.sector_size;
if ((intptr_t)dst % 4 == 0) {
err = sdmmc_read_sectors_dma(card, dst, start_block, block_count);
} else {
// SDMMC peripheral needs DMA-capable buffers. Split the read into
// separate single block reads, if needed, and allocate a temporary
// DMA-capable buffer.
uint32_t tmp_buf[block_size / sizeof(uint32_t)];
uint8_t* cur_dst = (uint8_t*) dst;
for (size_t i = 0; i < block_count; ++i) {
err = sdmmc_read_sectors_dma(card, tmp_buf, start_block + i, 1);
if (err != SDMMC_OK) {
ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
__func__, err, start_block, i);
break;
}
memcpy(cur_dst, tmp_buf, block_size);
cur_dst += block_size;
}
}
return err;
}
sdmmc_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count)
{
if (start_block + block_count > (size_t) card->csd.capacity) {
return SDMMC_ERR_INVALID_SIZE;
}
size_t block_size = card->csd.sector_size;
sdmmc_command_t cmd = {
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
.blklen = block_size,
.data = (void*) dst,
.datalen = block_count * block_size
};
if (block_count == 1) {
cmd.opcode = MMC_READ_BLOCK_SINGLE;
} else {
cmd.opcode = MMC_READ_BLOCK_MULTIPLE;
cmd.flags |= SCF_AUTO_STOP;
}
if (card->ocr & SD_OCR_SDHC_CAP) {
cmd.arg = start_block;
} else {
cmd.arg = start_block * block_size;
}
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
}
uint32_t status = 0;
size_t count = 0;
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
err = sdmmc_send_cmd_send_status(card, &status);
if (err != SDMMC_OK) {
return err;
}
if (++count % 10 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
}
}
return SDMMC_OK;
}

314
lib/sdmmc/sdmmc_common.c Normal file
View File

@ -0,0 +1,314 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include "sdmmc_common.h"
sdmmc_err_t sdmmc_init_ocr(sdmmc_card_t* card)
{
sdmmc_err_t err;
/* In SPI mode, READ_OCR (CMD58) command is used to figure out which voltage
* ranges the card can support. This step is skipped since 1.8V isn't
* supported on the ESP32.
*/
uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
if ((card->ocr & SD_OCR_SDHC_CAP) != 0) {
host_ocr |= SD_OCR_SDHC_CAP;
}
/* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
/* If time-out, re-try send_op_cond as MMC */
if (err == SDMMC_ERR_TIMEOUT && !host_is_spi(card)) {
ESP_LOGD(TAG, "send_op_cond timeout, trying MMC");
card->is_mmc = 1;
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
}
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
if (host_is_spi(card)) {
err = sdmmc_send_cmd_read_ocr(card, &card->ocr);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: read_ocr returned 0x%x", __func__, err);
return err;
}
}
ESP_LOGD(TAG, "host_ocr=0x%x card_ocr=0x%x", host_ocr, card->ocr);
/* Clear all voltage bits in host's OCR which the card doesn't support.
* Don't touch CCS bit because in SPI mode cards don't report CCS in ACMD41
* response.
*/
host_ocr &= (card->ocr | (~SD_OCR_VOL_MASK));
ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr);
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_cid(sdmmc_card_t* card)
{
sdmmc_err_t err;
sdmmc_response_t raw_cid;
if (!host_is_spi(card)) {
err = sdmmc_send_cmd_all_send_cid(card, &raw_cid);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
return err;
}
if (!card->is_mmc) {
err = sdmmc_decode_cid(raw_cid, &card->cid);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
return err;
}
} else {
/* For MMC, need to know CSD to decode CID. But CSD can only be read
* in data transfer mode, and it is not possible to read CID in data
* transfer mode. We temporiliy store the raw cid and do the
* decoding after the RCA is set and the card is in data transfer
* mode.
*/
memcpy(card->raw_cid, raw_cid, sizeof(sdmmc_response_t));
}
} else {
err = sdmmc_send_cmd_send_cid(card, &card->cid);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: send_cid returned 0x%x", __func__, err);
return err;
}
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_rca(sdmmc_card_t* card)
{
sdmmc_err_t err;
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
return err;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_mmc_decode_cid(sdmmc_card_t* card)
{
sdmmc_err_t err;
sdmmc_response_t raw_cid;
memcpy(raw_cid, card->raw_cid, sizeof(raw_cid));
err = sdmmc_mmc_decode_cid(card->csd.mmc_ver, raw_cid, &card->cid);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
return err;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_csd(sdmmc_card_t* card)
{
assert(card->is_mem == 1);
/* Get and decode the contents of CSD register. Determine card capacity. */
sdmmc_err_t err = sdmmc_send_cmd_send_csd(card, &card->csd);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
return err;
}
const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1;
if (!(card->ocr & SD_OCR_SDHC_CAP) &&
(size_t) card->csd.capacity > max_sdsc_capacity) {
ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.",
__func__, card->csd.capacity, max_sdsc_capacity);
card->csd.capacity = max_sdsc_capacity;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_select_card(sdmmc_card_t* card)
{
assert(!host_is_spi(card));
sdmmc_err_t err = sdmmc_send_cmd_select_card(card, card->rca);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
return err;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card)
{
sdmmc_err_t err = SDMMC_ERR_NOT_SUPPORTED;
if (card->is_mem && !card->is_mmc) {
err = sdmmc_enable_hs_mode_and_check(card);
} else if (card->is_sdio) {
err = sdmmc_io_enable_hs_mode(card);
} else if (card->is_mmc){
err = sdmmc_mmc_enable_hs_mode(card);
}
if (err == SDMMC_ERR_NOT_SUPPORTED) {
ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
} else if (err != SDMMC_OK) {
return err;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card)
{
int bus_width = 1;
if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
(card->log_bus_width == 2)) {
bus_width = 4;
} else if ((card->host.flags & SDMMC_HOST_FLAG_8BIT) &&
(card->log_bus_width == 3)) {
bus_width = 8;
}
ESP_LOGD(TAG, "%s: using %d-bit bus", __func__, bus_width);
if (bus_width > 1) {
sdmmc_err_t err = (*card->host.set_bus_width)(card->host.slot, bus_width);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "host.set_bus_width failed (0x%x)", err);
return err;
}
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
{
assert(card->max_freq_khz <= card->host.max_freq_khz);
/* Find highest frequency in the following list,
* which is below card->max_freq_khz.
*/
const uint32_t freq_values[] = {
SDMMC_FREQ_52M,
SDMMC_FREQ_HIGHSPEED,
SDMMC_FREQ_26M,
SDMMC_FREQ_DEFAULT
//NOTE: in sdspi mode, 20MHz may not work. in that case, add 10MHz here.
};
const int n_freq_values = sizeof(freq_values) / sizeof(freq_values[0]);
uint32_t selected_freq = SDMMC_FREQ_PROBING;
for (int i = 0; i < n_freq_values; ++i) {
uint32_t freq = freq_values[i];
if (card->max_freq_khz >= freq) {
selected_freq = freq;
break;
}
}
ESP_LOGD(TAG, "%s: using %d kHz bus frequency", __func__, selected_freq);
if (selected_freq > SDMMC_FREQ_PROBING) {
sdmmc_err_t err = (*card->host.set_card_clk)(card->host.slot, selected_freq);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "failed to switch bus frequency (0x%x)", err);
return err;
}
}
if (card->is_ddr) {
if (card->host.set_bus_ddr_mode == NULL) {
ESP_LOGE(TAG, "host doesn't support DDR mode or voltage switching");
return SDMMC_ERR_NOT_SUPPORTED;
}
sdmmc_err_t err = (*card->host.set_bus_ddr_mode)(card->host.slot, true);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "failed to switch bus to DDR mode (0x%x)", err);
return err;
}
}
return SDMMC_OK;
}
void sdmmc_flip_byte_order(uint32_t* response, size_t size)
{
assert(size % (2 * sizeof(uint32_t)) == 0);
const size_t n_words = size / sizeof(uint32_t);
for (size_t i = 0; i < n_words / 2; ++i) {
uint32_t left = __builtin_bswap32(response[i]);
uint32_t right = __builtin_bswap32(response[n_words - i - 1]);
response[i] = right;
response[n_words - i - 1] = left;
}
}
void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
{
// bool print_scr = false;
// bool print_csd = false;
// const char* type;
// fprintf(stream, "Name: %s\n", card->cid.name);
// if (card->is_sdio) {
// type = "SDIO";
// print_scr = true;
// print_csd = true;
// } else if (card->is_mmc) {
// type = "MMC";
// print_csd = true;
// } else {
// type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC";
// }
// fprintf(stream, "Type: %s\n", type);
// if (card->max_freq_khz < 1000) {
// fprintf(stream, "Speed: %d kHz\n", card->max_freq_khz);
// } else {
// fprintf(stream, "Speed: %d MHz%s\n", card->max_freq_khz / 1000,
// card->is_ddr ? ", DDR" : "");
// }
// fprintf(stream, "Size: %luMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
// if (print_csd) {
// fprintf(stream, "CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n",
// card->csd.csd_ver,
// card->csd.sector_size, card->csd.capacity, card->csd.read_block_len);
// }
// if (print_scr) {
// fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width);
// }
}
sdmmc_err_t sdmmc_fix_host_flags(sdmmc_card_t* card)
{
const uint32_t width_1bit = SDMMC_HOST_FLAG_1BIT;
const uint32_t width_4bit = SDMMC_HOST_FLAG_4BIT;
const uint32_t width_8bit = SDMMC_HOST_FLAG_8BIT;
const uint32_t width_mask = width_1bit | width_4bit | width_8bit;
int slot_bit_width = card->host.get_bus_width(card->host.slot);
if (slot_bit_width == 1 &&
(card->host.flags & (width_4bit | width_8bit))) {
card->host.flags &= ~width_mask;
card->host.flags |= width_1bit;
} else if (slot_bit_width == 4 && (card->host.flags & width_8bit)) {
if ((card->host.flags & width_4bit) == 0) {
ESP_LOGW(TAG, "slot width set to 4, but host flags don't have 4 line mode enabled; using 1 line mode");
card->host.flags &= ~width_mask;
card->host.flags |= width_1bit;
} else {
card->host.flags &= ~width_mask;
card->host.flags |= width_4bit;
}
}
return SDMMC_OK;
}

132
lib/sdmmc/sdmmc_common.h Normal file
View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string.h>
#include "sdmmc_defs.h"
#include "sdmmc_types.h"
#include "sdmmc_cmd.h"
#define SDMMC_GO_IDLE_DELAY_MS 20
#define SDMMC_IO_SEND_OP_COND_DELAY_MS 10
/* These delay values are mostly useful for cases when CD pin is not used, and
* the card is removed. In this case, SDMMC peripheral may not always return
* CMD_DONE / DATA_DONE interrupts after signaling the error. These timeouts work
* as a safety net in such cases.
*/
#define SDMMC_DEFAULT_CMD_TIMEOUT_MS 1000 // Max timeout of ordinary commands
#define SDMMC_WRITE_CMD_TIMEOUT_MS 5000 // Max timeout of write commands
/* Maximum retry/error count for SEND_OP_COND (CMD1).
* These are somewhat arbitrary, values originate from OpenBSD driver.
*/
#define SDMMC_SEND_OP_COND_MAX_RETRIES 100
#define SDMMC_SEND_OP_COND_MAX_ERRORS 3
/* Functions to send individual commands */
sdmmc_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
sdmmc_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
sdmmc_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card);
sdmmc_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr);
sdmmc_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
sdmmc_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp);
sdmmc_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid);
sdmmc_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_response_t* out_raw_cid);
sdmmc_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca);
sdmmc_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd);
sdmmc_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
uint32_t mode, uint32_t group, uint32_t function,
sdmmc_switch_func_rsp_t* resp);
sdmmc_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd);
sdmmc_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca);
sdmmc_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr);
sdmmc_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
sdmmc_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
sdmmc_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
/* Higher level functions */
sdmmc_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card);
sdmmc_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card);
sdmmc_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count);
sdmmc_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count);
/* SD specific */
sdmmc_err_t sdmmc_check_scr(sdmmc_card_t* card);
sdmmc_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid);
sdmmc_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
sdmmc_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr);
/* SDIO specific */
sdmmc_err_t sdmmc_io_reset(sdmmc_card_t* card);
sdmmc_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card);
sdmmc_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
sdmmc_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int function,
uint32_t reg, uint32_t arg, uint8_t *byte);
sdmmc_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int function,
uint32_t reg, int arg, void *data, size_t size);
/* MMC specific */
sdmmc_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen);
sdmmc_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value);
sdmmc_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid);
sdmmc_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
sdmmc_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card);
/* Parts of card initialization flow */
sdmmc_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_select_card(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_csd(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_cid(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_rca(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_mmc_decode_cid(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_ocr(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_spi_crc(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_io(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_sd_blocklen(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_sd_scr(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_mmc_read_cid(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_host_frequency(sdmmc_card_t* card);
sdmmc_err_t sdmmc_init_mmc_check_csd(sdmmc_card_t* card);
/* Various helper functions */
static inline bool host_is_spi(const sdmmc_card_t* card)
{
return (card->host.flags & SDMMC_HOST_FLAG_SPI) != 0;
}
static inline uint32_t get_host_ocr(float voltage)
{
// TODO: report exact voltage to the card
// For now tell that the host has 2.8-3.6V voltage range
(void) voltage;
return SD_OCR_VOL_MASK;
}
void sdmmc_flip_byte_order(uint32_t* response, size_t size);
sdmmc_err_t sdmmc_fix_host_flags(sdmmc_card_t* card);

123
lib/sdmmc/sdmmc_init.c Normal file
View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "sdmmc_common.h"
#define SDMMC_INIT_STEP(condition, function) \
do { \
if ((condition)) { \
sdmmc_err_t err = (function)(card); \
if (err != SDMMC_OK) { \
ESP_LOGD(TAG, "%s: %s returned 0x%x", __func__, #function, err); \
return err; \
} \
} \
} while(0);
sdmmc_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
{
memset(card, 0, sizeof(*card));
memcpy(&card->host, config, sizeof(*config));
const bool is_spi = host_is_spi(card);
const bool always = true;
const bool io_supported = true;
/* Check if host flags are compatible with slot configuration. */
SDMMC_INIT_STEP(!is_spi, sdmmc_fix_host_flags);
/* Reset SDIO (CMD52, RES) before re-initializing IO (CMD5). */
SDMMC_INIT_STEP(io_supported, sdmmc_io_reset);
/* GO_IDLE_STATE (CMD0) command resets the card */
SDMMC_INIT_STEP(always, sdmmc_send_cmd_go_idle_state);
/* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards. */
SDMMC_INIT_STEP(always, sdmmc_init_sd_if_cond);
/* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card. */
SDMMC_INIT_STEP(io_supported, sdmmc_init_io);
const bool is_mem = card->is_mem;
const bool is_sdio = !is_mem;
/* Enable CRC16 checks for data transfers in SPI mode */
SDMMC_INIT_STEP(is_spi, sdmmc_init_spi_crc);
/* Use SEND_OP_COND to set up card OCR */
SDMMC_INIT_STEP(is_mem, sdmmc_init_ocr);
const bool is_mmc = is_mem && card->is_mmc;
const bool is_sdmem = is_mem && !is_mmc;
ESP_LOGD(TAG, "%s: card type is %s", __func__,
is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");
/* Read the contents of CID register*/
SDMMC_INIT_STEP(is_mem, sdmmc_init_cid);
/* Assign RCA */
SDMMC_INIT_STEP(!is_spi, sdmmc_init_rca);
/* Read and decode the contents of CSD register */
SDMMC_INIT_STEP(is_mem, sdmmc_init_csd);
/* Decode the contents of mmc CID register */
SDMMC_INIT_STEP(is_mmc && !is_spi, sdmmc_init_mmc_decode_cid);
/* Switch the card from stand-by mode to data transfer mode (not needed if
* SPI interface is used). This is needed to issue SET_BLOCKLEN and
* SEND_SCR commands.
*/
SDMMC_INIT_STEP(!is_spi, sdmmc_init_select_card);
/* SD memory cards:
* Set block len for SDSC cards to 512 bytes (same as SDHC)
* Read SCR
* Wait to enter data transfer state
*/
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_blocklen);
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_scr);
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_wait_data_ready);
/* MMC cards: read CXD */
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_read_ext_csd);
/* Try to switch card to HS mode if the card supports it.
* Set card->max_freq_khz value accordingly.
*/
SDMMC_INIT_STEP(always, sdmmc_init_card_hs_mode);
/* Set bus width. One call for every kind of card, then one for the host */
if (!is_spi) {
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_bus_width);
SDMMC_INIT_STEP(is_sdio, sdmmc_init_io_bus_width);
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_bus_width);
SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
}
/* Switch to the host to use card->max_freq_khz frequency. */
SDMMC_INIT_STEP(always, sdmmc_init_host_frequency);
/* Sanity check after switching the bus mode and frequency */
SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
/* TODO: this is CMD line only, add data checks for eMMC */
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_check_csd);
/* TODO: add similar checks for SDIO */
return SDMMC_OK;
}

633
lib/sdmmc/sdmmc_io.c Normal file
View File

@ -0,0 +1,633 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include "sdmmc_common.h"
#define CIS_TUPLE(NAME) (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&cis_tuple_func_default, }
#define CIS_TUPLE_WITH_FUNC(NAME, FUNC) (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&(FUNC), }
#define CIS_CHECK_SIZE(SIZE, MINIMAL) do {int store_size = (SIZE); if((store_size) < (MINIMAL)) return SDMMC_ERR_INVALID_SIZE;} while(0)
#define CIS_CHECK_UNSUPPORTED(COND) do {if(!(COND)) return SDMMC_ERR_NOT_SUPPORTED;} while(0)
#define CIS_GET_MINIMAL_SIZE 32
typedef sdmmc_err_t (*cis_tuple_info_func_t)(const void* tuple_info, uint8_t* data, FILE* fp);
typedef struct {
int code;
const char *name;
cis_tuple_info_func_t func;
} cis_tuple_t;
static sdmmc_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp);
static sdmmc_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp);
static sdmmc_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp);
static sdmmc_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp);
static const cis_tuple_t cis_table[] = {
CIS_TUPLE(NULL),
CIS_TUPLE(DEVICE),
CIS_TUPLE(CHKSUM),
CIS_TUPLE(VERS1),
CIS_TUPLE(ALTSTR),
CIS_TUPLE(CONFIG),
CIS_TUPLE_WITH_FUNC(CFTABLE_ENTRY, cis_tuple_func_cftable_entry),
CIS_TUPLE_WITH_FUNC(MANFID, cis_tuple_func_manfid),
CIS_TUPLE(FUNCID),
CIS_TUPLE(FUNCE),
CIS_TUPLE(VENDER_BEGIN),
CIS_TUPLE(VENDER_END),
CIS_TUPLE(SDIO_STD),
CIS_TUPLE(SDIO_EXT),
CIS_TUPLE_WITH_FUNC(END, cis_tuple_func_end),
};
sdmmc_err_t sdmmc_io_reset(sdmmc_card_t* card)
{
uint8_t sdio_reset = CCCR_CTL_RES;
sdmmc_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CTL, SD_ARG_CMD52_WRITE, &sdio_reset);
if (err == SDMMC_ERR_TIMEOUT || (host_is_spi(card) && err == SDMMC_ERR_NOT_SUPPORTED)) {
/* Non-IO cards are allowed to time out (in SD mode) or
* return "invalid command" error (in SPI mode).
*/
} else if (err == SDMMC_ERR_NO_CARD) {
ESP_LOGD(TAG, "%s: card not present", __func__);
return err;
} else if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: unexpected return: 0x%x", __func__, err );
return err;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_io(sdmmc_card_t* card)
{
/* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card.
* Non-IO cards will not respond to this command.
*/
sdmmc_err_t err = sdmmc_io_send_op_cond(card, 0, &card->ocr);
if (err != SDMMC_OK) {
ESP_LOGD(TAG, "%s: io_send_op_cond (1) returned 0x%x; not IO card", __func__, err);
card->is_sdio = 0;
card->is_mem = 1;
} else {
card->is_sdio = 1;
if (card->ocr & SD_IO_OCR_MEM_PRESENT) {
ESP_LOGD(TAG, "%s: IO-only card", __func__);
card->is_mem = 0;
}
card->num_io_functions = SD_IO_OCR_NUM_FUNCTIONS(card->ocr);
ESP_LOGD(TAG, "%s: number of IO functions: %d", __func__, card->num_io_functions);
if (card->num_io_functions == 0) {
card->is_sdio = 0;
}
uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
host_ocr &= card->ocr;
err = sdmmc_io_send_op_cond(card, host_ocr, &card->ocr);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
err = sdmmc_io_enable_int(card);
if (err != SDMMC_OK) {
ESP_LOGD(TAG, "%s: sdmmc_enable_int failed (0x%x)", __func__, err);
}
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card)
{
sdmmc_err_t err;
card->log_bus_width = 0;
if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
uint8_t card_cap = 0;
err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CARD_CAP,
SD_ARG_CMD52_READ, &card_cap);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read SD_IO_CCCR_CARD_CAP) returned 0x%0x", __func__, err);
return err;
}
ESP_LOGD(TAG, "IO card capabilities byte: %02x", card_cap);
if (!(card_cap & CCCR_CARD_CAP_LSC) ||
(card_cap & CCCR_CARD_CAP_4BLS)) {
// This card supports 4-bit bus mode
uint8_t bus_width = CCCR_BUS_WIDTH_4;
err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_BUS_WIDTH,
SD_ARG_CMD52_WRITE, &bus_width);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write SD_IO_CCCR_BUS_WIDTH) returned 0x%0x", __func__, err);
return err;
}
card->log_bus_width = 2;
}
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card)
{
/* If the host is configured to use low frequency, don't attempt to switch */
if (card->host.max_freq_khz < SDMMC_FREQ_DEFAULT) {
card->max_freq_khz = card->host.max_freq_khz;
return SDMMC_OK;
} else if (card->host.max_freq_khz < SDMMC_FREQ_HIGHSPEED) {
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
return SDMMC_OK;
}
/* For IO cards, do write + read operation on "High Speed" register,
* setting EHS bit. If both EHS and SHS read back as set, then HS mode
* has been enabled.
*/
uint8_t val = CCCR_HIGHSPEED_ENABLE;
sdmmc_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_HIGHSPEED,
SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &val);
if (err != SDMMC_OK) {
ESP_LOGD(TAG, "%s: sdmmc_io_rw_direct returned 0x%x", __func__, err);
return err;
}
ESP_LOGD(TAG, "%s: CCCR_HIGHSPEED=0x%02x", __func__, val);
const uint8_t hs_mask = CCCR_HIGHSPEED_ENABLE | CCCR_HIGHSPEED_SUPPORT;
if ((val & hs_mask) != hs_mask) {
return SDMMC_ERR_NOT_SUPPORTED;
}
card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
return SDMMC_OK;
}
sdmmc_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
{
sdmmc_err_t err = SDMMC_OK;
sdmmc_command_t cmd = {
.flags = SCF_CMD_BCR | SCF_RSP_R4,
.arg = ocr,
.opcode = SD_IO_SEND_OP_COND
};
for (size_t i = 0; i < 100; i++) {
err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
break;
}
if ((MMC_R4(cmd.response) & SD_IO_OCR_MEM_READY) ||
ocr == 0) {
break;
}
err = SDMMC_ERR_TIMEOUT;
osal_task_delay(SDMMC_IO_SEND_OP_COND_DELAY_MS);
}
if (err == SDMMC_OK && ocrp != NULL)
*ocrp = MMC_R4(cmd.response);
return err;
}
sdmmc_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int func,
uint32_t reg, uint32_t arg, uint8_t *data)
{
sdmmc_err_t err;
sdmmc_command_t cmd = {
.flags = SCF_CMD_AC | SCF_RSP_R5,
.arg = 0,
.opcode = SD_IO_RW_DIRECT
};
arg |= (func & SD_ARG_CMD52_FUNC_MASK) << SD_ARG_CMD52_FUNC_SHIFT;
arg |= (reg & SD_ARG_CMD52_REG_MASK) << SD_ARG_CMD52_REG_SHIFT;
arg |= (*data & SD_ARG_CMD52_DATA_MASK) << SD_ARG_CMD52_DATA_SHIFT;
cmd.arg = arg;
err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
ESP_LOGV(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
}
*data = SD_R5_DATA(cmd.response);
return SDMMC_OK;
}
sdmmc_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function,
uint32_t addr, uint8_t *out_byte)
{
sdmmc_err_t ret = sdmmc_io_rw_direct(card, function, addr, SD_ARG_CMD52_READ, out_byte);
if (ret != SDMMC_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read 0x%x) returned 0x%x", __func__, addr, ret);
}
return ret;
}
sdmmc_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function,
uint32_t addr, uint8_t in_byte, uint8_t* out_byte)
{
uint8_t tmp_byte = in_byte;
sdmmc_err_t ret = sdmmc_io_rw_direct(card, function, addr,
SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &tmp_byte);
if (ret != SDMMC_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write 0x%x) returned 0x%x", __func__, addr, ret);
return ret;
}
if (out_byte != NULL) {
*out_byte = tmp_byte;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int func,
uint32_t reg, int arg, void *datap, size_t datalen)
{
sdmmc_err_t err;
const size_t max_byte_transfer_size = 512;
sdmmc_command_t cmd = {
.flags = SCF_CMD_AC | SCF_RSP_R5,
.arg = 0,
.opcode = SD_IO_RW_EXTENDED,
.data = datap,
.datalen = datalen,
.blklen = max_byte_transfer_size /* TODO: read max block size from CIS */
};
uint32_t count; /* number of bytes or blocks, depending on transfer mode */
if (arg & SD_ARG_CMD53_BLOCK_MODE) {
if (cmd.datalen % cmd.blklen != 0) {
return SDMMC_ERR_INVALID_SIZE;
}
count = cmd.datalen / cmd.blklen;
} else {
if (datalen > max_byte_transfer_size) {
/* TODO: split into multiple operations? */
return SDMMC_ERR_INVALID_SIZE;
}
if (datalen == max_byte_transfer_size) {
count = 0; // See 5.3.1 SDIO simplifed spec
} else {
count = datalen;
}
cmd.blklen = datalen;
}
arg |= (func & SD_ARG_CMD53_FUNC_MASK) << SD_ARG_CMD53_FUNC_SHIFT;
arg |= (reg & SD_ARG_CMD53_REG_MASK) << SD_ARG_CMD53_REG_SHIFT;
arg |= (count & SD_ARG_CMD53_LENGTH_MASK) << SD_ARG_CMD53_LENGTH_SHIFT;
cmd.arg = arg;
if ((arg & SD_ARG_CMD53_WRITE) == 0) {
cmd.flags |= SCF_CMD_READ;
}
err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
uint32_t addr, void* dst, size_t size)
{
/* host quirk: SDIO transfer with length not divisible by 4 bytes
* has to be split into two transfers: one with aligned length,
* the other one for the remaining 1-3 bytes.
*/
uint8_t *pc_dst = dst;
while (size > 0) {
size_t size_aligned = size & (~3);
size_t will_transfer = size_aligned > 0 ? size_aligned : size;
sdmmc_err_t err = sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT,
pc_dst, will_transfer);
if (err != SDMMC_OK) {
return err;
}
pc_dst += will_transfer;
size -= will_transfer;
addr += will_transfer;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
uint32_t addr, const void* src, size_t size)
{
/* same host quirk as in sdmmc_io_read_bytes */
const uint8_t *pc_src = (const uint8_t*) src;
while (size > 0) {
size_t size_aligned = size & (~3);
size_t will_transfer = size_aligned > 0 ? size_aligned : size;
sdmmc_err_t err = sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT,
(void*) pc_src, will_transfer);
if (err != SDMMC_OK) {
return err;
}
pc_src += will_transfer;
size -= will_transfer;
addr += will_transfer;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
uint32_t addr, void* dst, size_t size)
{
if (size % 4 != 0) {
return SDMMC_ERR_INVALID_SIZE;
}
return sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
dst, size);
}
sdmmc_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
uint32_t addr, const void* src, size_t size)
{
if (size % 4 != 0) {
return SDMMC_ERR_INVALID_SIZE;
}
return sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
(void*) src, size);
}
sdmmc_err_t sdmmc_io_enable_int(sdmmc_card_t* card)
{
if (card->host.io_int_enable == NULL) {
return SDMMC_ERR_NOT_SUPPORTED;
}
return (*card->host.io_int_enable)(card->host.slot);
}
sdmmc_err_t sdmmc_io_wait_int(sdmmc_card_t* card, int timeout_ms)
{
if (card->host.io_int_wait == NULL) {
return SDMMC_ERR_NOT_SUPPORTED;
}
return (*card->host.io_int_wait)(card->host.slot, timeout_ms);
}
/*
* Print the CIS information of a CIS card, currently only ESP slave supported.
*/
static sdmmc_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp)
{
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
uint8_t code = *(data++);
int size = *(data++);
if (tuple) {
fprintf(fp, "TUPLE: %s, size: %d: ", tuple->name, size);
} else {
fprintf(fp, "TUPLE: unknown(%02X), size: %d: ", code, size);
}
for (int i = 0; i < size; i++) fprintf(fp, "%02X ", *(data++));
fprintf(fp, "\n");
return SDMMC_OK;
}
static sdmmc_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp)
{
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
data++;
int size = *(data++);
fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size);
CIS_CHECK_SIZE(size, 4);
fprintf(fp, " MANF: %04X, CARD: %04X\n", *(uint16_t*)(data), *(uint16_t*)(data+2));
return SDMMC_OK;
}
static sdmmc_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp)
{
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
data++;
fprintf(fp, "TUPLE: %s\n", tuple->name);
return SDMMC_OK;
}
static sdmmc_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp)
{
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
data++;
int size = *(data++);
fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size);
CIS_CHECK_SIZE(size, 2);
CIS_CHECK_SIZE(size--, 1);
bool interface = data[0] & BIT(7);
bool def = data[0] & BIT(6);
int conf_ent_num = data[0] & 0x3F;
fprintf(fp, " INDX: %02X, Intface: %d, Default: %d, Conf-Entry-Num: %d\n", *(data++), interface, def, conf_ent_num);
if (interface) {
CIS_CHECK_SIZE(size--, 1);
fprintf(fp, " IF: %02X\n", *(data++));
}
CIS_CHECK_SIZE(size--, 1);
bool misc = data[0] & BIT(7);
int mem_space = (data[0] >> 5 )&(0x3);
bool irq = data[0] & BIT(4);
bool io_sp = data[0] & BIT(3);
bool timing = data[0] & BIT(2);
int power = data[0] & 3;
fprintf(fp, " FS: %02X, misc: %d, mem_space: %d, irq: %d, io_space: %d, timing: %d, power: %d\n", *(data++), misc, mem_space, irq, io_sp, timing, power);
CIS_CHECK_UNSUPPORTED(power == 0); //power descriptor is not handled yet
CIS_CHECK_UNSUPPORTED(!timing); //timing descriptor is not handled yet
CIS_CHECK_UNSUPPORTED(!io_sp); //io space descriptor is not handled yet
if (irq) {
CIS_CHECK_SIZE(size--, 1);
bool mask = data[0] & BIT(4);
fprintf(fp, " IR: %02X, mask: %d, ",*(data++), mask);
if (mask) {
CIS_CHECK_SIZE(size, 2);
size-=2;
fprintf(fp, " IRQ: %02X %02X\n", data[0], data[1]);
data+=2;
}
}
if (mem_space) {
CIS_CHECK_SIZE(size, 2);
size-=2;
CIS_CHECK_UNSUPPORTED(mem_space==1); //other cases not handled yet
int len = *(uint16_t*)data;
fprintf(fp, " LEN: %04X\n", len);
data+=2;
}
CIS_CHECK_UNSUPPORTED(misc==0); //misc descriptor is not handled yet
return SDMMC_OK;
}
static const cis_tuple_t* get_tuple(uint8_t code)
{
for (size_t i = 0; i < sizeof(cis_table)/sizeof(cis_tuple_t); i++) {
if (code == cis_table[i].code) return &cis_table[i];
}
return NULL;
}
sdmmc_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp)
{
if (!fp) fp = stdout;
uint8_t* cis = buffer;
do {
const cis_tuple_t* tuple = get_tuple(cis[0]);
int size = cis[1];
sdmmc_err_t ret = SDMMC_OK;
if (tuple) {
ret = tuple->func(tuple, cis, fp);
} else {
ret = cis_tuple_func_default(NULL, cis, fp);
}
if (ret != SDMMC_OK) return ret;
cis += 2 + size;
if (tuple && tuple->code == CISTPL_CODE_END) break;
} while (cis < buffer + buffer_size) ;
return SDMMC_OK;
}
/**
* Check tuples in the buffer.
*
* @param buf Buffer to check
* @param buffer_size Size of the buffer
* @param inout_cis_offset
* - input: the last cis_offset, relative to the beginning of the buf. -1 if
* this buffer begin with the tuple length, otherwise should be no smaller than
* zero.
* - output: when the end tuple found, output offset of the CISTPL_CODE_END
* byte + 1 (relative to the beginning of the buffer; when not found, output
* the address of next tuple code.
*
* @return true if found, false if haven't.
*/
static bool check_tuples_in_buffer(uint8_t* buf, int buffer_size, int* inout_cis_offset)
{
int cis_offset = *inout_cis_offset;
if (cis_offset == -1) {
//the CIS code is checked in the last buffer, skip to next tuple
cis_offset += buf[0] + 2;
}
assert(cis_offset >= 0);
while (1) {
if (cis_offset < buffer_size) {
//A CIS code in the buffer, check it
if (buf[cis_offset] == CISTPL_CODE_END) {
*inout_cis_offset = cis_offset + 1;
return true;
}
}
if (cis_offset + 1 < buffer_size) {
cis_offset += buf[cis_offset+1] + 2;
} else {
break;
}
}
*inout_cis_offset = cis_offset;
return false;
}
sdmmc_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size)
{
sdmmc_err_t ret = SDMMC_OK;
uint32_t buf[CIS_GET_MINIMAL_SIZE / sizeof(uint32_t)];
/* Pointer to size is a mandatory parameter */
assert(inout_cis_size);
/*
* CIS region exist in 0x1000~0x17FFF of FUNC 0, get the start address of it
* from CCCR register.
*/
uint32_t addr;
ret = sdmmc_io_read_bytes(card, 0, 9, &addr, 3);
if (ret != SDMMC_OK) return ret;
//the sdmmc_io driver reads 4 bytes, the most significant byte is not the address.
addr &= 0xffffff;
if (addr < 0x1000 || addr > 0x17FFF) {
return SDMMC_ERR_INVALID_RESPONSE;
}
/*
* To avoid reading too long, take the input value as limitation if
* existing.
*/
size_t max_reading = UINT32_MAX;
if (*inout_cis_size != 0) {
max_reading = *inout_cis_size;
}
/*
* Parse the length while reading. If find the end tuple, or reaches the
* limitation, read no more and return both the data and the size already
* read.
*/
size_t buffer_offset = 0;
size_t cur_cis_offset = 0;
bool end_tuple_found = false;
do {
ret = sdmmc_io_read_bytes(card, 0, addr + buffer_offset, &buf, CIS_GET_MINIMAL_SIZE);
if (ret != SDMMC_OK) return ret;
//calculate relative to the beginning of the buffer
int offset = cur_cis_offset - buffer_offset;
bool finish = check_tuples_in_buffer((uint8_t*) buf, CIS_GET_MINIMAL_SIZE, &offset);
int remain_size = buffer_size - buffer_offset;
int copy_len;
if (finish) {
copy_len = TSD_MIN(offset, remain_size);
end_tuple_found = true;
} else {
copy_len = TSD_MIN(CIS_GET_MINIMAL_SIZE, remain_size);
}
if (copy_len > 0) {
memcpy(out_buffer + buffer_offset, buf, copy_len);
}
cur_cis_offset = buffer_offset + offset;
buffer_offset += CIS_GET_MINIMAL_SIZE;
} while (!end_tuple_found && buffer_offset < max_reading);
if (end_tuple_found) {
*inout_cis_size = cur_cis_offset;
if (cur_cis_offset > buffer_size) {
return SDMMC_ERR_INVALID_SIZE;
} else {
return SDMMC_OK;
}
} else {
return SDMMC_ERR_NO_CARD;
}
}

251
lib/sdmmc/sdmmc_mmc.c Normal file
View File

@ -0,0 +1,251 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include <unistd.h>
#include "sdmmc_common.h"
sdmmc_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
{
int card_type;
sdmmc_err_t err = SDMMC_OK;
uint32_t ext_csd[EXT_CSD_MMC_SIZE / sizeof(uint32_t)];
uint32_t sectors = 0;
ESP_LOGD(TAG, "MMC version: %d", card->csd.mmc_ver);
if (card->csd.mmc_ver < MMC_CSD_MMCVER_4_0) {
return SDMMC_ERR_NOT_SUPPORTED;
}
/* read EXT_CSD */
err = sdmmc_mmc_send_ext_csd_data(card, ext_csd, EXT_CSD_MMC_SIZE);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: send_ext_csd_data error 0x%x", __func__, err);
return err;
}
card_type = ext_csd[EXT_CSD_CARD_TYPE / sizeof(uint32_t)];
card->is_ddr = 0;
if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) {
card->max_freq_khz = SDMMC_FREQ_52M;
if ((card->host.flags & SDMMC_HOST_FLAG_DDR) &&
card->host.max_freq_khz >= SDMMC_FREQ_26M &&
card->host.get_bus_width(card->host.slot) == 4) {
ESP_LOGD(TAG, "card and host support DDR mode");
card->is_ddr = 1;
}
} else if (card_type & EXT_CSD_CARD_TYPE_F_52M) {
card->max_freq_khz = SDMMC_FREQ_52M;
} else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
card->max_freq_khz = SDMMC_FREQ_26M;
} else {
ESP_LOGW(TAG, "%s: unknown CARD_TYPE 0x%x", __func__, card_type);
}
/* For MMC cards, use speed value from EXT_CSD */
card->csd.tr_speed = card->max_freq_khz * 1000;
ESP_LOGD(TAG, "MMC card type %d, max_freq_khz=%d, is_ddr=%d", card_type, card->max_freq_khz, card->is_ddr);
card->max_freq_khz = TSD_MIN(card->max_freq_khz, card->host.max_freq_khz);
if (card->host.flags & SDMMC_HOST_FLAG_8BIT) {
card->ext_csd.power_class = ext_csd[((card->max_freq_khz > SDMMC_FREQ_26M) ?
EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360) / sizeof(uint32_t)] >> 4;
card->log_bus_width = 3;
} else if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
card->ext_csd.power_class = ext_csd[((card->max_freq_khz > SDMMC_FREQ_26M) ?
EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360) / sizeof(uint32_t)] & 0x0f;
card->log_bus_width = 2;
} else {
card->ext_csd.power_class = 0; //card must be able to do full rate at powerclass 0 in 1-bit mode
card->log_bus_width = 0;
}
sectors = ext_csd[EXT_CSD_SEC_COUNT / sizeof(uint32_t)];
if (sectors > (2u * 1024 * 1024 * 1024) / 512) {
card->csd.capacity = sectors;
}
return err;
}
sdmmc_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card)
{
sdmmc_err_t err;
if (card->ext_csd.power_class != 0) {
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_CLASS, card->ext_csd.power_class);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: can't change power class (%d bit), 0x%x"
, __func__, card->ext_csd.power_class, err);
return err;
}
}
if (card->log_bus_width > 0) {
int csd_bus_width_value = EXT_CSD_BUS_WIDTH_1;
int bus_width = 1;
if (card->log_bus_width == 2) {
if (card->is_ddr) {
csd_bus_width_value = EXT_CSD_BUS_WIDTH_4_DDR;
} else {
csd_bus_width_value = EXT_CSD_BUS_WIDTH_4;
}
bus_width = 4;
} else if (card->log_bus_width == 3) {
if (card->is_ddr) {
csd_bus_width_value = EXT_CSD_BUS_WIDTH_8_DDR;
} else {
csd_bus_width_value = EXT_CSD_BUS_WIDTH_8;
}
bus_width = 8;
}
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, csd_bus_width_value);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: can't change bus width (%d bit), 0x%x",
__func__, bus_width, err);
(void) bus_width;
return err;
}
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card)
{
sdmmc_err_t err;
if (card->max_freq_khz > SDMMC_FREQ_26M) {
/* switch to high speed timing */
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, EXT_CSD_HS_TIMING_HS);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: mmc_switch EXT_CSD_HS_TIMING_HS error 0x%x",
__func__, err);
return err;
}
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid)
{
if (mmc_ver == MMC_CSD_MMCVER_1_0 ||
mmc_ver == MMC_CSD_MMCVER_1_4) {
out_cid->mfg_id = MMC_CID_MID_V1(resp);
out_cid->oem_id = 0;
MMC_CID_PNM_V1_CPY(resp, out_cid->name);
out_cid->revision = MMC_CID_REV_V1(resp);
out_cid->serial = MMC_CID_PSN_V1(resp);
out_cid->date = MMC_CID_MDT_V1(resp);
} else if (mmc_ver == MMC_CSD_MMCVER_2_0 ||
mmc_ver == MMC_CSD_MMCVER_3_1 ||
mmc_ver == MMC_CSD_MMCVER_4_0) {
out_cid->mfg_id = MMC_CID_MID_V2(resp);
out_cid->oem_id = MMC_CID_OID_V2(resp);
MMC_CID_PNM_V1_CPY(resp, out_cid->name);
out_cid->revision = 0;
out_cid->serial = MMC_CID_PSN_V1(resp);
out_cid->date = 0;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
{
out_csd->csd_ver = MMC_CSD_CSDVER(response);
if (out_csd->csd_ver == MMC_CSD_CSDVER_1_0 ||
out_csd->csd_ver == MMC_CSD_CSDVER_2_0 ||
out_csd->csd_ver == MMC_CSD_CSDVER_EXT_CSD) {
out_csd->mmc_ver = MMC_CSD_MMCVER(response);
out_csd->capacity = MMC_CSD_CAPACITY(response);
out_csd->read_block_len = MMC_CSD_READ_BL_LEN(response);
} else {
ESP_LOGE(TAG, "unknown MMC CSD structure version 0x%x\n", out_csd->csd_ver);
return 1;
}
int read_bl_size = 1 << out_csd->read_block_len;
out_csd->sector_size = TSD_MIN(read_bl_size, 512);
if (out_csd->sector_size < read_bl_size) {
out_csd->capacity *= read_bl_size / out_csd->sector_size;
}
/* tr_speed will be determined when reading CXD */
out_csd->tr_speed = 0;
return SDMMC_OK;
}
sdmmc_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen)
{
sdmmc_command_t cmd = {
.data = out_data,
.datalen = datalen,
.blklen = datalen,
.opcode = MMC_SEND_EXT_CSD,
.arg = 0,
.flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ
};
return sdmmc_send_cmd(card, &cmd);
}
sdmmc_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value)
{
sdmmc_command_t cmd = {
.opcode = MMC_SWITCH,
.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
.flags = SCF_RSP_R1B | SCF_CMD_AC | SCF_WAIT_BUSY,
};
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err == SDMMC_OK) {
//check response bit to see that switch was accepted
if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR)
err = SDMMC_ERR_INVALID_RESPONSE;
}
return err;
}
sdmmc_err_t sdmmc_init_mmc_check_csd(sdmmc_card_t* card)
{
sdmmc_err_t err;
assert(card->is_mem == 1);
assert(card->rca != 0);
//The card will not respond to send_csd command in the transfer state.
//Deselect it first.
err = sdmmc_send_cmd_select_card(card, 0);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
return err;
}
sdmmc_csd_t csd;
/* Get the contents of CSD register to verify the communication over CMD line
is OK. */
err = sdmmc_send_cmd_send_csd(card, &csd);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
return err;
}
//Select the card again
err = sdmmc_send_cmd_select_card(card, card->rca);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
return err;
}
return SDMMC_OK;
}

343
lib/sdmmc/sdmmc_sd.c Normal file
View File

@ -0,0 +1,343 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include "sdmmc_common.h"
sdmmc_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card)
{
/* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards.
* SD v1 and non-SD cards will not respond to this command.
*/
uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
sdmmc_err_t err = sdmmc_send_cmd_send_if_cond(card, host_ocr);
if (err == SDMMC_OK) {
ESP_LOGD(TAG, "SDHC/SDXC card");
host_ocr |= SD_OCR_SDHC_CAP;
} else if (err == SDMMC_ERR_TIMEOUT) {
ESP_LOGD(TAG, "CMD8 timeout; not an SD v2.00 card");
} else if (host_is_spi(card) && err == SDMMC_ERR_NOT_SUPPORTED) {
ESP_LOGD(TAG, "CMD8 rejected; not an SD v2.00 card");
} else {
ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err);
return err;
}
card->ocr = host_ocr;
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_sd_blocklen(sdmmc_card_t* card)
{
/* SDSC cards support configurable data block lengths.
* We don't use this feature and set the block length to 512 bytes,
* same as the block length for SDHC cards.
*/
if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
sdmmc_err_t err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
return err;
}
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_sd_scr(sdmmc_card_t* card)
{
sdmmc_err_t err;
/* Get the contents of SCR register: bus width and the version of SD spec
* supported by the card.
* In SD mode, this is the first command which uses D0 line. Errors at
* this step usually indicate connection issue or lack of pull-up resistor.
*/
err = sdmmc_send_cmd_send_scr(card, &card->scr);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
return err;
}
if ((card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)
&& (card->host.flags & SDMMC_HOST_FLAG_4BIT)) {
card->log_bus_width = 2;
} else {
card->log_bus_width = 0;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card)
{
int width = 1;
if (card->log_bus_width == 2) {
width = 4;
} else if (card->log_bus_width == 3) {
width = 8;
}
sdmmc_err_t err = sdmmc_send_cmd_set_bus_width(card, width);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "set_bus_width failed (0x%x)", err);
return err;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card)
{
/* Wait for the card to be ready for data transfers */
uint32_t status = 0;
uint32_t count = 0;
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
sdmmc_err_t err = sdmmc_send_cmd_send_status(card, &status);
if (err != SDMMC_OK) {
return err;
}
if (++count % 16 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
}
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
uint32_t mode, uint32_t group, uint32_t function,
sdmmc_switch_func_rsp_t* resp)
{
if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
return SDMMC_ERR_NOT_SUPPORTED;
}
if (group == 0 ||
group > SD_SFUNC_GROUP_MAX ||
function > SD_SFUNC_FUNC_MAX) {
return SDMMC_ERR_INVALID_ARG;
}
if (mode > 1) {
return SDMMC_ERR_INVALID_ARG;
}
uint32_t group_shift = (group - 1) << 2;
/* all functions which should not be affected are set to 0xf (no change) */
uint32_t other_func_mask = (0x00ffffff & ~(0xf << group_shift));
uint32_t func_val = (function << group_shift) | other_func_mask;
sdmmc_command_t cmd = {
.opcode = MMC_SWITCH,
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
.blklen = sizeof(sdmmc_switch_func_rsp_t),
.data = resp->data,
.datalen = sizeof(sdmmc_switch_func_rsp_t),
.arg = (!!mode << 31) | func_val
};
sdmmc_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
}
sdmmc_flip_byte_order(resp->data, sizeof(sdmmc_switch_func_rsp_t));
uint32_t resp_ver = SD_SFUNC_VER(resp->data);
if (resp_ver == 0) {
/* busy response is never sent */
} else if (resp_ver == 1) {
if (SD_SFUNC_BUSY(resp->data, group) & (1 << function)) {
ESP_LOGD(TAG, "%s: response indicates function %d:%d is busy",
__func__, group, function);
return SDMMC_ERR_BUSY;
}
} else {
ESP_LOGD(TAG, "%s: got an invalid version of SWITCH_FUNC response: 0x%02x",
__func__, resp_ver);
return SDMMC_ERR_INVALID_RESPONSE;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card)
{
/* This will determine if the card supports SWITCH_FUNC command,
* and high speed mode. If the cards supports both, this will enable
* high speed mode at the card side.
*/
if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
return SDMMC_ERR_NOT_SUPPORTED;
}
sdmmc_switch_func_rsp_t response;
sdmmc_err_t err = sdmmc_send_cmd_switch_func(card, 0, SD_ACCESS_MODE, 0, &response);
if (err != SDMMC_OK) {
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (1) returned 0x%x", __func__, err);
return err;
}
uint32_t supported_mask = SD_SFUNC_SUPPORTED(response.data, 1);
if ((supported_mask & BIT(SD_ACCESS_MODE_SDR25)) == 0) {
return SDMMC_ERR_NOT_SUPPORTED;
}
err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR25, &response);
if (err != SDMMC_OK) {
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err);
return err;
}
return err;
}
sdmmc_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
{
/* All cards should support at least default speed */
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
if (card->host.max_freq_khz <= card->max_freq_khz) {
/* Host is configured to use low frequency, don't attempt to switch */
card->max_freq_khz = card->host.max_freq_khz;
return SDMMC_OK;
}
/* Try to enabled HS mode */
sdmmc_err_t err = sdmmc_enable_hs_mode(card);
if (err != SDMMC_OK) {
return err;
}
/* HS mode has been enabled on the card.
* Read CSD again, it should now indicate that the card supports
* 50MHz clock.
* Since SEND_CSD is allowed only in standby mode, and the card is currently in data transfer
* mode, deselect the card first, then get the CSD, then select the card again. This step is
* not required in SPI mode, since CMD7 (select_card) is not supported.
*/
const bool is_spi = host_is_spi(card);
if (!is_spi) {
err = sdmmc_send_cmd_select_card(card, 0);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: select_card (1) returned 0x%x", __func__, err);
return err;
}
}
err = sdmmc_send_cmd_send_csd(card, &card->csd);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
return err;
}
if (!is_spi) {
err = sdmmc_send_cmd_select_card(card, card->rca);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: select_card (2) returned 0x%x", __func__, err);
return err;
}
}
if (card->csd.tr_speed != 50000000) {
ESP_LOGW(TAG, "unexpected: after enabling HS mode, tr_speed=%d", card->csd.tr_speed);
return SDMMC_ERR_NOT_SUPPORTED;
}
card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
return SDMMC_OK;
}
sdmmc_err_t sdmmc_check_scr(sdmmc_card_t* card)
{
/* If frequency switch has been performed, read SCR register one more time
* and compare the result with the previous one. Use this simple check as
* an indicator of potential signal integrity issues.
*/
sdmmc_scr_t scr_tmp;
sdmmc_err_t err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err);
return err;
}
if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
ESP_LOGE(TAG, "got corrupted data after increasing clock frequency");
return SDMMC_ERR_INVALID_RESPONSE;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_init_spi_crc(sdmmc_card_t* card)
{
/* In SD mode, CRC checks of data transfers are mandatory and performed
* by the hardware. In SPI mode, CRC16 of data transfers is optional and
* needs to be enabled.
*/
assert(host_is_spi(card));
sdmmc_err_t err = sdmmc_send_cmd_crc_on_off(card, true);
if (err != SDMMC_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd_crc_on_off returned 0x%x", __func__, err);
return err;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid)
{
out_cid->mfg_id = SD_CID_MID(resp);
out_cid->oem_id = SD_CID_OID(resp);
SD_CID_PNM_CPY(resp, out_cid->name);
out_cid->revision = SD_CID_REV(resp);
out_cid->serial = SD_CID_PSN(resp);
out_cid->date = SD_CID_MDT(resp);
return SDMMC_OK;
}
sdmmc_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
{
out_csd->csd_ver = SD_CSD_CSDVER(response);
switch (out_csd->csd_ver) {
case SD_CSD_CSDVER_2_0:
out_csd->capacity = SD_CSD_V2_CAPACITY(response);
out_csd->read_block_len = SD_CSD_V2_BL_LEN;
break;
case SD_CSD_CSDVER_1_0:
out_csd->capacity = SD_CSD_CAPACITY(response);
out_csd->read_block_len = SD_CSD_READ_BL_LEN(response);
break;
default:
ESP_LOGE(TAG, "unknown SD CSD structure version 0x%x", out_csd->csd_ver);
return SDMMC_ERR_NOT_SUPPORTED;
}
out_csd->card_command_class = SD_CSD_CCC(response);
int read_bl_size = 1 << out_csd->read_block_len;
out_csd->sector_size = TSD_MIN(read_bl_size, 512);
if (out_csd->sector_size < read_bl_size) {
out_csd->capacity *= read_bl_size / out_csd->sector_size;
}
int speed = SD_CSD_SPEED(response);
if (speed == SD_CSD_SPEED_50_MHZ) {
out_csd->tr_speed = 50000000;
} else {
out_csd->tr_speed = 25000000;
}
return SDMMC_OK;
}
sdmmc_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr)
{
sdmmc_response_t resp = { 0 };
resp[1] = __builtin_bswap32(raw_scr[0]);
resp[0] = __builtin_bswap32(raw_scr[1]);
int ver = SCR_STRUCTURE(resp);
if (ver != 0) {
return SDMMC_ERR_NOT_SUPPORTED;
}
out_scr->sd_spec = SCR_SD_SPEC(resp);
out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp);
return SDMMC_OK;
}

View File

@ -0,0 +1,4 @@
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES cmock sdmmc
)

View File

@ -0,0 +1 @@
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

584
lib/sdmmc/test/test_sd.c Normal file
View File

@ -0,0 +1,584 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include "unity.h"
#include "driver/gpio.h"
#include "soc/soc_caps.h"
#if SOC_SDMMC_HOST_SUPPORTED
#include "driver/sdmmc_host.h"
#endif
#include "driver/sdspi_host.h"
#include "driver/sdmmc_defs.h"
#include "sdmmc_cmd.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "esp_rom_gpio.h"
// Can't test eMMC (slot 0) and PSRAM together
#ifndef CONFIG_SPIRAM
#define WITH_EMMC_TEST
#endif
/* power supply enable pin */
#define SD_TEST_BOARD_VSEL_EN_GPIO 27
/* power supply voltage select pin */
#define SD_TEST_BOARD_VSEL_GPIO 26
#define SD_TEST_BOARD_VSEL_3V3 1
#define SD_TEST_BOARD_VSEL_1V8 0
#define TEST_SDSPI_DMACHAN 1
/* time to wait for reset / power-on */
#define SD_TEST_BOARD_PWR_RST_DELAY_MS 5
#define SD_TEST_BOARD_PWR_ON_DELAY_MS 50
/* gpio which is not connected to actual CD pin, used to simulate CD behavior */
#define CD_WP_TEST_GPIO 18
__attribute__((unused)) static void sd_test_board_power_on(void)
{
gpio_set_direction(SD_TEST_BOARD_VSEL_GPIO, GPIO_MODE_OUTPUT);
gpio_set_level(SD_TEST_BOARD_VSEL_GPIO, SD_TEST_BOARD_VSEL_3V3);
gpio_set_direction(SD_TEST_BOARD_VSEL_EN_GPIO, GPIO_MODE_OUTPUT);
gpio_set_level(SD_TEST_BOARD_VSEL_EN_GPIO, 0);
usleep(SD_TEST_BOARD_PWR_RST_DELAY_MS * 1000);
gpio_set_level(SD_TEST_BOARD_VSEL_EN_GPIO, 1);
usleep(SD_TEST_BOARD_PWR_ON_DELAY_MS * 1000);
}
__attribute__((unused)) static void sd_test_board_power_off(void)
{
gpio_set_level(SD_TEST_BOARD_VSEL_EN_GPIO, 0);
gpio_set_direction(SD_TEST_BOARD_VSEL_GPIO, GPIO_MODE_INPUT);
gpio_set_level(SD_TEST_BOARD_VSEL_GPIO, 0);
gpio_set_direction(SD_TEST_BOARD_VSEL_EN_GPIO, GPIO_MODE_INPUT);
}
TEST_CASE("MMC_RSP_BITS", "[sd]")
{
uint32_t data[2] = { 0x01234567, 0x89abcdef };
TEST_ASSERT_EQUAL_HEX32(0x7, MMC_RSP_BITS(data, 0, 4));
TEST_ASSERT_EQUAL_HEX32(0x567, MMC_RSP_BITS(data, 0, 12));
TEST_ASSERT_EQUAL_HEX32(0xf0, MMC_RSP_BITS(data, 28, 8));
TEST_ASSERT_EQUAL_HEX32(0x3, MMC_RSP_BITS(data, 1, 3));
TEST_ASSERT_EQUAL_HEX32(0x11, MMC_RSP_BITS(data, 59, 5));
}
#if SOC_SDMMC_HOST_SUPPORTED
static void probe_sd(int slot, int width, int freq_khz, int ddr)
{
sd_test_board_power_on();
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
config.slot = slot;
config.max_freq_khz = freq_khz;
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
if (width == 1) {
config.flags = SDMMC_HOST_FLAG_1BIT;
slot_config.width = 1;
} else if (width == 4) {
config.flags &= ~SDMMC_HOST_FLAG_8BIT;
slot_config.width = 4;
} else {
assert(!ddr && "host driver does not support 8-line DDR mode yet");
}
if (!ddr) {
config.flags &= ~SDMMC_HOST_FLAG_DDR;
}
TEST_ESP_OK(sdmmc_host_init());
TEST_ESP_OK(sdmmc_host_init_slot(slot, &slot_config));
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
TEST_ESP_OK(sdmmc_card_init(&config, card));
sdmmc_card_print_info(stdout, card);
uint8_t* buffer = heap_caps_malloc(512, MALLOC_CAP_DMA);
TEST_ESP_OK(sdmmc_read_sectors(card, buffer, 0, 1));
free(buffer);
TEST_ESP_OK(sdmmc_host_deinit());
free(card);
sd_test_board_power_off();
}
TEST_CASE("probe SD, slot 1, 4-bit", "[sd][test_env=UT_T1_SDMODE]")
{
probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_PROBING, 0);
probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_DEFAULT, 0);
probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, 0);
}
TEST_CASE("probe SD, slot 1, 1-bit", "[sd][test_env=UT_T1_SDMODE]")
{
probe_sd(SDMMC_HOST_SLOT_1, 1, SDMMC_FREQ_PROBING, 0);
probe_sd(SDMMC_HOST_SLOT_1, 1, SDMMC_FREQ_DEFAULT, 0);
probe_sd(SDMMC_HOST_SLOT_1, 1, SDMMC_FREQ_HIGHSPEED, 0);
}
#ifdef WITH_EMMC_TEST
TEST_CASE("probe eMMC, slot 0, 4-bit, DDR", "[sd][test_env=EMMC]")
{
probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, 1);
}
TEST_CASE("probe eMMC, slot 0, 8-bit", "[sd][test_env=EMMC]")
{
probe_sd(SDMMC_HOST_SLOT_0, 8, SDMMC_FREQ_PROBING, 0);
probe_sd(SDMMC_HOST_SLOT_0, 8, SDMMC_FREQ_DEFAULT, 0);
probe_sd(SDMMC_HOST_SLOT_0, 8, SDMMC_FREQ_HIGHSPEED, 0);
}
#endif // WITH_EMMC_TEST
TEST_CASE("probe SD, slot 0, 4-bit", "[sd][test_env=UT_T1_SDCARD][ignore]")
{
probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_PROBING, 0);
probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_DEFAULT, 0);
probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, 0);
}
TEST_CASE("probe SD, slot 0, 1-bit", "[sd][test_env=UT_T1_SDCARD][ignore]")
{
probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_PROBING, 0);
probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_DEFAULT, 0);
probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_HIGHSPEED, 0);
}
#endif
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
//No runners
static void test_sdspi_init_bus(spi_host_device_t host, int mosi_pin, int miso_pin, int clk_pin, int dma_chan)
{
spi_bus_config_t bus_config = {
.mosi_io_num = mosi_pin,
.miso_io_num = miso_pin,
.sclk_io_num = clk_pin,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
esp_err_t err = spi_bus_initialize(host, &bus_config, dma_chan);
TEST_ESP_OK(err);
}
static void test_sdspi_deinit_bus(spi_host_device_t host)
{
esp_err_t err = spi_bus_free(host);
TEST_ESP_OK(err);
}
static void probe_core(int slot)
{
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
config.slot = slot;
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
TEST_ESP_OK(sdmmc_card_init(&config, card));
sdmmc_card_print_info(stdout, card);
free(card);
}
static void probe_spi(int freq_khz, int pin_miso, int pin_mosi, int pin_sck, int pin_cs)
{
sd_test_board_power_on();
sdspi_dev_handle_t handle;
sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT();
dev_config.gpio_cs = pin_cs;
test_sdspi_init_bus(dev_config.host_id, pin_mosi, pin_miso, pin_sck, TEST_SDSPI_DMACHAN);
TEST_ESP_OK(sdspi_host_init());
TEST_ESP_OK(sdspi_host_init_device(&dev_config, &handle));
probe_core(handle);
TEST_ESP_OK(sdspi_host_deinit());
test_sdspi_deinit_bus(dev_config.host_id);
sd_test_board_power_off();
}
static void probe_spi_legacy(int freq_khz, int pin_miso, int pin_mosi, int pin_sck, int pin_cs)
{
sd_test_board_power_on();
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
slot_config.gpio_miso = pin_miso;
slot_config.gpio_mosi = pin_mosi;
slot_config.gpio_sck = pin_sck;
slot_config.gpio_cs = pin_cs;
TEST_ESP_OK(sdspi_host_init());
TEST_ESP_OK(sdspi_host_init_slot(config.slot, &slot_config));
probe_core(config.slot);
TEST_ESP_OK(sdspi_host_deinit());
sd_test_board_power_off();
}
TEST_CASE("probe SD in SPI mode, slot 1", "[sd][test_env=UT_T1_SPIMODE]")
{
probe_spi(SDMMC_FREQ_DEFAULT, 2, 15, 14, 13);
probe_spi_legacy(SDMMC_FREQ_DEFAULT, 2, 15, 14, 13);
}
TEST_CASE("probe SD in SPI mode, slot 0", "[sd][test_env=UT_T1_SDCARD][ignore]")
{
probe_spi(SDMMC_FREQ_DEFAULT, 7, 11, 6, 10);
probe_spi_legacy(SDMMC_FREQ_DEFAULT, 7, 11, 6, 10);
}
#endif //DISABLED(ESP32S2)
// Fill buffer pointed to by 'dst' with 'count' 32-bit ints generated
// from 'rand' with the starting value of 'seed'
__attribute__((unused)) static void fill_buffer(uint32_t seed, uint8_t* dst, size_t count) {
srand(seed);
for (size_t i = 0; i < count; ++i) {
uint32_t val = rand();
memcpy(dst + i * sizeof(uint32_t), &val, sizeof(val));
}
}
// Check if the buffer pointed to by 'dst' contains 'count' 32-bit
// ints generated from 'rand' with the starting value of 'seed'
__attribute__((unused)) static void check_buffer(uint32_t seed, const uint8_t* src, size_t count) {
srand(seed);
for (size_t i = 0; i < count; ++i) {
uint32_t val;
memcpy(&val, src + i * sizeof(uint32_t), sizeof(val));
TEST_ASSERT_EQUAL_HEX32(rand(), val);
}
}
__attribute__((unused)) static void do_single_write_read_test(sdmmc_card_t* card,
size_t start_block, size_t block_count, size_t alignment)
{
size_t block_size = card->csd.sector_size;
size_t total_size = block_size * block_count;
printf(" %8d | %3d | %d | %4.1f ", start_block, block_count, alignment, total_size / 1024.0f);
uint32_t* buffer = heap_caps_malloc(total_size + 4, MALLOC_CAP_DMA);
size_t offset = alignment % 4;
uint8_t* c_buffer = (uint8_t*) buffer + offset;
fill_buffer(start_block, c_buffer, total_size / sizeof(buffer[0]));
struct timeval t_start_wr;
gettimeofday(&t_start_wr, NULL);
TEST_ESP_OK(sdmmc_write_sectors(card, c_buffer, start_block, block_count));
struct timeval t_stop_wr;
gettimeofday(&t_stop_wr, NULL);
float time_wr = 1e3f * (t_stop_wr.tv_sec - t_start_wr.tv_sec) + 1e-3f * (t_stop_wr.tv_usec - t_start_wr.tv_usec);
memset(buffer, 0xbb, total_size + 4);
struct timeval t_start_rd;
gettimeofday(&t_start_rd, NULL);
TEST_ESP_OK(sdmmc_read_sectors(card, c_buffer, start_block, block_count));
struct timeval t_stop_rd;
gettimeofday(&t_stop_rd, NULL);
float time_rd = 1e3f * (t_stop_rd.tv_sec - t_start_rd.tv_sec) + 1e-3f * (t_stop_rd.tv_usec - t_start_rd.tv_usec);
printf(" | %6.2f | %5.2f | %6.2f | %5.2f\n",
time_wr, total_size / (time_wr / 1000) / (1024 * 1024),
time_rd, total_size / (time_rd / 1000) / (1024 * 1024));
check_buffer(start_block, c_buffer, total_size / sizeof(buffer[0]));
free(buffer);
}
__attribute__((unused)) static void read_write_test(sdmmc_card_t* card)
{
sdmmc_card_print_info(stdout, card);
printf(" sector | count | align | size(kB) | wr_time(ms) | wr_speed(MB/s) | rd_time(ms) | rd_speed(MB/s)\n");
do_single_write_read_test(card, 0, 1, 4);
do_single_write_read_test(card, 0, 4, 4);
do_single_write_read_test(card, 1, 16, 4);
do_single_write_read_test(card, 16, 32, 4);
do_single_write_read_test(card, 48, 64, 4);
do_single_write_read_test(card, 128, 128, 4);
do_single_write_read_test(card, card->csd.capacity - 64, 32, 4);
do_single_write_read_test(card, card->csd.capacity - 64, 64, 4);
do_single_write_read_test(card, card->csd.capacity - 8, 1, 4);
do_single_write_read_test(card, card->csd.capacity/2, 1, 4);
do_single_write_read_test(card, card->csd.capacity/2, 4, 4);
do_single_write_read_test(card, card->csd.capacity/2, 8, 4);
do_single_write_read_test(card, card->csd.capacity/2, 16, 4);
do_single_write_read_test(card, card->csd.capacity/2, 32, 4);
do_single_write_read_test(card, card->csd.capacity/2, 64, 4);
do_single_write_read_test(card, card->csd.capacity/2, 128, 4);
do_single_write_read_test(card, card->csd.capacity/2, 1, 1);
do_single_write_read_test(card, card->csd.capacity/2, 8, 1);
do_single_write_read_test(card, card->csd.capacity/2, 128, 1);
}
#if SOC_SDMMC_HOST_SUPPORTED
void test_sd_rw_blocks(int slot, int width)
{
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
config.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
config.slot = slot;
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
if (width != 0) {
slot_config.width = width;
}
if (slot_config.width == 8) {
config.flags &= ~SDMMC_HOST_FLAG_DDR;
}
TEST_ESP_OK(sdmmc_host_init());
TEST_ESP_OK(sdmmc_host_init_slot(slot, &slot_config));
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
TEST_ESP_OK(sdmmc_card_init(&config, card));
read_write_test(card);
free(card);
TEST_ESP_OK(sdmmc_host_deinit());
}
TEST_CASE("SDMMC read/write test (SD slot 1)", "[sd][test_env=UT_T1_SDMODE]")
{
sd_test_board_power_on();
test_sd_rw_blocks(1, 4);
sd_test_board_power_off();
}
#ifdef WITH_EMMC_TEST
TEST_CASE("SDMMC read/write test (eMMC slot 0, 4 line DDR)", "[sd][test_env=EMMC]")
{
sd_test_board_power_on();
test_sd_rw_blocks(0, 4);
sd_test_board_power_off();
}
TEST_CASE("SDMMC read/write test (eMMC slot 0, 8 line)", "[sd][test_env=EMMC]")
{
sd_test_board_power_on();
test_sd_rw_blocks(0, 8);
sd_test_board_power_off();
}
#endif // WITH_EMMC_TEST
#endif // SDMMC_HOST_SUPPORTED
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
//No runners
TEST_CASE("SDMMC read/write test (SD slot 1, in SPI mode)", "[sdspi][test_env=UT_T1_SPIMODE]")
{
sd_test_board_power_on();
sdspi_dev_handle_t handle;
sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT();
test_sdspi_init_bus(dev_config.host_id, GPIO_NUM_15, GPIO_NUM_2, GPIO_NUM_14, TEST_SDSPI_DMACHAN);
TEST_ESP_OK(sdspi_host_init());
TEST_ESP_OK(sdspi_host_init_device(&dev_config, &handle));
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
config.slot = handle;
// This test can only run under 20MHz on ESP32, because the runner connects the card to
// non-IOMUX pins of HSPI.
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
TEST_ESP_OK(sdmmc_card_init(&config, card));
read_write_test(card);
TEST_ESP_OK(sdspi_host_deinit());
free(card);
test_sdspi_deinit_bus(dev_config.host_id);
sd_test_board_power_off();
}
#endif //DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
#if SOC_SDMMC_HOST_SUPPORTED
TEST_CASE("reads and writes with an unaligned buffer", "[sd][test_env=UT_T1_SDMODE]")
{
sd_test_board_power_on();
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
TEST_ESP_OK(sdmmc_host_init());
TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
TEST_ESP_OK(sdmmc_card_init(&config, card));
const size_t buffer_size = 4096;
const size_t block_count = buffer_size / 512;
const size_t extra = 4;
uint8_t* buffer = heap_caps_malloc(buffer_size + extra, MALLOC_CAP_DMA);
// Check read behavior: do aligned write, then unaligned read
const uint32_t seed = 0x89abcdef;
fill_buffer(seed, buffer, buffer_size / sizeof(uint32_t));
TEST_ESP_OK(sdmmc_write_sectors(card, buffer, 0, block_count));
memset(buffer, 0xcc, buffer_size + extra);
TEST_ESP_OK(sdmmc_read_sectors(card, buffer + 1, 0, block_count));
check_buffer(seed, buffer + 1, buffer_size / sizeof(uint32_t));
// Check write behavior: do unaligned write, then aligned read
fill_buffer(seed, buffer + 1, buffer_size / sizeof(uint32_t));
TEST_ESP_OK(sdmmc_write_sectors(card, buffer + 1, 8, block_count));
memset(buffer, 0xcc, buffer_size + extra);
TEST_ESP_OK(sdmmc_read_sectors(card, buffer, 8, block_count));
check_buffer(seed, buffer, buffer_size / sizeof(uint32_t));
free(buffer);
free(card);
TEST_ESP_OK(sdmmc_host_deinit());
sd_test_board_power_off();
}
#endif
__attribute__((unused)) static void test_cd_input(int gpio_cd_num, const sdmmc_host_t* config)
{
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
// SDMMC host should have configured CD as input.
// Enable output as well (not using the driver, to avoid touching input
// enable bits).
esp_rom_gpio_connect_out_signal(gpio_cd_num, SIG_GPIO_OUT_IDX, false, false);
REG_WRITE(GPIO_ENABLE_W1TS_REG, BIT(gpio_cd_num));
// Check that card initialization fails if CD is high
REG_WRITE(GPIO_OUT_W1TS_REG, BIT(gpio_cd_num));
usleep(1000);
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, sdmmc_card_init(config, card));
// Check that card initialization succeeds if CD is low
REG_WRITE(GPIO_OUT_W1TC_REG, BIT(gpio_cd_num));
usleep(1000);
TEST_ESP_OK(sdmmc_card_init(config, card));
free(card);
}
#if SOC_SDMMC_HOST_SUPPORTED
TEST_CASE("CD input works in SD mode", "[sd][test_env=UT_T1_SDMODE]")
{
sd_test_board_power_on();
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
slot_config.gpio_cd = CD_WP_TEST_GPIO;
TEST_ESP_OK(sdmmc_host_init());
TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
test_cd_input(CD_WP_TEST_GPIO, &config);
TEST_ESP_OK(sdmmc_host_deinit());
sd_test_board_power_off();
}
#endif
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
//No runners
TEST_CASE("CD input works in SPI mode", "[sd][test_env=UT_T1_SPIMODE]")
{
sd_test_board_power_on();
sdspi_dev_handle_t handle;
sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT();
dev_config.gpio_cd = CD_WP_TEST_GPIO;
test_sdspi_init_bus(dev_config.host_id, GPIO_NUM_15, GPIO_NUM_2, GPIO_NUM_14, TEST_SDSPI_DMACHAN);
TEST_ESP_OK(sdspi_host_init());
TEST_ESP_OK(sdspi_host_init_device(&dev_config, &handle));
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
config.slot = handle;
test_cd_input(CD_WP_TEST_GPIO, &config);
TEST_ESP_OK(sdspi_host_deinit());
test_sdspi_deinit_bus(dev_config.host_id);
sd_test_board_power_off();
}
#endif //DISABLED_FOR_TARGETS(ESP32S2)
__attribute__((unused)) static void test_wp_input(int gpio_wp_num, const sdmmc_host_t* config)
{
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
// SDMMC host should have configured WP as input.
// Enable output as well (not using the driver, to avoid touching input
// enable bits).
esp_rom_gpio_connect_out_signal(gpio_wp_num, SIG_GPIO_OUT_IDX, false, false);
REG_WRITE(GPIO_ENABLE_W1TS_REG, BIT(gpio_wp_num));
// Check that the card can be initialized with WP low
REG_WRITE(GPIO_OUT_W1TC_REG, BIT(gpio_wp_num));
TEST_ESP_OK(sdmmc_card_init(config, card));
uint32_t* data = heap_caps_calloc(1, 512, MALLOC_CAP_DMA);
// Check that card write succeeds if WP is high
REG_WRITE(GPIO_OUT_W1TS_REG, BIT(gpio_wp_num));
usleep(1000);
TEST_ESP_OK(sdmmc_write_sectors(card, &data, 0, 1));
// Check that write fails if WP is low
REG_WRITE(GPIO_OUT_W1TC_REG, BIT(gpio_wp_num));
usleep(1000);
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, sdmmc_write_sectors(card, &data, 0, 1));
// ...but reads still work
TEST_ESP_OK(sdmmc_read_sectors(card, &data, 0, 1));
free(data);
free(card);
}
#if SOC_SDMMC_HOST_SUPPORTED
TEST_CASE("WP input works in SD mode", "[sd][test_env=UT_T1_SDMODE]")
{
sd_test_board_power_on();
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
slot_config.gpio_wp = CD_WP_TEST_GPIO;
TEST_ESP_OK(sdmmc_host_init());
TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
test_wp_input(CD_WP_TEST_GPIO, &config);
TEST_ESP_OK(sdmmc_host_deinit());
sd_test_board_power_off();
}
#endif
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
//No runners
TEST_CASE("WP input works in SPI mode", "[sd][test_env=UT_T1_SPIMODE]")
{
sd_test_board_power_on();
sdspi_dev_handle_t handle;
sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT();
dev_config.gpio_wp = CD_WP_TEST_GPIO;
test_sdspi_init_bus(dev_config.host_id, GPIO_NUM_15, GPIO_NUM_2, GPIO_NUM_14, TEST_SDSPI_DMACHAN);
TEST_ESP_OK(sdspi_host_init());
TEST_ESP_OK(sdspi_host_init_device(&dev_config, &handle));
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
config.slot = handle;
test_wp_input(CD_WP_TEST_GPIO, &config);
TEST_ESP_OK(sdspi_host_deinit());
test_sdspi_deinit_bus(dev_config.host_id);
sd_test_board_power_off();
}
#endif //DISABLED_FOR_TARGETS(ESP32S2)

388
lib/sdmmc/test/test_sdio.c Normal file
View File

@ -0,0 +1,388 @@
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "soc/soc_caps.h"
#if SOC_SDMMC_HOST_SUPPORTED
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/sdmmc_host.h"
#include "driver/sdmmc_defs.h"
#include "sdmmc_cmd.h"
#include "unity.h"
/* Second ESP32 board attached as follows:
* Master Slave
* IO18 EN
* IO19 IO0
* IO14 SD_CLK
* IO15 SD_CMD
* IO2 SD_D0
* IO4 SD_D1
* IO12 SD_D2
* IO13 SD_D3
*/
/* TODO: add SDIO slave header files, remove these definitions */
#define DR_REG_SLC_MASK 0xfffffc00
#define SLCCONF1 (DR_REG_SLC_BASE + 0x60)
#define SLC_SLC0_RX_STITCH_EN (BIT(6))
#define SLC_SLC0_TX_STITCH_EN (BIT(5))
#define SLC0TX_LINK (DR_REG_SLC_BASE + 0x40)
#define SLC_SLC0_TXLINK_PARK (BIT(31))
#define SLC_SLC0_TXLINK_RESTART (BIT(30))
#define SLC_SLC0_TXLINK_START (BIT(29))
#define DR_REG_SLCHOST_MASK 0xfffffc00
#define SLCHOST_STATE_W0 (DR_REG_SLCHOST_BASE + 0x64)
#define SLCHOST_CONF_W0 (DR_REG_SLCHOST_BASE + 0x6C)
#define SLCHOST_CONF_W5 (DR_REG_SLCHOST_BASE + 0x80)
#define SLCHOST_WIN_CMD (DR_REG_SLCHOST_BASE + 0x84)
#define SLC_WIN_CMD_READ 0x80
#define SLC_WIN_CMD_WRITE 0xC0
#define SLC_WIN_CMD_S 8
#define SLC_THRESHOLD_ADDR 0x1f800
static const char* TAG = "sdio_test";
static esp_err_t slave_slchost_reg_read(sdmmc_card_t* card, uint32_t addr, uint32_t* out_val)
{
if ((addr & DR_REG_SLCHOST_MASK) != DR_REG_SLCHOST_BASE) {
ESP_LOGW(TAG, "%s: invalid addr 0x%08x\n", __func__, addr);
return ESP_ERR_INVALID_ARG;
}
return sdmmc_io_read_bytes(card, 1, addr & (~DR_REG_SLCHOST_MASK), out_val, sizeof(*out_val));
}
static esp_err_t slave_slchost_reg_write(sdmmc_card_t* card, uint32_t addr, uint32_t val)
{
if ((addr & DR_REG_SLCHOST_MASK) != DR_REG_SLCHOST_BASE) {
ESP_LOGW(TAG, "%s: invalid addr 0x%08x\n", __func__, addr);
return ESP_ERR_INVALID_ARG;
}
return sdmmc_io_write_bytes(card, 1, addr & (~DR_REG_SLCHOST_MASK), &val, sizeof(val));
}
static esp_err_t slave_slc_reg_read(sdmmc_card_t* card, uint32_t addr, uint32_t* val)
{
if ((addr & DR_REG_SLC_MASK) != DR_REG_SLC_BASE) {
ESP_LOGW(TAG, "%s: invalid addr 0x%08x\n", __func__, addr);
return ESP_ERR_INVALID_ARG;
}
uint32_t word = (addr - DR_REG_SLC_BASE) / 4;
if (word > INT8_MAX) {
return ESP_ERR_INVALID_ARG;
}
uint32_t window_command = word | (SLC_WIN_CMD_READ << SLC_WIN_CMD_S);
esp_err_t err = slave_slchost_reg_write(card, SLCHOST_WIN_CMD, window_command);
if (err != ESP_OK) {
return err;
}
return slave_slchost_reg_read(card, SLCHOST_STATE_W0, val);
}
static esp_err_t slave_slc_reg_write(sdmmc_card_t* card, uint32_t addr, uint32_t val)
{
if ((addr & DR_REG_SLC_MASK) != DR_REG_SLC_BASE) {
ESP_LOGW(TAG, "%s: invalid addr 0x%08x\n", __func__, addr);
return ESP_ERR_INVALID_ARG;
}
uint32_t word = (addr - DR_REG_SLC_BASE) / 4;
if (word > INT8_MAX) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = slave_slchost_reg_write(card, SLCHOST_CONF_W5, val);
if (err != ESP_OK) {
return err;
}
uint32_t window_command = word | (SLC_WIN_CMD_WRITE << SLC_WIN_CMD_S);
return slave_slchost_reg_write(card, SLCHOST_WIN_CMD, window_command);
}
/** Reset and put slave into download mode */
static void reset_slave(void)
{
const int pin_en = 18;
const int pin_io0 = 19;
gpio_config_t gpio_cfg = {
.pin_bit_mask = BIT64(pin_en) | BIT64(pin_io0),
.mode = GPIO_MODE_OUTPUT_OD,
};
TEST_ESP_OK(gpio_config(&gpio_cfg));
gpio_set_level(pin_en, 0);
gpio_set_level(pin_io0, 0);
vTaskDelay(10 / portTICK_PERIOD_MS);
gpio_set_level(pin_en, 1);
vTaskDelay(10 / portTICK_PERIOD_MS);
gpio_set_level(pin_io0, 1);
}
static void sdio_slave_common_init(sdmmc_card_t* card)
{
uint8_t card_cap;
esp_err_t err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_CARD_CAP, &card_cap);
TEST_ESP_OK(err);
printf("CAP: 0x%02x\n", card_cap);
uint8_t hs;
err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_HIGHSPEED, &hs);
TEST_ESP_OK(err);
printf("HS: 0x%02x\n", hs);
#define FUNC1_EN_MASK (BIT(1))
uint8_t ioe;
err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_ENABLE, &ioe);
TEST_ESP_OK(err);
printf("IOE: 0x%02x\n", ioe);
uint8_t ior = 0;
err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior);
TEST_ESP_OK(err);
printf("IOR: 0x%02x\n", ior);
// enable function 1
ioe |= FUNC1_EN_MASK;
err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_FN_ENABLE, ioe, NULL);
TEST_ESP_OK(err);
err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_ENABLE, &ioe);
TEST_ESP_OK(err);
printf("IOE: 0x%02x\n", ioe);
// wait for the card to become ready
while ( (ior & FUNC1_EN_MASK) == 0 ) {
err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior);
TEST_ESP_OK(err);
printf("IOR: 0x%02x\n", ior);
}
// get interrupt status
uint8_t ie;
err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_INT_ENABLE, &ie);
TEST_ESP_OK(err);
printf("IE: 0x%02x\n", ie);
// enable interrupts for function 1&2 and master enable
ie |= BIT(0) | FUNC1_EN_MASK;
err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_INT_ENABLE, ie, NULL);
TEST_ESP_OK(err);
err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_INT_ENABLE, &ie);
TEST_ESP_OK(err);
printf("IE: 0x%02x\n", ie);
}
/** Common for all SDIO devices, set block size for specific function */
static void sdio_slave_set_blocksize(sdmmc_card_t* card, int function, uint16_t bs)
{
const uint8_t* bs_u8 = (const uint8_t*) &bs;
uint16_t bs_read = 0;
uint8_t* bs_read_u8 = (uint8_t*) &bs_read;
uint32_t offset = SD_IO_FBR_START * function;
TEST_ESP_OK( sdmmc_io_write_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, bs_u8[0], NULL));
TEST_ESP_OK( sdmmc_io_write_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, bs_u8[1], NULL));
TEST_ESP_OK( sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0]));
TEST_ESP_OK( sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1]));
TEST_ASSERT_EQUAL_HEX16(bs, bs_read);
}
/**
* ESP32 ROM code does not set some SDIO slave registers to the defaults
* we need, this function clears/sets some bits.
*/
static void esp32_slave_init_extra(sdmmc_card_t* card)
{
printf("Initialize some ESP32 SDIO slave registers\n");
uint32_t reg_val;
TEST_ESP_OK( slave_slc_reg_read(card, SLCCONF1, &reg_val) );
reg_val &= ~(SLC_SLC0_RX_STITCH_EN | SLC_SLC0_TX_STITCH_EN);
TEST_ESP_OK( slave_slc_reg_write(card, SLCCONF1, reg_val) );
TEST_ESP_OK( slave_slc_reg_read(card, SLC0TX_LINK, &reg_val) );
reg_val |= SLC_SLC0_TXLINK_START;
TEST_ESP_OK( slave_slc_reg_write(card, SLC0TX_LINK, reg_val) );
}
/**
* ESP32 bootloader implements "SIP" protocol which can be used to exchange
* some commands, events, and data packets between the host and the slave.
* This function sends a SIP command, testing CMD53 block writes along the way.
*/
static void esp32_send_sip_command(sdmmc_card_t* card)
{
printf("Test block write using CMD53\n");
const size_t block_size = 512;
uint8_t* data = heap_caps_calloc(1, block_size, MALLOC_CAP_DMA);
struct sip_cmd_bootup {
uint32_t boot_addr;
uint32_t discard_link;
};
struct sip_cmd_write_reg {
uint32_t addr;
uint32_t val;
};
struct sip_hdr {
uint8_t fc[2];
uint16_t len;
uint32_t cmdid;
uint32_t seq;
};
struct sip_hdr* hdr = (struct sip_hdr*) data;
size_t len;
#define SEND_WRITE_REG_CMD
#ifdef SEND_WRITE_REG_CMD
struct sip_cmd_write_reg *write_reg = (struct sip_cmd_write_reg*) (data + sizeof(*hdr));
len = sizeof(*hdr) + sizeof(*write_reg);
hdr->cmdid = 3; /* SIP_CMD_WRITE_REG */
write_reg->addr = GPIO_ENABLE_W1TS_REG;
write_reg->val = BIT(0) | BIT(2) | BIT(4); /* Turn of RGB LEDs on WROVER-KIT */
#else
struct sip_cmd_bootup *bootup = (struct sip_cmd_bootup*) (data + sizeof(*hdr));
len = sizeof(*hdr) + sizeof(*bootup);
hdr->cmdid = 5; /* SIP_CMD_BOOTUP */
bootup->boot_addr = 0x4005a980; /* start_tb_console function in ROM */
bootup->discard_link = 1;
#endif
hdr->len = len;
TEST_ESP_OK( sdmmc_io_write_blocks(card, 1, SLC_THRESHOLD_ADDR - len, data, block_size) );
free(data);
}
static void test_cmd52_read_write_single_byte(sdmmc_card_t* card)
{
esp_err_t err;
printf("Write bytes to slave's W0_REG using CMD52\n");
const size_t scratch_area_reg = SLCHOST_CONF_W0 - DR_REG_SLCHOST_BASE;
const uint8_t test_byte_1 = 0xa5;
const uint8_t test_byte_2 = 0xb6;
// used to check Read-After-Write
uint8_t test_byte_1_raw;
uint8_t test_byte_2_raw;
uint8_t val = 0;
err = sdmmc_io_write_byte(card, 1, scratch_area_reg, test_byte_1, &test_byte_1_raw);
TEST_ESP_OK(err);
TEST_ASSERT_EQUAL_UINT8(test_byte_1, test_byte_1_raw);
err = sdmmc_io_write_byte(card, 1, scratch_area_reg + 1, test_byte_2, &test_byte_2_raw);
TEST_ESP_OK(err);
TEST_ASSERT_EQUAL_UINT8(test_byte_2, test_byte_2_raw);
printf("Read back bytes using CMD52\n");
TEST_ESP_OK(sdmmc_io_read_byte(card, 1, scratch_area_reg, &val));
TEST_ASSERT_EQUAL_UINT8(test_byte_1, val);
TEST_ESP_OK(sdmmc_io_read_byte(card, 1, scratch_area_reg + 1, &val));
TEST_ASSERT_EQUAL_UINT8(test_byte_2, val);
}
static void test_cmd53_read_write_multiple_bytes(sdmmc_card_t* card, size_t n_bytes)
{
printf("Write multiple bytes using CMD53\n");
const size_t scratch_area_reg = SLCHOST_CONF_W0 - DR_REG_SLCHOST_BASE;
uint8_t* src = heap_caps_malloc(512, MALLOC_CAP_DMA);
uint32_t* src_32 = (uint32_t*) src;
for (size_t i = 0; i < (n_bytes + 3) / 4; ++i) {
src_32[i] = rand();
}
TEST_ESP_OK(sdmmc_io_write_bytes(card, 1, scratch_area_reg, src, n_bytes));
ESP_LOG_BUFFER_HEX(TAG, src, n_bytes);
printf("Read back using CMD52\n");
uint8_t* dst = heap_caps_malloc(512, MALLOC_CAP_DMA);
for (size_t i = 0; i < n_bytes; ++i) {
TEST_ESP_OK(sdmmc_io_read_byte(card, 1, scratch_area_reg + i, &dst[i]));
}
ESP_LOG_BUFFER_HEX(TAG, dst, n_bytes);
TEST_ASSERT_EQUAL_UINT8_ARRAY(src, dst, n_bytes);
printf("Read back using CMD53\n");
TEST_ESP_OK(sdmmc_io_read_bytes(card, 1, scratch_area_reg, dst, n_bytes));
ESP_LOG_BUFFER_HEX(TAG, dst, n_bytes);
TEST_ASSERT_EQUAL_UINT8_ARRAY(src, dst, n_bytes);
free(src);
free(dst);
}
TEST_CASE("can probe and talk to ESP32 SDIO slave", "[sdio][ignore]")
{
reset_slave();
/* Probe */
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
config.flags = SDMMC_HOST_FLAG_1BIT;
config.max_freq_khz = SDMMC_FREQ_PROBING;
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
(sdmmc_host_init());
(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
TEST_ESP_OK(sdmmc_card_init(&config, card));
sdmmc_card_print_info(stdout, card);
/* Set up standard SDIO registers */
sdio_slave_common_init(card);
srand(0);
for (int repeat = 0; repeat < 4; ++repeat) {
test_cmd52_read_write_single_byte(card);
test_cmd53_read_write_multiple_bytes(card, 1);
test_cmd53_read_write_multiple_bytes(card, 2);
test_cmd53_read_write_multiple_bytes(card, 3);
test_cmd53_read_write_multiple_bytes(card, 4);
test_cmd53_read_write_multiple_bytes(card, 5);
test_cmd53_read_write_multiple_bytes(card, 23);
test_cmd53_read_write_multiple_bytes(card, 24);
}
sdio_slave_set_blocksize(card, 0, 512);
sdio_slave_set_blocksize(card, 1, 512);
esp32_slave_init_extra(card);
esp32_send_sip_command(card);
TEST_ESP_OK(sdmmc_host_deinit());
free(card);
}
#endif //SOC_SDMMC_HOST_SUPPORTED

@ -1 +1 @@
Subproject commit 43aac7074be0d14e72cb04eaddcddf1a926c6a74
Subproject commit ae73873b5cba0eb11c89165f4559964940430d44

View File

@ -1221,6 +1221,10 @@ msgstr ""
msgid "Insufficient encryption"
msgstr ""
#: ports/espressif/common-hal/wifi/Radio.c
msgid "Interface must be started"
msgstr ""
#: ports/atmel-samd/audio_dma.c ports/raspberrypi/audio_dma.c
msgid "Internal audio buffer too small"
msgstr ""
@ -2142,10 +2146,6 @@ msgstr ""
msgid "Stack size must be at least 256"
msgstr ""
#: ports/espressif/common-hal/wifi/Radio.c
msgid "Interface must be started"
msgstr ""
#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c
msgid "Stereo left must be on PWM channel A"
msgstr ""

12
main.c
View File

@ -158,7 +158,7 @@ STATIC void start_mp(supervisor_allocation *heap) {
gc_init(heap->ptr, heap->ptr + get_allocation_length(heap) / 4);
#endif
mp_init();
mp_obj_list_init(mp_sys_path, 0);
mp_obj_list_init((mp_obj_list_t *)mp_sys_path, 0);
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script)
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_));
// Frozen modules are in their own pseudo-dir, e.g., ".frozen".
@ -166,7 +166,7 @@ STATIC void start_mp(supervisor_allocation *heap) {
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_FROZEN_FAKE_DIR_QSTR));
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib));
mp_obj_list_init(mp_sys_argv, 0);
mp_obj_list_init((mp_obj_list_t *)mp_sys_argv, 0);
#if CIRCUITPY_ALARM
// Record which alarm woke us up, if any. An object may be created so the heap must be functional.
@ -797,6 +797,9 @@ int __attribute__((used)) main(void) {
supervisor_bluetooth_init();
#endif
// Start the debug serial
serial_early_init();
// Create a new filesystem only if we're not in a safe mode.
// A power brownout here could make it appear as if there's
// no SPI flash filesystem, and we might erase the existing one.
@ -805,9 +808,6 @@ int __attribute__((used)) main(void) {
// displays init after filesystem, since they could share the flash SPI
board_init();
// Start the debug serial
serial_early_init();
// Reset everything and prep MicroPython to run boot.py.
reset_port();
// Port-independent devices, like CIRCUITPY_BLEIO_HCI.
@ -905,7 +905,7 @@ void gc_collect(void) {
// This naively collects all object references from an approximate stack
// range.
gc_collect_root((void **)sp, ((uint32_t)port_stack_get_top() - sp) / sizeof(uint32_t));
gc_collect_root((void **)sp, ((mp_uint_t)port_stack_get_top() - sp) / sizeof(mp_uint_t));
gc_collect_end();
}

View File

@ -47,7 +47,7 @@ STATIC canio_can_obj_t *can_objs[MP_ARRAY_SIZE(can_insts)];
// This must be placed in the first 64kB of RAM
STATIC COMPILER_SECTION(".canram") canio_can_state_t can_state[MP_ARRAY_SIZE(can_insts)];
void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *tx, mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent) {
void common_hal_canio_can_construct(canio_can_obj_t *self, const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent) {
mcu_pin_function_t *tx_function = mcu_find_pin_function(can_tx, tx, -1, MP_QSTR_tx);
int instance = tx_function->instance;

View File

@ -59,7 +59,7 @@ static Sdhc *sdhc_insts[] = SDHC_INSTS;
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, mcu_pin_obj_t **data, uint32_t frequency) {
uint8_t num_data, const mcu_pin_obj_t **data, uint32_t frequency) {
/*
SD breakout as assembled ("*" = minimum viable set)

179
ports/broadcom/Makefile Normal file
View File

@ -0,0 +1,179 @@
# Select the board to build for.
BOARD=raspberrypi_pi4
ifeq ($(BOARD),)
$(error You must provide a BOARD parameter)
else
ifeq ($(wildcard boards/$(BOARD)/.),)
$(error Invalid BOARD specified)
endif
endif
# If the build directory is not given, make it reflect the board name.
BUILD ?= build-$(BOARD)
include ../../py/mkenv.mk
# Board-specific
include boards/$(BOARD)/mpconfigboard.mk
# Port-specific
include mpconfigport.mk
# CircuitPython-specific
include $(TOP)/py/circuitpy_mpconfig.mk
# qstr definitions (must come before including py.mk)
QSTR_DEFS = qstrdefsport.h
# include py core make definitions
include $(TOP)/py/py.mk
include $(TOP)/supervisor/supervisor.mk
# Include make rules and variables common across CircuitPython builds.
include $(TOP)/py/circuitpy_defns.mk
HAL_DIR=hal/$(MCU_SERIES)
INC += -I. \
-I../.. \
-I../../lib/mp-readline \
-I../../lib/timeutils \
-I../../lib/sdmmc/include \
-Iboards/$(BOARD) \
-Iboards/ \
-Iperipherals/ \
-I../../lib/tinyusb/src \
-I../../supervisor/shared/usb \
-I$(BUILD)
SRC_C += bindings/videocore/__init__.c \
bindings/videocore/Framebuffer.c \
boards/$(BOARD)/board.c \
boards/$(BOARD)/pins.c \
background.c \
common-hal/videocore/Framebuffer.c \
fatfs_port.c \
mphalport.c \
lib/sdmmc/sdmmc_cmd.c \
lib/sdmmc/sdmmc_common.c \
lib/sdmmc/sdmmc_init.c \
lib/sdmmc/sdmmc_io.c \
lib/sdmmc/sdmmc_mmc.c \
lib/sdmmc/sdmmc_sd.c \
lib/tinyusb/src/portable/synopsys/dwc2/dcd_dwc2.c \
peripherals/broadcom/caches.c \
peripherals/broadcom/gen/interrupt_handlers.c \
peripherals/broadcom/gpio.c \
peripherals/broadcom/interrupts.c \
peripherals/broadcom/mmu.c \
peripherals/broadcom/vcmailbox.c
SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \
$(addprefix shared-bindings/, $(SRC_BINDINGS_ENUMS)) \
$(addprefix common-hal/, $(SRC_COMMON_HAL))
SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \
$(addprefix shared-module/, $(SRC_SHARED_MODULE)) \
$(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL))
# There may be duplicates between SRC_COMMON_HAL_EXPANDED and SRC_SHARED_MODULE_EXPANDED,
# because a few modules have files both in common-hal/ and shared-modules/.
# Doing a $(sort ...) removes duplicates as part of sorting.
SRC_COMMON_HAL_SHARED_MODULE_EXPANDED = $(sort $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_MODULE_EXPANDED))
SRC_S = peripherals/broadcom/boot.s
OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o))
ifeq ($(INTERNAL_LIBM),1)
OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o))
endif
OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o))
# BCM CLFAGS
CFLAGS += -ffreestanding -nostartfiles -DMICROPY_HW_MCU_NAME="\"$(CHIP_VARIANT)\""
ifeq ($(CHIP_VARIANT), "bcm2711")
CFLAGS += -mcpu=cortex-a72 -DBCM_VERSION=2711
CROSS_COMPILE = aarch64-none-elf-
else
CFLAGS += -mcpu=cortex-a53 -DBCM_VERSION=2837
CROSS_COMPILE = aarch64-none-elf-
# TODO add 32-bit support for Cortex-A7 and ARM1176
endif
OPTIMIZATION_FLAGS ?= -O3
CFLAGS += $(OPTIMIZATION_FLAGS)
# TinyUSB defines
CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_BCM2711 -DCFG_TUD_MIDI_RX_BUFSIZE=512 \
-DCFG_TUD_CDC_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=512 \
-DCFG_TUD_CDC_TX_BUFSIZE=512 -DCFG_TUD_MSC_BUFSIZE=1024
#Debugging/Optimization
ifeq ($(DEBUG), 1)
CFLAGS += -ggdb3 -Og
# No LTO because we may place some functions in RAM instead of flash.
else
CFLAGS += -DNDEBUG -ggdb3
# No LTO because we may place some functions in RAM instead of flash.
ifdef CFLAGS_BOARD
CFLAGS += $(CFLAGS_BOARD)
endif
endif
CFLAGS += $(INC) -Wall -Werror -std=gnu11 $(BASE_CFLAGS) $(CFLAGS_MOD) $(COPT) $(DISABLE_WARNINGS)
SRC_QSTR += $(SRC_C) $(SRC_SUPERVISOR) $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_MODULE_EXPANDED)
LDFLAGS += $(CFLAGS) -T peripherals/broadcom/link.ld -Wl,--gc-sections -Wl,-Map=$@.map # -Wl,--cref
# Use toolchain libm if we're not using our own.
ifndef INTERNAL_LIBM
LIBS += -lm
endif
all: $(BUILD)/firmware.kernel8.img $(BUILD)/firmware.disk.img.zip
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD)/kernel8.elf: $(OBJ)
$(STEPECHO) "LINK $@"
$(Q)echo $(OBJ) > $(BUILD)/firmware.objs
$(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--start-group $(LIBS) -Wl,--end-group
$(BUILD)/kernel8.img: $(BUILD)/kernel8.elf
$(STEPECHO) "Create $@"
$(OBJCOPY) -O binary $(BUILD)/kernel8.elf $@
$(BUILD)/firmware.kernel8.img: $(BUILD)/kernel8.img
$(STEPECHO) "Create $@"
$(CP) $^ $@
$(BUILD)/firmware.disk.img.zip: $(BUILD)/kernel8.img
$(STEPECHO) "Create $@"
$(Q)dd if=/dev/zero of=$(BUILD)/circuitpython-disk.img bs=1 count=0 seek=256M
$(Q)parted -s $(BUILD)/circuitpython-disk.img mktable msdos
$(Q)parted -s $(BUILD)/circuitpython-disk.img mkpart primary fat32 0% 100%
$(Q)mkfs.fat -F 32 -n BOOT --offset=2048 $(BUILD)/circuitpython-disk.img
$(Q)mcopy -i $(BUILD)/circuitpython-disk.img@@1M config.txt firmware/bootcode.bin firmware/fixup* firmware/start* ::
$(Q)mcopy -i $(BUILD)/circuitpython-disk.img@@1M $(BUILD)/kernel8.img ::
$(Q)zip $@ $(BUILD)/circuitpython-disk.img
$(Q)rm $(BUILD)/circuitpython-disk.img
include $(TOP)/py/mkrules.mk
# Print out the value of a make variable.
# https://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile
print-%:
@echo $* = $($*)

View File

@ -0,0 +1,37 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "background.h"
#include "py/runtime.h"
#include "supervisor/port.h"
void port_start_background_task(void) {
}
void port_finish_background_task(void) {
}
void port_background_task(void) {
}

View File

@ -0,0 +1,32 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_BACKGROUND_H
#define MICROPY_INCLUDED_BROADCOM_BACKGROUND_H
#include <stdbool.h>
#endif // MICROPY_INCLUDED_BROADCOM_BACKGROUND_H

View File

@ -0,0 +1,197 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "py/obj.h"
#include "py/objproperty.h"
#include "py/runtime.h"
#include "py/objarray.h"
#include "bindings/videocore/Framebuffer.h"
#include "shared-bindings/util.h"
#include "shared-module/displayio/__init__.h"
#include "shared-module/framebufferio/__init__.h"
#include "shared-module/framebufferio/FramebufferDisplay.h"
//| class Framebuffer:
//| """A VideoCore managed frame buffer."""
//|
//| def __init__(self, width: int, height: int) -> None:
//| """Create a Framebuffer object with the given dimensions. Memory is
//| allocated outside of the heap in GPU memory.
//|
//| The framebuffer is in "ARGB8888" format.
//|
//| A Framebuffer is often used in conjunction with a
//| `framebufferio.FramebufferDisplay`."""
//|
STATIC mp_obj_t videocore_framebuffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_width, ARG_height, };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
videocore_framebuffer_obj_t *self = &allocate_display_bus_or_raise()->videocore;
self->base.type = &videocore_framebuffer_type;
if (args[ARG_width].u_int <= 0) {
mp_raise_ValueError(translate("width must be greater than zero"));
}
common_hal_videocore_framebuffer_construct(self,
args[ARG_width].u_int,
args[ARG_height].u_int);
return MP_OBJ_FROM_PTR(self);
}
//| def deinit(self) -> None:
//| """Free the resources (pins, timers, etc.) associated with this
//| rgbmatrix instance. After deinitialization, no further operations
//| may be performed."""
//| ...
//|
STATIC mp_obj_t videocore_framebuffer_deinit(mp_obj_t self_in) {
videocore_framebuffer_obj_t *self = (videocore_framebuffer_obj_t *)self_in;
common_hal_videocore_framebuffer_deinit(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(videocore_framebuffer_deinit_obj, videocore_framebuffer_deinit);
static void check_for_deinit(videocore_framebuffer_obj_t *self) {
if (common_hal_videocore_framebuffer_deinited(self)) {
raise_deinited_error();
}
}
//| width: int
//| """The width of the display, in pixels"""
//|
STATIC mp_obj_t videocore_framebuffer_get_width(mp_obj_t self_in) {
videocore_framebuffer_obj_t *self = (videocore_framebuffer_obj_t *)self_in;
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_videocore_framebuffer_get_width(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(videocore_framebuffer_get_width_obj, videocore_framebuffer_get_width);
const mp_obj_property_t videocore_framebuffer_width_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&videocore_framebuffer_get_width_obj,
MP_ROM_NONE,
MP_ROM_NONE},
};
//| height: int
//| """The height of the display, in pixels"""
//|
STATIC mp_obj_t videocore_framebuffer_get_height(mp_obj_t self_in) {
videocore_framebuffer_obj_t *self = (videocore_framebuffer_obj_t *)self_in;
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_videocore_framebuffer_get_height(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(videocore_framebuffer_get_height_obj, videocore_framebuffer_get_height);
const mp_obj_property_t videocore_framebuffer_height_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&videocore_framebuffer_get_height_obj,
MP_ROM_NONE,
MP_ROM_NONE},
};
STATIC const mp_rom_map_elem_t videocore_framebuffer_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&videocore_framebuffer_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&videocore_framebuffer_width_obj) },
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&videocore_framebuffer_height_obj) },
};
STATIC MP_DEFINE_CONST_DICT(videocore_framebuffer_locals_dict, videocore_framebuffer_locals_dict_table);
STATIC void videocore_framebuffer_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) {
common_hal_videocore_framebuffer_get_buffer(self_in, bufinfo, 0);
}
// These version exists so that the prototype matches the protocol,
// avoiding a type cast that can hide errors
STATIC void videocore_framebuffer_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) {
(void)dirty_row_bitmap;
common_hal_videocore_framebuffer_refresh(self_in);
}
STATIC void videocore_framebuffer_deinit_proto(mp_obj_t self_in) {
common_hal_videocore_framebuffer_deinit(self_in);
}
STATIC int videocore_framebuffer_get_width_proto(mp_obj_t self_in) {
return common_hal_videocore_framebuffer_get_width(self_in);
}
STATIC int videocore_framebuffer_get_height_proto(mp_obj_t self_in) {
return common_hal_videocore_framebuffer_get_height(self_in);
}
STATIC int videocore_framebuffer_get_color_depth_proto(mp_obj_t self_in) {
return 32;
}
STATIC int videocore_framebuffer_get_bytes_per_cell_proto(mp_obj_t self_in) {
return 1;
}
STATIC int videocore_framebuffer_get_native_frames_per_second_proto(mp_obj_t self_in) {
return 30;
}
STATIC int videocore_framebuffer_get_row_stride_proto(mp_obj_t self_in) {
return common_hal_videocore_framebuffer_get_row_stride(self_in);
}
STATIC const framebuffer_p_t videocore_framebuffer_proto = {
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer)
.get_bufinfo = videocore_framebuffer_get_bufinfo,
.get_width = videocore_framebuffer_get_width_proto,
.get_height = videocore_framebuffer_get_height_proto,
.get_color_depth = videocore_framebuffer_get_color_depth_proto,
.get_row_stride = videocore_framebuffer_get_row_stride_proto,
.get_bytes_per_cell = videocore_framebuffer_get_bytes_per_cell_proto,
.get_native_frames_per_second = videocore_framebuffer_get_native_frames_per_second_proto,
.swapbuffers = videocore_framebuffer_swapbuffers,
.deinit = videocore_framebuffer_deinit_proto,
};
const mp_obj_type_t videocore_framebuffer_type = {
{ &mp_type_type },
.flags = MP_TYPE_FLAG_EXTENDED,
.name = MP_QSTR_Framebuffer,
.locals_dict = (mp_obj_dict_t *)&videocore_framebuffer_locals_dict,
.make_new = videocore_framebuffer_make_new,
MP_TYPE_EXTENDED_FIELDS(
.buffer_p = { .get_buffer = common_hal_videocore_framebuffer_get_buffer, },
.protocol = &videocore_framebuffer_proto,
),
};

View File

@ -0,0 +1,40 @@
/*
* This file is part of the Micro Python 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.
*/
#pragma once
#include "common-hal/videocore/Framebuffer.h"
extern const mp_obj_type_t videocore_framebuffer_type;
void common_hal_videocore_framebuffer_construct(videocore_framebuffer_obj_t *self, uint16_t width, uint16_t height);
void common_hal_videocore_framebuffer_deinit(videocore_framebuffer_obj_t *self);
bool common_hal_videocore_framebuffer_deinited(videocore_framebuffer_obj_t *self);
void common_hal_videocore_framebuffer_refresh(videocore_framebuffer_obj_t *self);
int common_hal_videocore_framebuffer_get_width(videocore_framebuffer_obj_t *self);
int common_hal_videocore_framebuffer_get_height(videocore_framebuffer_obj_t *self);
int common_hal_videocore_framebuffer_get_row_stride(videocore_framebuffer_obj_t *self);
mp_int_t common_hal_videocore_framebuffer_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags);

View File

@ -0,0 +1,49 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 <stdint.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "bindings/videocore/Framebuffer.h"
//| """Low-level routines for interacting with the Broadcom VideoCore GPU"""
//|
STATIC const mp_rom_map_elem_t videocore_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_videocore) },
{ MP_ROM_QSTR(MP_QSTR_Framebuffer), MP_ROM_PTR(&videocore_framebuffer_type) },
};
STATIC MP_DEFINE_CONST_DICT(videocore_module_globals, videocore_module_globals_table);
const mp_obj_module_t videocore_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&videocore_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_videocore, videocore_module, CIRCUITPY_VIDEOCORE);

View File

@ -0,0 +1,53 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "supervisor/board.h"
#include "mpconfigboard.h"
#include "bindings/videocore/Framebuffer.h"
#include "shared-module/displayio/__init__.h"
#include "shared-bindings/framebufferio/FramebufferDisplay.h"
void board_init(void) {
videocore_framebuffer_obj_t *fb = &allocate_display_bus()->videocore;
fb->base.type = &videocore_framebuffer_type;
common_hal_videocore_framebuffer_construct(fb, 640, 480);
framebufferio_framebufferdisplay_obj_t *display = &displays[0].framebuffer_display;
display->base.type = &framebufferio_framebufferdisplay_type;
common_hal_framebufferio_framebufferdisplay_construct(
display,
MP_OBJ_FROM_PTR(fb),
0,
true);
}
bool board_requests_safe_mode(void) {
return false;
}
void reset_board(void) {
}

View File

@ -0,0 +1 @@
#define MICROPY_HW_BOARD_NAME "Raspberry Pi Compute Module 4 IO Board"

View File

@ -0,0 +1,6 @@
USB_VID = 0x2E8A
USB_PID = 0xf000
USB_PRODUCT = "Compute Module 4 IO Board"
USB_MANUFACTURER = "Raspberry Pi"
CHIP_VARIANT = "bcm2711"

View File

@ -0,0 +1,62 @@
#include "shared-bindings/board/__init__.h"
#include "shared-module/displayio/__init__.h"
STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
// These match the names used in Blinka
{ MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO0) },
{ MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) },
{ MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) },
{ MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO2) },
{ MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO3) },
{ MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO3) },
{ MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO4) },
{ MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) },
{ MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) },
{ MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) },
{ MP_ROM_QSTR(MP_QSTR_CE1), MP_ROM_PTR(&pin_GPIO7) },
{ MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) },
{ MP_ROM_QSTR(MP_QSTR_CE0), MP_ROM_PTR(&pin_GPIO8) },
{ MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) },
{ MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO9) },
{ MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) },
{ MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO10) },
{ MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO11) },
{ MP_ROM_QSTR(MP_QSTR_SCLK), MP_ROM_PTR(&pin_GPIO11) },
{ MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO11) },
{ MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO12) },
{ MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) },
{ MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO14) },
{ MP_ROM_QSTR(MP_QSTR_TXD), MP_ROM_PTR(&pin_GPIO14) },
{ MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO15) },
{ MP_ROM_QSTR(MP_QSTR_RXD), MP_ROM_PTR(&pin_GPIO15) },
{ MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO14) },
{ MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO15) },
{ MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO16) },
{ MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) },
{ MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) },
{ MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO19) },
{ MP_ROM_QSTR(MP_QSTR_MISO_1), MP_ROM_PTR(&pin_GPIO19) },
{ MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO20) },
{ MP_ROM_QSTR(MP_QSTR_MOSI_1), MP_ROM_PTR(&pin_GPIO20) },
{ MP_ROM_QSTR(MP_QSTR_D21), MP_ROM_PTR(&pin_GPIO21) },
{ MP_ROM_QSTR(MP_QSTR_SCLK_1), MP_ROM_PTR(&pin_GPIO21) },
{ MP_ROM_QSTR(MP_QSTR_SCK_1), MP_ROM_PTR(&pin_GPIO21) },
{ MP_ROM_QSTR(MP_QSTR_D22), MP_ROM_PTR(&pin_GPIO22) },
{ MP_ROM_QSTR(MP_QSTR_D23), MP_ROM_PTR(&pin_GPIO23) },
{ MP_ROM_QSTR(MP_QSTR_D24), MP_ROM_PTR(&pin_GPIO24) },
{ MP_ROM_QSTR(MP_QSTR_D25), MP_ROM_PTR(&pin_GPIO25) },
{ MP_ROM_QSTR(MP_QSTR_D26), MP_ROM_PTR(&pin_GPIO26) },
{ MP_ROM_QSTR(MP_QSTR_D27), MP_ROM_PTR(&pin_GPIO27) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)},
};
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);

View File

@ -0,0 +1,53 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "supervisor/board.h"
#include "mpconfigboard.h"
#include "bindings/videocore/Framebuffer.h"
#include "shared-module/displayio/__init__.h"
#include "shared-bindings/framebufferio/FramebufferDisplay.h"
void board_init(void) {
videocore_framebuffer_obj_t *fb = &allocate_display_bus()->videocore;
fb->base.type = &videocore_framebuffer_type;
common_hal_videocore_framebuffer_construct(fb, 640, 480);
framebufferio_framebufferdisplay_obj_t *display = &displays[0].framebuffer_display;
display->base.type = &framebufferio_framebufferdisplay_type;
common_hal_framebufferio_framebufferdisplay_construct(
display,
MP_OBJ_FROM_PTR(fb),
0,
true);
}
bool board_requests_safe_mode(void) {
return false;
}
void reset_board(void) {
}

View File

@ -0,0 +1 @@
#define MICROPY_HW_BOARD_NAME "Raspberry Pi 4B"

View File

@ -0,0 +1,6 @@
USB_VID = 0x2E8A
USB_PID = 0xF001
USB_PRODUCT = "Raspberry Pi 4B"
USB_MANUFACTURER = "Raspberry Pi"
CHIP_VARIANT = bcm2711

View File

@ -0,0 +1,62 @@
#include "shared-bindings/board/__init__.h"
#include "shared-module/displayio/__init__.h"
STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
// These match the names used in Blinka
{ MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO0) },
{ MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) },
{ MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) },
{ MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO2) },
{ MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO3) },
{ MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO3) },
{ MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO4) },
{ MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) },
{ MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) },
{ MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) },
{ MP_ROM_QSTR(MP_QSTR_CE1), MP_ROM_PTR(&pin_GPIO7) },
{ MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) },
{ MP_ROM_QSTR(MP_QSTR_CE0), MP_ROM_PTR(&pin_GPIO8) },
{ MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) },
{ MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO9) },
{ MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) },
{ MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO10) },
{ MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO11) },
{ MP_ROM_QSTR(MP_QSTR_SCLK), MP_ROM_PTR(&pin_GPIO11) },
{ MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO11) },
{ MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO12) },
{ MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) },
{ MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO14) },
{ MP_ROM_QSTR(MP_QSTR_TXD), MP_ROM_PTR(&pin_GPIO14) },
{ MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO15) },
{ MP_ROM_QSTR(MP_QSTR_RXD), MP_ROM_PTR(&pin_GPIO15) },
{ MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO14) },
{ MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO15) },
{ MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO16) },
{ MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) },
{ MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) },
{ MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO19) },
{ MP_ROM_QSTR(MP_QSTR_MISO_1), MP_ROM_PTR(&pin_GPIO19) },
{ MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO20) },
{ MP_ROM_QSTR(MP_QSTR_MOSI_1), MP_ROM_PTR(&pin_GPIO20) },
{ MP_ROM_QSTR(MP_QSTR_D21), MP_ROM_PTR(&pin_GPIO21) },
{ MP_ROM_QSTR(MP_QSTR_SCLK_1), MP_ROM_PTR(&pin_GPIO21) },
{ MP_ROM_QSTR(MP_QSTR_SCK_1), MP_ROM_PTR(&pin_GPIO21) },
{ MP_ROM_QSTR(MP_QSTR_D22), MP_ROM_PTR(&pin_GPIO22) },
{ MP_ROM_QSTR(MP_QSTR_D23), MP_ROM_PTR(&pin_GPIO23) },
{ MP_ROM_QSTR(MP_QSTR_D24), MP_ROM_PTR(&pin_GPIO24) },
{ MP_ROM_QSTR(MP_QSTR_D25), MP_ROM_PTR(&pin_GPIO25) },
{ MP_ROM_QSTR(MP_QSTR_D26), MP_ROM_PTR(&pin_GPIO26) },
{ MP_ROM_QSTR(MP_QSTR_D27), MP_ROM_PTR(&pin_GPIO27) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)},
};
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);

View File

@ -0,0 +1,53 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "supervisor/board.h"
#include "mpconfigboard.h"
#include "bindings/videocore/Framebuffer.h"
#include "shared-module/displayio/__init__.h"
#include "shared-bindings/framebufferio/FramebufferDisplay.h"
void board_init(void) {
videocore_framebuffer_obj_t *fb = &allocate_display_bus()->videocore;
fb->base.type = &videocore_framebuffer_type;
common_hal_videocore_framebuffer_construct(fb, 640, 480);
framebufferio_framebufferdisplay_obj_t *display = &displays[0].framebuffer_display;
display->base.type = &framebufferio_framebufferdisplay_type;
common_hal_framebufferio_framebufferdisplay_construct(
display,
MP_OBJ_FROM_PTR(fb),
0,
true);
}
bool board_requests_safe_mode(void) {
return false;
}
void reset_board(void) {
}

View File

@ -0,0 +1 @@
#define MICROPY_HW_BOARD_NAME "Raspberry Pi Zero 2W"

View File

@ -0,0 +1,6 @@
USB_VID = 0x2E8A
USB_PID = 0xF002
USB_PRODUCT = "Raspberry Pi Zero 2W"
USB_MANUFACTURER = "Raspberry Pi"
CHIP_VARIANT = rp3a0

View File

@ -0,0 +1,11 @@
#include "shared-bindings/board/__init__.h"
STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
{ MP_ROM_QSTR(MP_QSTR_GPIO18), MP_ROM_PTR(&pin_GPIO18) },
{ MP_ROM_QSTR(MP_QSTR_GPIO19), MP_ROM_PTR(&pin_GPIO19) },
{ MP_ROM_QSTR(MP_QSTR_GPIO20), MP_ROM_PTR(&pin_GPIO20) },
{ MP_ROM_QSTR(MP_QSTR_GPIO21), MP_ROM_PTR(&pin_GPIO21) },
};
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);

View File

@ -0,0 +1,34 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 <string.h>
#include "py/runtime.h"
#include "py/mphal.h"
#include "common-hal/microcontroller/Pin.h"
// Pins aren't actually defined here. They are in the board specific directory
// such as boards/arduino_zero/pins.c.

View File

@ -0,0 +1,108 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "py/mperrno.h"
#include "py/mphal.h"
#include "shared-bindings/busio/I2C.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/__init__.h"
#define NO_PIN 0xff
// One second
#define BUS_TIMEOUT_US 1000000
STATIC bool never_reset_i2c[2];
// STATIC i2c_inst_t *i2c[2] = {i2c0, i2c1};
void reset_i2c(void) {
for (size_t i = 0; i < 2; i++) {
if (never_reset_i2c[i]) {
continue;
}
}
}
void common_hal_busio_i2c_construct(busio_i2c_obj_t *self,
const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) {
}
bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) {
return self->sda_pin == NO_PIN;
}
void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) {
if (common_hal_busio_i2c_deinited(self)) {
return;
}
// never_reset_i2c[i2c_hw_index(self->peripheral)] = false;
reset_pin_number(self->sda_pin);
reset_pin_number(self->scl_pin);
self->sda_pin = NO_PIN;
self->scl_pin = NO_PIN;
}
bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) {
return common_hal_busio_i2c_write(self, addr, NULL, 0, true) == 0;
}
bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) {
bool grabbed_lock = false;
if (!self->has_lock) {
grabbed_lock = true;
self->has_lock = true;
}
return grabbed_lock;
}
bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) {
return self->has_lock;
}
void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) {
self->has_lock = false;
}
uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr,
const uint8_t *data, size_t len, bool transmit_stop_bit) {
return MP_ENODEV;
}
uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr,
uint8_t *data, size_t len) {
return MP_ENODEV;
}
void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) {
// never_reset_i2c[i2c_hw_index(self->peripheral)] = true;
never_reset_pin_number(self->scl_pin);
never_reset_pin_number(self->sda_pin);
}

View File

@ -0,0 +1,44 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_COMMON_HAL_BUSIO_I2C_H
#define MICROPY_INCLUDED_BROADCOM_COMMON_HAL_BUSIO_I2C_H
#include "common-hal/microcontroller/Pin.h"
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
bool has_lock;
uint baudrate;
uint8_t scl_pin;
uint8_t sda_pin;
} busio_i2c_obj_t;
void reset_i2c(void);
#endif // MICROPY_INCLUDED_BROADCOM_COMMON_HAL_BUSIO_I2C_H

View File

@ -0,0 +1,142 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "shared-bindings/busio/SPI.h"
#include "shared/runtime/interrupt_char.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "supervisor/board.h"
#include "common-hal/microcontroller/Pin.h"
#include "shared-bindings/microcontroller/Pin.h"
#define NO_INSTANCE 0xff
STATIC bool never_reset_spi[2];
void reset_spi(void) {
for (size_t i = 0; i < 2; i++) {
if (never_reset_spi[i]) {
continue;
}
// TODO
}
}
void common_hal_busio_spi_construct(busio_spi_obj_t *self,
const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi,
const mcu_pin_obj_t *miso) {
}
void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) {
// never_reset_spi[spi_get_index(self->peripheral)] = true;
common_hal_never_reset_pin(self->clock);
common_hal_never_reset_pin(self->MOSI);
common_hal_never_reset_pin(self->MISO);
}
bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) {
return self->clock == NULL;
}
void common_hal_busio_spi_deinit(busio_spi_obj_t *self) {
if (common_hal_busio_spi_deinited(self)) {
return;
}
// never_reset_spi[spi_get_index(self->peripheral)] = false;
common_hal_reset_pin(self->clock);
common_hal_reset_pin(self->MOSI);
common_hal_reset_pin(self->MISO);
self->clock = NULL;
}
bool common_hal_busio_spi_configure(busio_spi_obj_t *self,
uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
if (baudrate == self->target_frequency &&
polarity == self->polarity &&
phase == self->phase &&
bits == self->bits) {
return true;
}
// TODO
self->polarity = polarity;
self->phase = phase;
self->bits = bits;
self->target_frequency = baudrate;
self->real_frequency = baudrate; // TODO
return true;
}
bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) {
bool grabbed_lock = false;
if (!self->has_lock) {
grabbed_lock = true;
self->has_lock = true;
}
return grabbed_lock;
}
bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) {
return self->has_lock;
}
void common_hal_busio_spi_unlock(busio_spi_obj_t *self) {
self->has_lock = false;
}
bool common_hal_busio_spi_write(busio_spi_obj_t *self,
const uint8_t *data, size_t len) {
return false;
}
bool common_hal_busio_spi_read(busio_spi_obj_t *self,
uint8_t *data, size_t len, uint8_t write_value) {
return false;
}
bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len) {
return false;
}
uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) {
return self->real_frequency;
}
uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) {
return self->phase;
}
uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) {
return self->polarity;
}

View File

@ -0,0 +1,49 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_COMMON_HAL_BUSIO_SPI_H
#define MICROPY_INCLUDED_BROADCOM_COMMON_HAL_BUSIO_SPI_H
#include "common-hal/microcontroller/Pin.h"
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
bool has_lock;
const mcu_pin_obj_t *clock;
const mcu_pin_obj_t *MOSI;
const mcu_pin_obj_t *MISO;
uint32_t target_frequency;
int32_t real_frequency;
uint8_t polarity;
uint8_t phase;
uint8_t bits;
} busio_spi_obj_t;
void reset_spi(void);
#endif // MICROPY_INCLUDED_BROADCOM_COMMON_HAL_BUSIO_SPI_H

View File

@ -0,0 +1,308 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 microDev
*
* 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 "shared-bindings/busio/UART.h"
#include "py/stream.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "supervisor/shared/tick.h"
#include "shared/runtime/interrupt_char.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "peripherals/broadcom/cpu.h"
#include "peripherals/broadcom/defines.h"
#include "peripherals/broadcom/gpio.h"
#include "peripherals/broadcom/interrupts.h"
#include "peripherals/broadcom/vcmailbox.h"
#define NO_PIN 0xff
#define UART_INST(uart) (((uart) ? uart1 : uart0))
typedef enum {
STATUS_FREE = 0,
STATUS_BUSY,
STATUS_NEVER_RESET
} uart_status_t;
#define NUM_UARTS 2
static uart_status_t uart_status[NUM_UARTS];
void reset_uart(void) {
for (uint8_t num = 0; num < NUM_UARTS; num++) {
if (uart_status[num] == STATUS_BUSY) {
uart_status[num] = STATUS_FREE;
}
}
}
static busio_uart_obj_t *active_uarts[NUM_UARTS];
void UART1_IRQHandler(void) {
while (UART1->STAT_b.DATA_READY && ringbuf_num_empty(&active_uarts[1]->ringbuf) > 0) {
ringbuf_put(&active_uarts[1]->ringbuf, (uint8_t)UART1->IO_b.DATA);
}
// We couldn't read all pending data (overrun) so clear the FIFO so that the interrupt
// can finish.
if (UART1->STAT_b.DATA_READY) {
UART1->IIR_b.DATA_READY = 1;
}
}
void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) {
uart_status[self->uart_id] = STATUS_NEVER_RESET;
}
void common_hal_busio_uart_construct(busio_uart_obj_t *self,
const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx,
const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts,
const mcu_pin_obj_t *rs485_dir, bool rs485_invert,
uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop,
mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer,
bool sigint_enabled) {
if (bits > 8) {
mp_raise_ValueError(translate("Invalid word/bit length"));
}
if (receiver_buffer_size == 0) {
mp_raise_ValueError(translate("Invalid buffer size"));
}
if ((rs485_dir != NULL) || (rs485_invert)) {
mp_raise_NotImplementedError(translate("RS485 Not yet supported on this device"));
}
if (tx == &pin_GPIO14) {
if (rx == &pin_GPIO15) {
self->uart_id = 1;
}
}
self->rx_pin = rx;
self->tx_pin = tx;
if (rx != NULL) {
if (receiver_buffer != NULL) {
self->ringbuf = (ringbuf_t) { receiver_buffer, receiver_buffer_size };
} else {
// Initially allocate the UART's buffer in the long-lived part of the
// heap. UARTs are generally long-lived objects, but the "make long-
// lived" machinery is incapable of moving internal pointers like
// self->buffer, so do it manually. (However, as long as internal
// pointers like this are NOT moved, allocating the buffer
// in the long-lived pool is not strictly necessary)
// (This is a macro.)
if (!ringbuf_alloc(&self->ringbuf, receiver_buffer_size, true)) {
mp_raise_msg(&mp_type_MemoryError, translate("Failed to allocate RX buffer"));
}
}
}
if (self->uart_id == 1) {
active_uarts[1] = self;
AUX->ENABLES_b.UART_1 = true;
UART1->IER = 0;
UART1->CNTL = 0;
if (bits == 8) {
UART1->LCR_b.DATA_SIZE = UART1_LCR_DATA_SIZE_MODE_8BIT;
} else if (bits == 7) {
UART1->LCR_b.DATA_SIZE = UART1_LCR_DATA_SIZE_MODE_7BIT;
}
UART1->MCR = 0;
UART1->IER = 0;
// Clear interrupts
UART1->IIR = 0xff;
uint32_t source_clock = vcmailbox_get_clock_rate_measured(VCMAILBOX_CLOCK_CORE);
UART1->BAUD = ((source_clock / (baudrate * 8)) - 1);
if (tx != NULL) {
UART1->CNTL |= UART1_CNTL_TX_ENABLE_Msk;
gpio_set_pull(14, BP_PULL_NONE);
gpio_set_function(14, GPIO_GPFSEL1_FSEL14_TXD1);
}
if (rx != NULL) {
UART1->CNTL |= UART1_CNTL_RX_ENABLE_Msk;
gpio_set_pull(15, BP_PULL_NONE);
gpio_set_function(15, GPIO_GPFSEL1_FSEL15_RXD1);
}
UART1->IER_b.DATA_READY = true;
// Never disable this in case the SPIs are used. They can each be
// disabled at the peripheral itself.
BP_EnableIRQ(AUX_IRQn);
}
}
bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) {
return self->tx_pin == NULL && self->rx_pin == NULL;
}
void common_hal_busio_uart_deinit(busio_uart_obj_t *self) {
if (common_hal_busio_uart_deinited(self)) {
return;
}
if (self->uart_id == 1) {
UART1->IER_b.DATA_READY = false;
UART1->CNTL = 0;
AUX->ENABLES_b.UART_1 = false;
active_uarts[1] = NULL;
}
ringbuf_free(&self->ringbuf);
uart_status[self->uart_id] = STATUS_FREE;
common_hal_reset_pin(self->tx_pin);
common_hal_reset_pin(self->rx_pin);
common_hal_reset_pin(self->cts_pin);
common_hal_reset_pin(self->rts_pin);
self->tx_pin = NULL;
self->rx_pin = NULL;
self->cts_pin = NULL;
self->rts_pin = NULL;
}
// Write characters.
size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) {
if (self->tx_pin == NULL) {
mp_raise_ValueError(translate("No TX pin"));
}
if (self->uart_id == 1) {
COMPLETE_MEMORY_READS;
for (size_t i = 0; i < len; i++) {
// Wait for the FIFO to have space.
while (!UART1->STAT_b.TX_READY) {
RUN_BACKGROUND_TASKS;
}
UART1->IO = data[i];
}
COMPLETE_MEMORY_READS;
return len;
}
return 0;
}
// Read characters.
size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) {
if (self->rx_pin == NULL) {
mp_raise_ValueError(translate("No RX pin"));
}
if (len == 0) {
// Nothing to read.
return 0;
}
COMPLETE_MEMORY_READS;
// Prevent conflict with uart irq.
if (self->uart_id == 1) {
UART1->IER_b.DATA_READY = false;
}
// Copy as much received data as available, up to len bytes.
size_t total_read = ringbuf_get_n(&self->ringbuf, data, len);
// Check if we still need to read more data.
if (len > total_read) {
len -= total_read;
uint64_t start_ticks = supervisor_ticks_ms64();
// Busy-wait until timeout or until we've read enough chars.
while (len > 0 && (supervisor_ticks_ms64() - start_ticks < self->timeout_ms)) {
if (UART1->STAT_b.DATA_READY) {
// Read and advance.
data[total_read] = UART1->IO_b.DATA;
// Adjust the counters.
len--;
total_read++;
// Reset the timeout on every character read.
start_ticks = supervisor_ticks_ms64();
}
RUN_BACKGROUND_TASKS;
// Allow user to break out of a timeout with a KeyboardInterrupt.
if (mp_hal_is_interrupted()) {
break;
}
}
}
// Now that we've emptied the ringbuf some, fill it up with anything in the
// FIFO. This ensures that we'll empty the FIFO as much as possible and
// reset the interrupt when we catch up.
while (UART1->STAT_b.DATA_READY && ringbuf_num_empty(&self->ringbuf) > 0) {
ringbuf_put(&self->ringbuf, (uint8_t)UART1->IO_b.DATA);
}
// Re-enable irq.
if (self->uart_id == 1) {
UART1->IER_b.DATA_READY = true;
}
COMPLETE_MEMORY_READS;
if (total_read == 0) {
*errcode = EAGAIN;
return MP_STREAM_ERROR;
}
return total_read;
}
uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) {
return self->baudrate;
}
void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) {
self->baudrate = baudrate;
}
mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) {
return (mp_float_t)(self->timeout_ms / 1000.0L);
}
void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout) {
self->timeout_ms = timeout * 1000;
}
uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) {
return ringbuf_num_filled(&self->ringbuf);
}
void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) {
ringbuf_clear(&self->ringbuf);
}
bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) {
if (self->tx_pin == NULL) {
return false;
}
if (self->uart_id == 1) {
return UART1->STAT_b.TX_READY;
}
return false;
}

View File

@ -0,0 +1,47 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 microDev
*
* 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_COMMON_HAL_BUSIO_UART_H
#define MICROPY_INCLUDED_BROADCOM_COMMON_HAL_BUSIO_UART_H
#include "py/obj.h"
#include "py/ringbuf.h"
typedef struct {
mp_obj_base_t base;
const mcu_pin_obj_t *tx_pin;
const mcu_pin_obj_t *rx_pin;
const mcu_pin_obj_t *cts_pin;
const mcu_pin_obj_t *rts_pin;
uint8_t uart_id;
uint32_t baudrate;
uint32_t timeout_ms;
ringbuf_t ringbuf;
} busio_uart_obj_t;
extern void reset_uart(void);
#endif // MICROPY_INCLUDED_BROADCOM_COMMON_HAL_BUSIO_UART_H

View File

@ -0,0 +1 @@
// No busio module functions.

View File

@ -0,0 +1,163 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Scott Shawcroft 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 <stdint.h>
#include <string.h>
#include "py/runtime.h"
#include "py/mphal.h"
#include "common-hal/microcontroller/Pin.h"
#include "shared-bindings/digitalio/DigitalInOut.h"
#include "supervisor/shared/translate.h"
#include "peripherals/broadcom/gpio.h"
digitalinout_result_t common_hal_digitalio_digitalinout_construct(
digitalio_digitalinout_obj_t *self, const mcu_pin_obj_t *pin) {
claim_pin(pin);
self->pin = pin;
self->output = false;
self->open_drain = false;
return DIGITALINOUT_OK;
}
void common_hal_digitalio_digitalinout_never_reset(
digitalio_digitalinout_obj_t *self) {
never_reset_pin_number(self->pin->number);
}
bool common_hal_digitalio_digitalinout_deinited(digitalio_digitalinout_obj_t *self) {
return self->pin == NULL;
}
void common_hal_digitalio_digitalinout_deinit(digitalio_digitalinout_obj_t *self) {
if (common_hal_digitalio_digitalinout_deinited(self)) {
return;
}
reset_pin_number(self->pin->number);
self->pin = NULL;
}
void common_hal_digitalio_digitalinout_switch_to_input(
digitalio_digitalinout_obj_t *self, digitalio_pull_t pull) {
self->output = false;
// This also sets direction to input.
common_hal_digitalio_digitalinout_set_pull(self, pull);
}
digitalinout_result_t common_hal_digitalio_digitalinout_switch_to_output(
digitalio_digitalinout_obj_t *self, bool value,
digitalio_drive_mode_t drive_mode) {
const uint8_t pin = self->pin->number;
gpio_set_pull(pin, PULL_NONE);
self->output = true;
self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN;
// Pin direction is ultimately set in set_value. We don't need to do it here.
common_hal_digitalio_digitalinout_set_value(self, value);
return DIGITALINOUT_OK;
}
digitalio_direction_t common_hal_digitalio_digitalinout_get_direction(
digitalio_digitalinout_obj_t *self) {
return self->output ? DIRECTION_OUTPUT : DIRECTION_INPUT;
}
void common_hal_digitalio_digitalinout_set_value(
digitalio_digitalinout_obj_t *self, bool value) {
const uint8_t pin = self->pin->number;
if (self->open_drain && value) {
// If true and open-drain, set the direction -before- setting
// the pin value, to to avoid a high glitch on the pin before
// switching from output to input for open-drain.
gpio_set_function(pin, GPIO_FUNCTION_INPUT);
gpio_set_value(pin, value);
} else {
// Otherwise set the direction -after- setting the pin value,
// to avoid a glitch which might occur if the old value was
// different and the pin was previously set to input.
gpio_set_value(pin, value);
gpio_set_function(pin, GPIO_FUNCTION_OUTPUT);
}
}
bool common_hal_digitalio_digitalinout_get_value(
digitalio_digitalinout_obj_t *self) {
return gpio_get_value(self->pin->number);
}
digitalinout_result_t common_hal_digitalio_digitalinout_set_drive_mode(
digitalio_digitalinout_obj_t *self,
digitalio_drive_mode_t drive_mode) {
bool value = common_hal_digitalio_digitalinout_get_value(self);
self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN;
// True is implemented differently between modes so reset the value to make
// sure it's correct for the new mode.
if (value) {
common_hal_digitalio_digitalinout_set_value(self, value);
}
return DIGITALINOUT_OK;
}
digitalio_drive_mode_t common_hal_digitalio_digitalinout_get_drive_mode(
digitalio_digitalinout_obj_t *self) {
if (self->open_drain) {
return DRIVE_MODE_OPEN_DRAIN;
} else {
return DRIVE_MODE_PUSH_PULL;
}
}
void common_hal_digitalio_digitalinout_set_pull(
digitalio_digitalinout_obj_t *self, digitalio_pull_t pull) {
const uint8_t pin = self->pin->number;
BP_PULL_Enum bp_pull = BP_PULL_NONE;
if (pull == PULL_UP) {
bp_pull = BP_PULL_UP;
} else if (pull == PULL_DOWN) {
bp_pull = BP_PULL_DOWN;
}
gpio_set_pull(pin, bp_pull);
}
digitalio_pull_t common_hal_digitalio_digitalinout_get_pull(
digitalio_digitalinout_obj_t *self) {
uint32_t pin = self->pin->number;
if (self->output) {
mp_raise_AttributeError(translate("Cannot get pull while in output mode"));
return PULL_NONE;
} else {
if (gpio_get_pull(pin) == BP_PULL_UP) {
return PULL_UP;
} else if (gpio_get_pull(pin) == BP_PULL_DOWN) {
return PULL_DOWN;
}
}
return PULL_NONE;
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Scott Shawcroft 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_COMMON_HAL_DIGITALIO_DIGITALINOUT_H
#define MICROPY_INCLUDED_BROADCOM_COMMON_HAL_DIGITALIO_DIGITALINOUT_H
#include "common-hal/microcontroller/Pin.h"
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
const mcu_pin_obj_t *pin;
bool output;
bool open_drain;
} digitalio_digitalinout_obj_t;
#endif // MICROPY_INCLUDED_BROADCOM_COMMON_HAL_DIGITALIO_DIGITALINOUT_H

View File

@ -0,0 +1 @@
// No digitalio module functions.

View File

@ -0,0 +1,112 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "shared-bindings/microcontroller/Pin.h"
#include "peripherals/broadcom/gpio.h"
void reset_all_pins(void) {
}
void never_reset_pin_number(uint8_t pin_number) {
}
void reset_pin_number(uint8_t pin_number) {
gpio_set_function(pin_number, 0);
}
void common_hal_never_reset_pin(const mcu_pin_obj_t *pin) {
never_reset_pin_number(pin->number);
}
void common_hal_reset_pin(const mcu_pin_obj_t *pin) {
reset_pin_number(pin->number);
}
void claim_pin(const mcu_pin_obj_t *pin) {
// Nothing to do because all changes will set the GPIO settings.
}
bool pin_number_is_free(uint8_t pin_number) {
return true;
}
bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t *pin) {
return pin_number_is_free(pin->number);
}
uint8_t common_hal_mcu_pin_number(const mcu_pin_obj_t *pin) {
return pin->number;
}
void common_hal_mcu_pin_claim(const mcu_pin_obj_t *pin) {
return claim_pin(pin);
}
void common_hal_mcu_pin_reset_number(uint8_t pin_no) {
reset_pin_number(pin_no);
}
#define PIN(num) \
const mcu_pin_obj_t pin_GPIO##num = { \
{ &mcu_pin_type }, \
.number = num \
};
PIN(0)
PIN(1)
PIN(2)
PIN(3)
PIN(4)
PIN(5)
PIN(6)
PIN(7)
PIN(8)
PIN(9)
PIN(10)
PIN(11)
PIN(12)
PIN(13)
PIN(14)
PIN(15)
PIN(16)
PIN(17)
PIN(18)
PIN(19)
PIN(20)
PIN(21)
PIN(22)
PIN(23)
PIN(24)
PIN(25)
PIN(26)
PIN(27)
PIN(48)
PIN(49)
PIN(50)
PIN(51)
PIN(52)
PIN(53)

View File

@ -0,0 +1,87 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_COMMON_HAL_MICROCONTROLLER_PIN_H
#define MICROPY_INCLUDED_BROADCOM_COMMON_HAL_MICROCONTROLLER_PIN_H
#include <assert.h>
#include <stdint.h>
#include <py/obj.h>
typedef struct {
mp_obj_base_t base;
uint8_t number;
} mcu_pin_obj_t;
extern const mcu_pin_obj_t pin_GPIO0;
extern const mcu_pin_obj_t pin_GPIO1;
extern const mcu_pin_obj_t pin_GPIO2;
extern const mcu_pin_obj_t pin_GPIO3;
extern const mcu_pin_obj_t pin_GPIO4;
extern const mcu_pin_obj_t pin_GPIO5;
extern const mcu_pin_obj_t pin_GPIO6;
extern const mcu_pin_obj_t pin_GPIO7;
extern const mcu_pin_obj_t pin_GPIO8;
extern const mcu_pin_obj_t pin_GPIO9;
extern const mcu_pin_obj_t pin_GPIO10;
extern const mcu_pin_obj_t pin_GPIO11;
extern const mcu_pin_obj_t pin_GPIO12;
extern const mcu_pin_obj_t pin_GPIO13;
extern const mcu_pin_obj_t pin_GPIO14;
extern const mcu_pin_obj_t pin_GPIO15;
extern const mcu_pin_obj_t pin_GPIO16;
extern const mcu_pin_obj_t pin_GPIO17;
extern const mcu_pin_obj_t pin_GPIO18;
extern const mcu_pin_obj_t pin_GPIO19;
extern const mcu_pin_obj_t pin_GPIO20;
extern const mcu_pin_obj_t pin_GPIO21;
extern const mcu_pin_obj_t pin_GPIO22;
extern const mcu_pin_obj_t pin_GPIO23;
extern const mcu_pin_obj_t pin_GPIO24;
extern const mcu_pin_obj_t pin_GPIO25;
extern const mcu_pin_obj_t pin_GPIO26;
extern const mcu_pin_obj_t pin_GPIO27;
extern const mcu_pin_obj_t pin_GPIO28;
extern const mcu_pin_obj_t pin_GPIO29;
extern const mcu_pin_obj_t pin_GPIO48;
extern const mcu_pin_obj_t pin_GPIO49;
extern const mcu_pin_obj_t pin_GPIO50;
extern const mcu_pin_obj_t pin_GPIO51;
extern const mcu_pin_obj_t pin_GPIO52;
extern const mcu_pin_obj_t pin_GPIO53;
void reset_all_pins(void);
// reset_pin_number takes the pin number instead of the pointer so that objects don't
// need to store a full pointer.
void reset_pin_number(uint8_t pin_number);
void never_reset_pin_number(uint8_t pin_number);
void claim_pin(const mcu_pin_obj_t *pin);
bool pin_number_is_free(uint8_t pin_number);
#endif // MICROPY_INCLUDED_BROADCOM_COMMON_HAL_MICROCONTROLLER_PIN_H

View File

@ -0,0 +1,53 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "shared-bindings/microcontroller/Processor.h"
#include "peripherals/broadcom/vcmailbox.h"
float common_hal_mcu_processor_get_temperature(void) {
uint32_t temp = vcmailbox_get_temperature();
return temp / 1000.0f;
}
float common_hal_mcu_processor_get_voltage(void) {
return 1.2f;
}
uint32_t common_hal_mcu_processor_get_frequency(void) {
return vcmailbox_get_clock_rate_measured(VCMAILBOX_CLOCK_ARM);
}
void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) {
uint64_t serial = vcmailbox_get_serial_number();
for (size_t i = 0; i < sizeof(uint64_t); i++) {
raw_id[i] = (serial >> (64 - ((i + 1) * 8))) & 0xff;
}
}
mcu_reset_reason_t common_hal_mcu_processor_get_reset_reason(void) {
return RESET_REASON_UNKNOWN;
}

View File

@ -0,0 +1,39 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_COMMON_HAL_MICROCONTROLLER_PROCESSOR_H
#define MICROPY_INCLUDED_BROADCOM_COMMON_HAL_MICROCONTROLLER_PROCESSOR_H
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
// Stores no state currently.
} mcu_processor_obj_t;
#define COMMON_HAL_MCU_PROCESSOR_UID_LENGTH 8
#endif // MICROPY_INCLUDED_BROADCOM_COMMON_HAL_MICROCONTROLLER_PROCESSOR_H

View File

@ -0,0 +1,132 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/microcontroller/Processor.h"
#include "common-hal/microcontroller/__init__.h"
#include "peripherals/broadcom/interrupts.h"
#include "mphalport.h"
void common_hal_mcu_delay_us(uint32_t delay) {
mp_hal_delay_us(delay);
}
volatile uint32_t nesting_count = 0;
void common_hal_mcu_disable_interrupts(void) {
BP_DisableIRQs();
nesting_count++;
}
void common_hal_mcu_enable_interrupts(void) {
if (nesting_count == 0) {
// reset_into_safe_mode(LOCKING_ERROR);
}
nesting_count--;
if (nesting_count > 0) {
return;
}
BP_EnableIRQs();
}
void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
}
void common_hal_mcu_reset(void) {
}
// The singleton microcontroller.Processor object, bound to microcontroller.cpu
// It currently only has properties, and no state.
#if CIRCUITPY_PROCESSOR_COUNT > 1
static const mcu_processor_obj_t processor0 = {
.base = {
.type = &mcu_processor_type,
},
};
static const mcu_processor_obj_t processor1 = {
.base = {
.type = &mcu_processor_type,
},
};
static const mcu_processor_obj_t processor2 = {
.base = {
.type = &mcu_processor_type,
},
};
static const mcu_processor_obj_t processor3 = {
.base = {
.type = &mcu_processor_type,
},
};
const mp_rom_obj_tuple_t common_hal_multi_processor_obj = {
{&mp_type_tuple},
CIRCUITPY_PROCESSOR_COUNT,
{
MP_ROM_PTR(&processor0),
MP_ROM_PTR(&processor1),
MP_ROM_PTR(&processor2),
MP_ROM_PTR(&processor3)
}
};
#endif
const mcu_processor_obj_t common_hal_mcu_processor_obj = {
.base = {
.type = &mcu_processor_type,
},
};
#if CIRCUITPY_NVM && CIRCUITPY_INTERNAL_NVM_SIZE > 0
// The singleton nvm.ByteArray object.
const nvm_bytearray_obj_t common_hal_mcu_nvm_obj = {
.base = {
.type = &nvm_bytearray_type,
},
.len = CIRCUITPY_INTERNAL_NVM_SIZE,
.start_address = (uint8_t *)(CIRCUITPY_INTERNAL_NVM_START_ADDR)
};
#endif
#if CIRCUITPY_WATCHDOG
// The singleton watchdog.WatchDogTimer object.
watchdog_watchdogtimer_obj_t common_hal_mcu_watchdogtimer_obj = {
.base = {
.type = &watchdog_watchdogtimer_type,
},
.timeout = 0.0f,
.mode = WATCHDOGMODE_NONE,
};
#endif
// This maps MCU pin names to pin objects.
const mp_rom_map_elem_t mcu_pin_global_dict_table[TOTAL_GPIO_COUNT] = {
};
MP_DEFINE_CONST_DICT(mcu_pin_globals, mcu_pin_global_dict_table);

View File

@ -0,0 +1,32 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_COMMON_HAL_MICROCONTROLLER___INIT___H
#define MICROPY_INCLUDED_BROADCOM_COMMON_HAL_MICROCONTROLLER___INIT___H
#define TOTAL_GPIO_COUNT 10
#endif // MICROPY_INCLUDED_BROADCOM_COMMON_HAL_MICROCONTROLLER___INIT___H

View File

@ -0,0 +1,62 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Scott Shawcroft 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 "genhdr/mpversion.h"
#include "py/mpconfig.h"
#include "py/objstr.h"
#include "py/objtuple.h"
#include "py/qstr.h"
STATIC const qstr os_uname_info_fields[] = {
MP_QSTR_sysname, MP_QSTR_nodename,
MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine
};
STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, MICROPY_HW_MCU_NAME);
STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, MICROPY_HW_MCU_NAME);
STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING);
STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE);
STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME);
STATIC MP_DEFINE_ATTRTUPLE(
os_uname_info_obj,
os_uname_info_fields,
5,
(mp_obj_t)&os_uname_info_sysname_obj,
(mp_obj_t)&os_uname_info_nodename_obj,
(mp_obj_t)&os_uname_info_release_obj,
(mp_obj_t)&os_uname_info_version_obj,
(mp_obj_t)&os_uname_info_machine_obj
);
mp_obj_t common_hal_os_uname(void) {
return (mp_obj_t)&os_uname_info_obj;
}
bool common_hal_os_urandom(uint8_t *buffer, uint32_t length) {
return false;
}

View File

@ -0,0 +1,367 @@
/*
* 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 "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);
gpio_set_function(command->number, GPIO_GPFSEL4_FSEL49_SD1_CMD);
gpio_set_pull(command->number, BP_PULL_UP);
gpio_set_function(data[0]->number, GPIO_GPFSEL5_FSEL50_SD1_DAT0);
gpio_set_pull(data[0]->number, BP_PULL_UP);
gpio_set_function(data[1]->number, GPIO_GPFSEL5_FSEL51_SD1_DAT1);
gpio_set_pull(data[1]->number, BP_PULL_UP);
gpio_set_function(data[2]->number, GPIO_GPFSEL5_FSEL52_SD1_DAT2);
gpio_set_pull(data[2]->number, BP_PULL_UP);
gpio_set_function(data[3]->number, GPIO_GPFSEL5_FSEL53_SD1_DAT3);
gpio_set_pull(data[3]->number, BP_PULL_UP);
} 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_card_init(&self->host_info, &self->card_info);
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_for_deinit(sdioio_sdcard_obj_t *self) {
}
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) {
check_for_deinit(self);
check_whole_block(bufinfo);
self->state_programming = true;
// mp_printf(&mp_plat_print, "write %d %d %d %d\n", start_block, bufinfo->len / 512, self->card_info.csd.capacity, self->card_info.csd.sector_size);
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) {
check_for_deinit(self);
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) {
check_for_deinit(self);
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;
}
void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self) {
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Scott Shawcroft
*
* 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.
*/
#pragma once
#include "lib/sdmmc/include/sdmmc_types.h"
typedef struct {
mp_obj_base_t base;
sdmmc_host_t host_info;
sdmmc_card_t card_info;
uint32_t frequency;
uint32_t capacity;
uint8_t num_data;
bool state_programming;
bool has_lock;
uint8_t command_pin;
uint8_t clock_pin;
uint8_t data_pins[4];
} sdioio_sdcard_obj_t;

View File

@ -0,0 +1,37 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Michael Schroeder
*
* 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/supervisor/Runtime.h"
#include "supervisor/serial.h"
bool common_hal_supervisor_runtime_get_serial_connected(void) {
return (bool)serial_connected();
}
bool common_hal_supervisor_runtime_get_serial_bytes_available(void) {
return (bool)serial_bytes_available();
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Michael Schroeder
*
* 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_COMMON_HAL_SUPERVISOR_RUNTIME_H
#define MICROPY_INCLUDED_BROADCOM_COMMON_HAL_SUPERVISOR_RUNTIME_H
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
// Stores no state currently.
} super_runtime_obj_t;
#endif // MICROPY_INCLUDED_BROADCOM_COMMON_HAL_SUPERVISOR_RUNTIME_H

View File

@ -0,0 +1,40 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Michael Schroeder
*
* 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 "py/obj.h"
#include "shared-bindings/supervisor/__init__.h"
#include "shared-bindings/supervisor/Runtime.h"
// The singleton supervisor.Runtime object, bound to supervisor.runtime
// It currently only has properties, and no state.
const super_runtime_obj_t common_hal_supervisor_runtime_obj = {
.base = {
.type = &supervisor_runtime_type,
},
};

View File

@ -0,0 +1,68 @@
#include "bindings/videocore/Framebuffer.h"
#include "py/gc.h"
#include "py/runtime.h"
#include "peripherals/broadcom/caches.h"
#include "peripherals/broadcom/vcmailbox.h"
void common_hal_videocore_framebuffer_construct(videocore_framebuffer_obj_t *self,
uint16_t width, uint16_t height) {
// These will be modified on success to indicate actual values.
uint32_t virtual_width = width;
uint32_t virtual_height = height;
uint32_t physical_width = width;
// TODO: Make the FB twice as tall if double buffering.
uint32_t physical_height = height;
uint32_t pitch = 0;
self->framebuffer = vcmailbox_get_framebuffer(&virtual_width, &virtual_height, &physical_width, &physical_height, &pitch);
if (self->framebuffer == NULL) {
if (gc_alloc_possible()) {
mp_raise_ValueError(translate("no fb"));
} else {
mp_printf(&mp_plat_print, "no fb\n");
}
self->width = 0;
self->height = 0;
return;
}
mp_printf(&mp_plat_print, "%dx%d pitch %d\n", virtual_width, virtual_height, pitch);
self->width = virtual_width;
self->height = virtual_height;
self->pitch = pitch;
}
void common_hal_videocore_framebuffer_deinit(videocore_framebuffer_obj_t *self) {
if (vcmailbox_release_framebuffer()) {
self->framebuffer = NULL;
}
}
bool common_hal_videocore_framebuffer_deinited(videocore_framebuffer_obj_t *self) {
return self->framebuffer == NULL;
}
void common_hal_videocore_framebuffer_refresh(videocore_framebuffer_obj_t *self) {
data_clean(self->framebuffer, sizeof(uint32_t) * self->width * self->height);
}
int common_hal_videocore_framebuffer_get_width(videocore_framebuffer_obj_t *self) {
return self->width;
}
int common_hal_videocore_framebuffer_get_height(videocore_framebuffer_obj_t *self) {
return self->height;
}
mp_int_t common_hal_videocore_framebuffer_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
videocore_framebuffer_obj_t *self = (videocore_framebuffer_obj_t *)self_in;
bufinfo->buf = self->framebuffer;
bufinfo->typecode = 'L';
bufinfo->len = self->pitch * self->height * sizeof(uint32_t);
return 0;
}
int common_hal_videocore_framebuffer_get_row_stride(videocore_framebuffer_obj_t *self) {
return self->pitch;
}

View File

@ -0,0 +1,38 @@
#pragma once
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 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 "py/obj.h"
typedef struct {
mp_obj_base_t base;
uint32_t *framebuffer;
uint16_t width;
uint16_t height;
uint16_t pitch;
bool doublebuffer;
} videocore_framebuffer_obj_t;

View File

@ -0,0 +1,9 @@
# dtparam=audio=on
# dtoverlay=dwc2
# core_freq_min=500
gpio=22-27=np
enable_jtag_gpio=1
# hdmi_group=1
# hdmi_mode=16
gpu_mem=16

View File

@ -0,0 +1,48 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George
*
* 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 "py/mphal.h"
#include "py/runtime.h"
#include "lib/oofatfs/ff.h" /* FatFs lower layer API */
#include "lib/oofatfs/diskio.h" /* FatFs lower layer API */
#include "shared/timeutils/timeutils.h"
#if CIRCUITPY_RTC
#include "shared-bindings/rtc/RTC.h"
#endif
DWORD get_fattime(void) {
#if CIRCUITPY_RTC
timeutils_struct_time_t tm;
common_hal_rtc_get_time(&tm);
return ((tm.tm_year - 1980) << 25) | (tm.tm_mon << 21) | (tm.tm_mday << 16) |
(tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1);
#else
return ((2016 - 1980) << 25) | ((9) << 21) | ((1) << 16) | ((16) << 11) | ((43) << 5) | (35 / 2);
#endif
}

@ -0,0 +1 @@
Subproject commit 6a5207946edcd45813d1dd1572ca8bd8101b68b6

View File

@ -0,0 +1,62 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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.
*/
#ifndef __INCLUDED_MPCONFIGPORT_H
#define __INCLUDED_MPCONFIGPORT_H
// Definitions that control circuitpy_mpconfig.h:
////////////////////////////////////////////////////////////////////////////////////////////////////
#define CIRCUITPY_MCU_FAMILY broadcom
#define MICROPY_PY_SYS_PLATFORM "BROADCOM"
#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1)
#define MICROPY_PY_FUNCTION_ATTRS (1)
#define MICROPY_OBJ_REPR (0)
#define CIRCUITPY_DEFAULT_STACK_SIZE (0x10000)
#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (1920)
#define CIRCUITPY_PROCESSOR_COUNT (4)
#define MICROPY_FF_MKFS_FAT32 (1)
#define MICROPY_FATFS_EXFAT (1)
////////////////////////////////////////////////////////////////////////////////////////////////////
// This also includes mpconfigboard.h.
#include "py/circuitpy_mpconfig.h"
// Definitions that can be overridden by mpconfigboard.h:
////////////////////////////////////////////////////////////////////////////////////////////////////
#define MICROPY_PORT_ROOT_POINTERS \
CIRCUITPY_COMMON_ROOT_POINTERS
#define DEBUG_UART_TX (&pin_GPIO14)
#define DEBUG_UART_RX (&pin_GPIO15)
#endif // __INCLUDED_MPCONFIGPORT_H

View File

@ -0,0 +1,58 @@
# Typically the first module to create
CIRCUITPY_MICROCONTROLLER = 1
# Typically the second module to create
CIRCUITPY_DIGITALIO = 1
# Other modules:
CIRCUITPY_ANALOGIO = 0
CIRCUITPY_BUSIO = 1
CIRCUITPY_ONEWIREIO = 0
CIRCUITPY_PWMIO = 0
CIRCUITPY_COUNTIO = 0
CIRCUITPY_NEOPIXEL_WRITE = 0
CIRCUITPY_PULSEIO = 0
CIRCUITPY_OS = 1
CIRCUITPY_NVM = 0
CIRCUITPY_AUDIOBUSIO = 0
CIRCUITPY_AUDIOIO = 0
CIRCUITPY_ROTARYIO = 0
CIRCUITPY_RTC = 0
CIRCUITPY_SDCARDIO = 0
CIRCUITPY_SDIOIO = 1
CIRCUITPY_FRAMEBUFFERIO = 1
CIRCUITPY_FREQUENCYIO = 0
CIRCUITPY_I2CPERIPHERAL = 0
# Requires SPI, PulseIO (stub ok):
CIRCUITPY_DISPLAYIO = 1
# These modules are implemented in shared-module/ - they can be included in
# any port once their prerequisites in common-hal are complete.
# Requires DigitalIO:
CIRCUITPY_BITBANGIO = 1
# Requires DigitalIO
CIRCUITPY_GAMEPAD = 0
# Requires neopixel_write or SPI (dotstar)
CIRCUITPY_PIXELBUF = 0
# Requires OS
CIRCUITPY_RANDOM = 1
# Requires OS, filesystem
CIRCUITPY_STORAGE = 1
# Requires Microcontroller
CIRCUITPY_TOUCHIO = 0
# Requires USB
CIRCUITPY_USB = 1
CIRCUITPY_USB_MSC = 1
CIRCUITPY_USB_HID = 1
CIRCUITPY_USB_MIDI = 1
USB_NUM_ENDPOINT_PAIRS = 8
# Does nothing without I2C
CIRCUITPY_REQUIRE_I2C_PULLUPS = 0
# No requirements, but takes extra flash
CIRCUITPY_ULAB = 0
CIRCUITPY_VIDEOCORE = 1
CIRCUITPY_ERRNO = 0
CIRCUITPY_FULL_BUILD = 0
INTERNAL_FLASH_FILESYSTEM = 1

View File

@ -0,0 +1,34 @@
#include <stdio.h>
#include <stdint.h>
#include "py/mpconfig.h"
#include "py/obj.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "mphalport.h"
void mp_hal_delay_us(mp_uint_t delay) {
}
void mp_hal_disable_all_interrupts(void) {
common_hal_mcu_disable_interrupts();
}
void mp_hal_enable_all_interrupts(void) {
common_hal_mcu_enable_interrupts();
}
mp_uint_t cpu_get_regs_and_sp(mp_uint_t *regs) {
size_t sp = 0;
__asm__ ("mov %[out], sp" : [out] "=r" (sp));
__asm__ ("mov %[out], x19" : [out] "=r" (regs[0]));
__asm__ ("mov %[out], x20" : [out] "=r" (regs[1]));
__asm__ ("mov %[out], x21" : [out] "=r" (regs[2]));
__asm__ ("mov %[out], x22" : [out] "=r" (regs[3]));
__asm__ ("mov %[out], x23" : [out] "=r" (regs[4]));
__asm__ ("mov %[out], x24" : [out] "=r" (regs[5]));
__asm__ ("mov %[out], x25" : [out] "=r" (regs[6]));
__asm__ ("mov %[out], x26" : [out] "=r" (regs[7]));
__asm__ ("mov %[out], x27" : [out] "=r" (regs[8]));
__asm__ ("mov %[out], x28" : [out] "=r" (regs[9]));
return sp;
}

View File

@ -0,0 +1,26 @@
#include <stdint.h>
#include "py/obj.h"
void mp_hal_delay_ms(mp_uint_t ms);
void mp_hal_delay_us(mp_uint_t us);
mp_uint_t mp_hal_ticks_cpu(void);
mp_uint_t mp_hal_ticks_us(void);
mp_uint_t mp_hal_ticks_ms(void);
void mp_hal_set_interrupt_char(int c);
int mp_hal_stdin_rx_chr(void);
void mp_hal_stdout_tx_strn(const char *str, size_t len);
typedef enum std_io_t {
MINI_UART = 0,
UART,
UART_QEMU
} std_io_t;
void uart_init(std_io_t interface);
#ifdef MICROPY_HW_USBHOST
#include "usbkbd.h"
void usbkbd_setup();
#endif

@ -0,0 +1 @@
Subproject commit 764ea35db106d2d4a2fe2f827a65485eded42e00

View File

@ -0,0 +1,31 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Damien P. George
*
* 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.
*/
// qstrs specific to this port, only needed if they aren't auto-generated
// Entries for sys.path
Q(/sd)
Q(/sd/lib)

View File

@ -0,0 +1,179 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 "supervisor/flash.h"
#include "supervisor/internal_flash.h"
#include <string.h>
#include "shared-bindings/digitalio/DigitalInOut.h"
#include "shared-bindings/sdioio/SDCard.h"
STATIC sdioio_sdcard_obj_t sd;
STATIC uint32_t first_lba = 0;
STATIC uint32_t sector_count = 0;
STATIC bool inited = false;
void supervisor_flash_init(void) {
if (inited) {
return;
}
inited = true;
#if BCM_VERSION != 2711
const mcu_pin_obj_t *data_pins[4] = {
&pin_GPIO50,
&pin_GPIO51,
&pin_GPIO52,
&pin_GPIO53,
};
common_hal_sdioio_sdcard_construct(&sd,
&pin_GPIO48, &pin_GPIO49,
4, data_pins, 8000000);
#else
common_hal_sdioio_sdcard_construct(&sd,
NULL, NULL,
0, NULL, 8000000);
#endif
uint32_t buffer[512 / sizeof(uint32_t)];
mp_buffer_info_t bufinfo;
bufinfo.len = 512;
bufinfo.buf = buffer;
common_hal_sdioio_sdcard_readblocks(&sd, 0, &bufinfo);
size_t partition_count = 0;
size_t first_free_sector = 0;
for (size_t i = 0; i < 4; i++) {
uint32_t part_first_lba = 0;
uint32_t part_sector_count = 0;
for (size_t j = 0; j < 16; j++) {
size_t offset = 446 + i * 16 + j;
uint8_t value = ((uint8_t *)buffer)[offset];
// Little endian
if (j >= 8 && j < 12) {
part_first_lba |= value << (8 * (j - 8));
} else if (j >= 12) {
part_sector_count |= value << (8 * (j - 12));
}
}
if (part_sector_count > 0) {
partition_count += 1;
first_free_sector = part_first_lba + part_sector_count;
}
// If the second partition exists, then use it (possibly after reformatting it.)
if (i == 1 && part_sector_count > 0 && part_first_lba > 0) {
sector_count = part_sector_count;
first_lba = part_first_lba;
} else if (i > 1 && part_sector_count > 0) {
sector_count = 0;
}
}
// Special case where most of the card is unused and the first and only
// partition is likely the CP boot partition. (It is 255MB starting at 1MB)
size_t sectors_per_mb = 2048;
if (partition_count == 1 && first_free_sector == 256 * sectors_per_mb) {
size_t total_sectors = common_hal_sdioio_sdcard_get_count(&sd);
// Align to a MB
total_sectors = total_sectors - (total_sectors % sectors_per_mb);
// Don't use the last MB because the imager erases it. GPT tables put a
// backup there sometimes.
total_sectors -= sectors_per_mb;
size_t new_sector_count = total_sectors - first_free_sector;
// Modify the MBR
size_t offset = 446 + 1 * 16;
uint8_t *part = ((uint8_t *)buffer) + offset;
part[0] = 0; // not boot
part[1] = 0xff; // Old unused CHS sizing
part[2] = 0xff;
part[3] = 0xff;
uint8_t type = 0x0C; // FAT32 with LBA
if (new_sector_count >= 0x4000000) {
type = 0x07; // Exfat when 32GB+
}
part[4] = type;
part[5] = 0xff; // Old unused CHS sizing
part[6] = 0xff;
part[7] = 0xff;
part[8] = first_free_sector & 0xff;
part[9] = (first_free_sector >> 8) & 0xff;
part[10] = (first_free_sector >> 16) & 0xff;
part[11] = (first_free_sector >> 24) & 0xff;
part[12] = new_sector_count & 0xff;
part[13] = (new_sector_count >> 8) & 0xff;
part[14] = (new_sector_count >> 16) & 0xff;
part[15] = (new_sector_count >> 24) & 0xff;
// Write back the modified MBR with the new partition. CircuitPython can
// detect a previously formatted FAT filesystem later if only the MBR and
// BOOT partition was updated.
if (common_hal_sdioio_sdcard_writeblocks(&sd, 0, &bufinfo) == 0) {
sector_count = new_sector_count;
first_lba = first_free_sector;
}
}
}
uint32_t supervisor_flash_get_block_size(void) {
return 512;
}
uint32_t supervisor_flash_get_block_count(void) {
return sector_count;
}
void port_internal_flash_flush(void) {
}
mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) {
mp_buffer_info_t bufinfo;
bufinfo.len = 512;
for (size_t i = 0; i < num_blocks; i++) {
bufinfo.buf = dest + 512 * i;
if (common_hal_sdioio_sdcard_readblocks(&sd, first_lba + block + i, &bufinfo) != 0) {
return 1; // error
}
}
return 0; // success
}
mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block, uint32_t num_blocks) {
mp_buffer_info_t bufinfo;
bufinfo.len = 512;
for (size_t i = 0; i < num_blocks; i++) {
bufinfo.buf = (uint8_t *)src + 512 * i;
if (common_hal_sdioio_sdcard_writeblocks(&sd, first_lba + block + i, &bufinfo) != 0) {
return 1; // error
}
}
return 0; // success
}
void supervisor_flash_release_cache(void) {
}

View File

@ -0,0 +1,33 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_INTERNAL_FLASH_H
#define MICROPY_INCLUDED_BROADCOM_INTERNAL_FLASH_H
#include <stdbool.h>
#include "mpconfigport.h"
#endif // MICROPY_INCLUDED_BROADCOM_INTERNAL_FLASH_H

View File

@ -0,0 +1,31 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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.
*/
#ifndef MICROPY_INCLUDED_BROADCOM_INTERNAL_FLASH_ROOT_POINTERS_H
#define MICROPY_INCLUDED_BROADCOM_INTERNAL_FLASH_ROOT_POINTERS_H
#define FLASH_ROOT_POINTERS
#endif // MICROPY_INCLUDED_BROADCOM_INTERNAL_FLASH_ROOT_POINTERS_H

View File

@ -0,0 +1,199 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft 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 <string.h>
#include <stdlib.h>
#include "supervisor/board.h"
#include "supervisor/port.h"
#include "genhdr/mpversion.h"
// #include "common-hal/rtc/RTC.h"
#include "common-hal/busio/I2C.h"
#include "common-hal/busio/SPI.h"
#include "common-hal/busio/UART.h"
#include "peripherals/broadcom/caches.h"
#include "peripherals/broadcom/cpu.h"
#include "peripherals/broadcom/defines.h"
#include "peripherals/broadcom/interrupts.h"
#include "peripherals/broadcom/mmu.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "supervisor/shared/safe_mode.h"
#include "supervisor/shared/stack.h"
#include "supervisor/shared/tick.h"
#include "peripherals/broadcom/vcmailbox.h"
// #include "tusb.h"
extern volatile bool mp_msc_enabled;
STATIC uint32_t board_revision;
safe_mode_t port_init(void) {
board_revision = vcmailbox_get_board_revision();
setup_mmu_flat_map();
init_caches();
// Set brown out.
// Reset everything into a known state before board_init.
reset_port();
// Initialize RTC
// common_hal_rtc_init();
// For the tick.
// hardware_alarm_claim(0);
// hardware_alarm_set_callback(0, _tick_callback);
// Check brownout.
if (board_requests_safe_mode()) {
return USER_SAFE_MODE;
}
return NO_SAFE_MODE;
}
void reset_port(void) {
#if CIRCUITPY_BUSIO
reset_i2c();
reset_spi();
reset_uart();
#endif
#if CIRCUITPY_PWMIO
pwmout_reset();
#endif
#if CIRCUITPY_RTC
rtc_reset();
#endif
#if CIRCUITPY_AUDIOPWMIO
audiopwmout_reset();
#endif
#if CIRCUITPY_AUDIOCORE
audio_dma_reset();
#endif
// reset_all_pins();
}
void reset_to_bootloader(void) {
// reset_usb_boot(0, 0);
while (true) {
}
}
void reset_cpu(void) {
while (true) {
}
}
bool port_has_fixed_stack(void) {
return true;
}
uint32_t *port_stack_get_limit(void) {
return (uint32_t *)0x4;
}
uint32_t *port_stack_get_top(void) {
return (uint32_t *)0x80000;
}
// From the linker script
extern uint32_t __bss_end;
uint32_t *port_heap_get_bottom(void) {
return &__bss_end;
}
uint32_t *port_heap_get_top(void) {
// 32M heap
return (uint32_t *)(((uint64_t)&__bss_end) + 32 * 1024 * 1024);
}
void port_set_saved_word(uint32_t value) {
// NOTE: This doesn't survive pressing the reset button (aka toggling RUN).
// watchdog_hw->scratch[0] = value;
}
uint32_t port_get_saved_word(void) {
// return watchdog_hw->scratch[0];
return 0;
}
uint64_t port_get_raw_ticks(uint8_t *subticks) {
COMPLETE_MEMORY_READS;
uint64_t hi = SYSTMR->CHI;
uint64_t lo = SYSTMR->CLO;
if (hi != SYSTMR->CHI) {
hi = SYSTMR->CHI;
lo = SYSTMR->CLO;
}
COMPLETE_MEMORY_READS;
uint64_t microseconds = hi << 32 | lo;
return 1024 * (microseconds / 1000000) + (microseconds % 1000000) / 977;
}
void TIMER_1_IRQHandler(void) {
SYSTMR->C1 += 977;
SYSTMR->CS_b.M1 = 1;
supervisor_tick();
}
// Enable 1/1024 second tick.
void port_enable_tick(void) {
COMPLETE_MEMORY_READS;
SYSTMR->CS_b.M1 = 1;
SYSTMR->C1 = SYSTMR->CLO + 977;
BP_EnableIRQ(TIMER_1_IRQn);
}
// Disable 1/1024 second tick.
void port_disable_tick(void) {
BP_DisableIRQ(TIMER_1_IRQn);
}
void port_interrupt_after_ticks(uint32_t ticks) {
}
void port_idle_until_interrupt(void) {
common_hal_mcu_disable_interrupts();
// if (!tud_task_event_ready()) {
// // asm volatile ("dsb 0xF":::"memory");
// // __wfi();
// }
common_hal_mcu_enable_interrupts();
}

View File

@ -0,0 +1,48 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017, 2018 Scott Shawcroft 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 "lib/tinyusb/src/device/usbd.h"
#include "py/runtime.h"
#include "supervisor/background_callback.h"
#include "supervisor/usb.h"
#include "peripherals/broadcom/interrupts.h"
#include "peripherals/broadcom/vcmailbox.h"
uint32_t SystemCoreClock = 700 * 1000 * 1000;
void USB_IRQHandler(void) {
usb_irq_handler();
}
void init_usb_hardware(void) {
vcmailbox_set_power_state(VCMAILBOX_DEVICE_USB_HCD, true);
BP_SetPriority(USB_IRQn, 0x00);
BP_ClearPendingIRQ(USB_IRQn);
BP_EnableIRQ(USB_IRQn);
BP_EnableIRQs();
}

View File

@ -38,7 +38,7 @@
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, mcu_pin_obj_t **data, uint32_t frequency) {
uint8_t num_data, const mcu_pin_obj_t **data, uint32_t frequency) {
struct geometry geo;
if (clock->number != PIN_SDIO_CLK || command->number != PIN_SDIO_CMD) {

View File

@ -105,7 +105,7 @@ STATIC twai_timing_config_t get_t_config(int baudrate) {
}
}
void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *tx, mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent) {
void common_hal_canio_can_construct(canio_can_obj_t *self, const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent) {
#define DIV_ROUND(a, b) (((a) + (b) / 2) / (b))
#define DIV_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
if (reserved_can) {

View File

@ -44,7 +44,7 @@
*/
void common_hal_paralleldisplay_parallelbus_construct_nonsequential(paralleldisplay_parallelbus_obj_t *self,
uint8_t n_pins, mcu_pin_obj_t **data_pins,
uint8_t n_pins, const mcu_pin_obj_t **data_pins,
const mcu_pin_obj_t *command, const mcu_pin_obj_t *chip_select,
const mcu_pin_obj_t *write, const mcu_pin_obj_t *read, const mcu_pin_obj_t *reset, uint32_t frequency) {
@ -118,7 +118,7 @@ void common_hal_paralleldisplay_parallelbus_construct(paralleldisplay_parallelbu
const mcu_pin_obj_t *data0, const mcu_pin_obj_t *command, const mcu_pin_obj_t *chip_select,
const mcu_pin_obj_t *write, const mcu_pin_obj_t *read, const mcu_pin_obj_t *reset, uint32_t frequency) {
char buf[7];
mcu_pin_obj_t *data_pins[8];
const mcu_pin_obj_t *data_pins[8];
for (int i = 0; i < 8; i++) {
snprintf(buf, sizeof(buf), "GPIO%d", data0->number + i);
data_pins[i] = validate_obj_is_free_pin(mp_obj_dict_get(MP_OBJ_FROM_PTR(&mcu_pin_globals), mp_obj_new_str(buf, strlen(buf))));

View File

@ -201,22 +201,22 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n
mp_get_buffer(args[ARG_init].u_obj, &init_bufinfo, MP_BUFFER_READ);
// We don't validate pin in use here because we may be ok sharing them within a PIO.
mcu_pin_obj_t *first_out_pin = validate_obj_is_pin_or_none(args[ARG_first_out_pin].u_obj);
const mcu_pin_obj_t *first_out_pin = validate_obj_is_pin_or_none(args[ARG_first_out_pin].u_obj);
if (args[ARG_out_pin_count].u_int < 1) {
mp_raise_ValueError(translate("Pin count must be at least 1"));
}
mcu_pin_obj_t *first_in_pin = validate_obj_is_pin_or_none(args[ARG_first_in_pin].u_obj);
const mcu_pin_obj_t *first_in_pin = validate_obj_is_pin_or_none(args[ARG_first_in_pin].u_obj);
if (args[ARG_in_pin_count].u_int < 1) {
mp_raise_ValueError(translate("Pin count must be at least 1"));
}
mcu_pin_obj_t *first_set_pin = validate_obj_is_pin_or_none(args[ARG_first_set_pin].u_obj);
const mcu_pin_obj_t *first_set_pin = validate_obj_is_pin_or_none(args[ARG_first_set_pin].u_obj);
if (args[ARG_set_pin_count].u_int < 1) {
mp_raise_ValueError(translate("Pin count must be at least 1"));
}
if (args[ARG_set_pin_count].u_int > 5) {
mp_raise_ValueError(translate("Set pin count must be between 1 and 5"));
}
mcu_pin_obj_t *first_sideset_pin = validate_obj_is_pin_or_none(args[ARG_first_sideset_pin].u_obj);
const mcu_pin_obj_t *first_sideset_pin = validate_obj_is_pin_or_none(args[ARG_first_sideset_pin].u_obj);
if (args[ARG_sideset_pin_count].u_int < 1) {
mp_raise_ValueError(translate("Pin count must be at least 1"));
}

View File

@ -32,9 +32,9 @@ bool common_hal_rp2pio_pins_are_sequential(size_t len, mp_obj_t *items) {
if (len == 0) {
return true;
}
mcu_pin_obj_t *last_pin = validate_obj_is_pin(items[0]);
const mcu_pin_obj_t *last_pin = validate_obj_is_pin(items[0]);
for (int i = 1; i < len; i++) {
mcu_pin_obj_t *pin = validate_obj_is_pin(items[i]);
const mcu_pin_obj_t *pin = validate_obj_is_pin(items[i]);
if (pin->number != last_pin->number + 1) {
return false;
}

View File

@ -50,7 +50,7 @@ STATIC const mcu_periph_obj_t *find_pin_function(const mcu_periph_obj_t *table,
}
void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *tx, mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent) {
void common_hal_canio_can_construct(canio_can_obj_t *self, const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent) {
#define DIV_ROUND(a, b) (((a) + (b) / 2) / (b))
#define DIV_ROUND_UP(a, b) (((a) + (b) - 1) / (b))

View File

@ -51,7 +51,7 @@ STATIC const mcu_periph_obj_t *find_pin_function(const mcu_periph_obj_t *table,
// 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, mcu_pin_obj_t **data) {
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);
@ -122,7 +122,7 @@ STATIC int check_pins(sdioio_sdcard_obj_t *self,
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, mcu_pin_obj_t **data, uint32_t frequency) {
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];

View File

@ -483,7 +483,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
// afterwards.
gc_collect();
}
if (outer_module_obj != MP_OBJ_NULL && VERIFY_PTR(outer_module_obj)) {
if (outer_module_obj != MP_OBJ_NULL && VERIFY_PTR(MP_OBJ_TO_PTR(outer_module_obj))) {
qstr s = qstr_from_strn(mod_str + last, i - last);
mp_store_attr(outer_module_obj, s, module_obj);
// The above store can cause a dictionary rehash and new allocation. So,

View File

@ -206,6 +206,9 @@ endif
ifeq ($(CIRCUITPY_I2CPERIPHERAL),1)
SRC_PATTERNS += i2cperipheral/%
endif
ifeq ($(CIRCUITPY_IMAGECAPTURE),1)
SRC_PATTERNS += imagecapture/%
endif
ifeq ($(CIRCUITPY_IPADDRESS),1)
SRC_PATTERNS += ipaddress/%
endif
@ -332,6 +335,9 @@ endif
ifeq ($(CIRCUITPY_USTACK),1)
SRC_PATTERNS += ustack/%
endif
ifeq ($(CIRCUITPY_VIDEOCORE),1)
SRC_PATTERNS += videocore/%
endif
ifeq ($(CIRCUITPY_WATCHDOG),1)
SRC_PATTERNS += watchdog/%
endif
@ -341,9 +347,6 @@ endif
ifeq ($(CIRCUITPY_PEW),1)
SRC_PATTERNS += _pew/%
endif
ifeq ($(CIRCUITPY_IMAGECAPTURE),1)
SRC_PATTERNS += imagecapture/%
endif
ifeq ($(CIRCUITPY_MSGPACK),1)
SRC_PATTERNS += msgpack/%
endif

View File

@ -38,7 +38,9 @@
#define CIRCUITPY 1
// REPR_C encodes qstrs, 31-bit ints, and 30-bit floats in a single 32-bit word.
#ifndef MICROPY_OBJ_REPR
#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C)
#endif
// options to control how MicroPython is built
// TODO(tannewt): Reduce this number if we want the REPL to function under 512
@ -168,14 +170,17 @@ extern void common_hal_mcu_enable_interrupts(void);
// Track stack usage. Expose results via ustack module.
#define MICROPY_MAX_STACK_USAGE (0)
// This port is intended to be 32-bit, but unfortunately, int32_t for
// different targets may be defined in different ways - either as int
// or as long. This requires different printf formatting specifiers
// to print such value. So, we avoid int32_t and use int directly.
#define UINT_FMT "%u"
#define INT_FMT "%d"
#ifdef __LP64__
typedef long mp_int_t; // must be pointer size
typedef unsigned long mp_uint_t; // must be pointer size
#else
// These are definitions for machines where sizeof(int) == sizeof(void*),
// regardless of actual size.
typedef int mp_int_t; // must be pointer size
typedef unsigned mp_uint_t; // must be pointer size
typedef unsigned int mp_uint_t; // must be pointer size
#endif
#if __GNUC__ >= 10 // on recent gcc versions we can check that this is so
_Static_assert(sizeof(mp_int_t) == sizeof(void *));
_Static_assert(sizeof(mp_uint_t) == sizeof(void *));
@ -275,8 +280,15 @@ typedef long mp_off_t;
#ifndef CIRCUITPY_DISPLAY_LIMIT
#define CIRCUITPY_DISPLAY_LIMIT (1)
#endif
// Framebuffer area size in bytes. Rounded down to power of four for alignment.
#ifndef CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE
#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (128)
#endif
#else
#define CIRCUITPY_DISPLAY_LIMIT (0)
#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (0)
#endif
#if CIRCUITPY_GAMEPADSHIFT
@ -357,82 +369,6 @@ extern const struct _mp_obj_module_t nvm_module;
#define MICROPY_PORT_BUILTIN_MODULES_STRONG_LINKS
// The following modules are defined in their respective __init__.c file in the
// shared-bindings directory using MP_REGISTER_MODULE.
//
// CIRCUITPY_AESIO
// CIRCUITPY_ANALOGIO
// CIRCUITPY_ATEXIT
// CIRCUITPY_AUDIOBUSIO
// CIRCUITPY_AUDIOCORE
// CIRCUITPY_AUDIOIO
// CIRCUITPY_AUDIOMIXER
// CIRCUITPY_AUDIOMP3
// CIRCUITPY_AUDIOPWMIO
// CIRCUITPY_BITBANGIO
// CIRCUITPY_BITMAPTOOLS
// CIRCUITPY_BITOPS
// CIRCUITPY_BLEIO
// CIRCUITPY_BOARD
// CIRCUITPY_BUSDEVICE
// CIRCUITPY_BUSIO
// CIRCUITPY_CAMERA
// CIRCUITPY_CANIO
// CIRCUITPY_COUNTIO
// CIRCUITPY_DIGITALIO
// CIRCUITPY_DISPLAYIO
// CIRCUITPY_DUALBANK
// CIRCUITPY__EVE
// CIRCUITPY_FONTIO
// CIRCUITPY_FRAMEBUFFERIO
// CIRCUITPY_FREQUENCYIO
// CIRCUITPY_GAMEPADSHIFT
// CIRCUITPY_GETPASS
// CIRCUITPY_GNSS
// CIRCUITPY_I2CPERIPHERAL
// CIRCUITPY_IMAGECAPTURE
// CIRCUITPY_IPADDRESS
// CIRCUITPY_KEYPAD
// CIRCUITPY_MATH
// CIRCUITPY_MEMORYMONITOR
// CIRCUITPY_MICROCONTROLLER
// CIRCUITPY_MSGPACK
// CIRCUITPY_NEOPIXEL_WRITE
// CIRCUITPY_ONEWIREIO_WRITE
// CIRCUITPY_PARALLELDISPLAY
// CIRCUITPY_PEW
// CIRCUITPY_PIXELBUF
// CIRCUITPY_PS2IO
// CIRCUITPY_PULSEIO
// CIRCUITPY_PWMIO
// CIRCUITPY_QRIO
// CIRCUITPY_RAINBOWIO
// CIRCUITPY_RANDOM
// CIRCUITPY_RGBMATRIX
// CIRCUITPY_ROTARYIO
// CIRCUITPY_RTC
// CIRCUITPY_SDCARDIO
// CIRCUITPY_SDIOIO
// CIRCUITPY_SHARPDISPLAY
// CIRCUITPY_SOCKETPOOL
// CIRCUITPY_SSL
// CIRCUITPY_STAGE
// CIRCUITPY_STORAGE
// CIRCUITPY_STRUCT
// CIRCUITPY_SUPERVISOR
// CIRCUITPY_SYNTHIO
// CIRCUITPY_TERMINALIO
// CIRCUITPY_TOUCHIO
// CIRCUITPY_TRACEBACK
// CIRCUITPY_UHEAP
// CIRCUITPY_USB_CDC
// CIRCUITPY_USB_HID
// CIRCUITPY_USB_MIDI
// CIRCUITPY_USTACK
// CIRCUITPY_VECTORIO
// CIRCUITPY_WATCHDOG
// CIRCUITPY_WIFI
// If weak links are enabled, just include strong links in the main list of modules,
// and also include the underscore alternate names.
#if MICROPY_MODULE_WEAK_LINKS

Some files were not shown because too many files have changed in this diff Show More