Merge pull request #5610 from tannewt/rpi
Initial broadcom port for Raspberry Pi
This commit is contained in:
commit
95c09ac66f
129
.github/workflows/build.yml
vendored
129
.github/workflows/build.yml
vendored
@ -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 || git log --parents HEAD~4..
|
||||
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'))
|
||||
|
7
.github/workflows/create_website_pr.yml
vendored
7
.github/workflows/create_website_pr.yml
vendored
@ -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
|
||||
|
10
.github/workflows/ports_windows.yml
vendored
10
.github/workflows/ports_windows.yml
vendored
@ -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
|
||||
|
2
.github/workflows/pre-commit.yml
vendored
2
.github/workflows/pre-commit.yml
vendored
@ -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
8
.gitmodules
vendored
@ -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
|
||||
|
2
conf.py
2
conf.py
@ -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",
|
||||
|
@ -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) {
|
||||
|
@ -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": [
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
9
lib/sdmmc/CMakeLists.txt
Normal 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
0
lib/sdmmc/component.mk
Normal file
274
lib/sdmmc/include/sdmmc_cmd.h
Normal file
274
lib/sdmmc/include/sdmmc_cmd.h
Normal 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
|
498
lib/sdmmc/include/sdmmc_defs.h
Normal file
498
lib/sdmmc/include/sdmmc_defs.h
Normal 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_
|
199
lib/sdmmc/include/sdmmc_types.h
Normal file
199
lib/sdmmc/include/sdmmc_types.h
Normal 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
501
lib/sdmmc/sdmmc_cmd.c
Normal 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
314
lib/sdmmc/sdmmc_common.c
Normal 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
132
lib/sdmmc/sdmmc_common.h
Normal 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
123
lib/sdmmc/sdmmc_init.c
Normal 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
633
lib/sdmmc/sdmmc_io.c
Normal 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
251
lib/sdmmc/sdmmc_mmc.c
Normal 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
343
lib/sdmmc/sdmmc_sd.c
Normal 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;
|
||||
}
|
4
lib/sdmmc/test/CMakeLists.txt
Normal file
4
lib/sdmmc/test/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
PRIV_INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES cmock sdmmc
|
||||
)
|
1
lib/sdmmc/test/component.mk
Normal file
1
lib/sdmmc/test/component.mk
Normal 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
584
lib/sdmmc/test/test_sd.c
Normal 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
388
lib/sdmmc/test/test_sdio.c
Normal 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, ®_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, ®_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
|
@ -1222,6 +1222,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 ""
|
||||
@ -2151,10 +2155,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
12
main.c
@ -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.
|
||||
@ -909,7 +909,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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
179
ports/broadcom/Makefile
Normal 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 $* = $($*)
|
37
ports/broadcom/background.c
Normal file
37
ports/broadcom/background.c
Normal 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) {
|
||||
}
|
32
ports/broadcom/background.h
Normal file
32
ports/broadcom/background.h
Normal 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
|
193
ports/broadcom/bindings/videocore/Framebuffer.c
Normal file
193
ports/broadcom/bindings/videocore/Framebuffer.c
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
mp_uint_t width = (mp_uint_t)mp_arg_validate_int_min(args[ARG_width].u_int, 0, MP_QSTR_width);
|
||||
mp_uint_t height = (mp_uint_t)mp_arg_validate_int_min(args[ARG_height].u_int, 0, MP_QSTR_height);
|
||||
common_hal_videocore_framebuffer_construct(self, width, height);
|
||||
|
||||
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 versions exist 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,
|
||||
),
|
||||
};
|
40
ports/broadcom/bindings/videocore/Framebuffer.h
Normal file
40
ports/broadcom/bindings/videocore/Framebuffer.h
Normal 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, mp_uint_t width, mp_uint_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);
|
49
ports/broadcom/bindings/videocore/__init__.c
Normal file
49
ports/broadcom/bindings/videocore/__init__.c
Normal 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);
|
53
ports/broadcom/boards/raspberrypi_cm4io/board.c
Normal file
53
ports/broadcom/boards/raspberrypi_cm4io/board.c
Normal 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) {
|
||||
}
|
4
ports/broadcom/boards/raspberrypi_cm4io/mpconfigboard.h
Normal file
4
ports/broadcom/boards/raspberrypi_cm4io/mpconfigboard.h
Normal file
@ -0,0 +1,4 @@
|
||||
#define MICROPY_HW_BOARD_NAME "Raspberry Pi Compute Module 4 IO Board"
|
||||
|
||||
#define DEFAULT_I2C_BUS_SCL (&pin_GPIO3)
|
||||
#define DEFAULT_I2C_BUS_SDA (&pin_GPIO2)
|
6
ports/broadcom/boards/raspberrypi_cm4io/mpconfigboard.mk
Normal file
6
ports/broadcom/boards/raspberrypi_cm4io/mpconfigboard.mk
Normal 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"
|
63
ports/broadcom/boards/raspberrypi_cm4io/pins.c
Normal file
63
ports/broadcom/boards/raspberrypi_cm4io/pins.c
Normal file
@ -0,0 +1,63 @@
|
||||
#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_I2C), MP_ROM_PTR(&board_i2c_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)},
|
||||
};
|
||||
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);
|
53
ports/broadcom/boards/raspberrypi_pi4b/board.c
Normal file
53
ports/broadcom/boards/raspberrypi_pi4b/board.c
Normal 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) {
|
||||
}
|
4
ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.h
Normal file
4
ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.h
Normal file
@ -0,0 +1,4 @@
|
||||
#define MICROPY_HW_BOARD_NAME "Raspberry Pi 4B"
|
||||
|
||||
#define DEFAULT_I2C_BUS_SCL (&pin_GPIO3)
|
||||
#define DEFAULT_I2C_BUS_SDA (&pin_GPIO2)
|
6
ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.mk
Normal file
6
ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.mk
Normal file
@ -0,0 +1,6 @@
|
||||
USB_VID = 0x2E8A
|
||||
USB_PID = 0xF001
|
||||
USB_PRODUCT = "Raspberry Pi 4B"
|
||||
USB_MANUFACTURER = "Raspberry Pi"
|
||||
|
||||
CHIP_VARIANT = bcm2711
|
63
ports/broadcom/boards/raspberrypi_pi4b/pins.c
Normal file
63
ports/broadcom/boards/raspberrypi_pi4b/pins.c
Normal file
@ -0,0 +1,63 @@
|
||||
#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_I2C), MP_ROM_PTR(&board_i2c_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)},
|
||||
};
|
||||
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);
|
53
ports/broadcom/boards/raspberrypi_zero2w/board.c
Normal file
53
ports/broadcom/boards/raspberrypi_zero2w/board.c
Normal 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) {
|
||||
}
|
4
ports/broadcom/boards/raspberrypi_zero2w/mpconfigboard.h
Normal file
4
ports/broadcom/boards/raspberrypi_zero2w/mpconfigboard.h
Normal file
@ -0,0 +1,4 @@
|
||||
#define MICROPY_HW_BOARD_NAME "Raspberry Pi Zero 2W"
|
||||
|
||||
#define DEFAULT_I2C_BUS_SCL (&pin_GPIO3)
|
||||
#define DEFAULT_I2C_BUS_SDA (&pin_GPIO2)
|
@ -0,0 +1,6 @@
|
||||
USB_VID = 0x2E8A
|
||||
USB_PID = 0xF002
|
||||
USB_PRODUCT = "Raspberry Pi Zero 2W"
|
||||
USB_MANUFACTURER = "Raspberry Pi"
|
||||
|
||||
CHIP_VARIANT = rp3a0
|
62
ports/broadcom/boards/raspberrypi_zero2w/pins.c
Normal file
62
ports/broadcom/boards/raspberrypi_zero2w/pins.c
Normal 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_I2C), MP_ROM_PTR(&board_i2c_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)},
|
||||
};
|
||||
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);
|
34
ports/broadcom/common-hal/board/__init__.c
Normal file
34
ports/broadcom/common-hal/board/__init__.c
Normal 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.
|
226
ports/broadcom/common-hal/busio/I2C.c
Normal file
226
ports/broadcom/common-hal/busio/I2C.c
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "shared-bindings/microcontroller/Pin.h"
|
||||
|
||||
#include "peripherals/broadcom/cpu.h"
|
||||
#include "peripherals/broadcom/vcmailbox.h"
|
||||
|
||||
#define NUM_I2C (2)
|
||||
|
||||
STATIC bool never_reset_i2c[NUM_I2C];
|
||||
STATIC bool in_use_i2c[NUM_I2C];
|
||||
STATIC BSC0_Type *i2c[2] = {BSC0, BSC1};
|
||||
|
||||
void reset_i2c(void) {
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (never_reset_i2c[i]) {
|
||||
continue;
|
||||
}
|
||||
in_use_i2c[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
size_t instance_index = NUM_I2C;
|
||||
if ((scl == &pin_GPIO1 || scl == &pin_GPIO29 || scl == &pin_GPIO45) &&
|
||||
(sda == &pin_GPIO0 || sda == &pin_GPIO28 || sda == &pin_GPIO44)) {
|
||||
instance_index = 0;
|
||||
} else if ((scl == &pin_GPIO44 || scl == &pin_GPIO3) &&
|
||||
(sda == &pin_GPIO43 || sda == &pin_GPIO2)) {
|
||||
instance_index = 1;
|
||||
}
|
||||
if (instance_index == NUM_I2C) {
|
||||
mp_raise_ValueError(translate("Invalid pins"));
|
||||
}
|
||||
in_use_i2c[instance_index] = true;
|
||||
self->index = instance_index;
|
||||
self->peripheral = i2c[self->index];
|
||||
self->sda_pin = sda;
|
||||
self->scl_pin = scl;
|
||||
|
||||
uint32_t source_clock = vcmailbox_get_clock_rate_measured(VCMAILBOX_CLOCK_CORE);
|
||||
uint16_t clock_divider = source_clock / frequency;
|
||||
self->peripheral->DIV_b.CDIV = clock_divider;
|
||||
|
||||
gpio_set_function(sda->number, GPIO_GPFSEL0_FSEL2_SDA1);
|
||||
gpio_set_function(scl->number, GPIO_GPFSEL0_FSEL3_SCL1);
|
||||
}
|
||||
|
||||
bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) {
|
||||
return self->sda_pin == NULL;
|
||||
}
|
||||
|
||||
void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) {
|
||||
if (common_hal_busio_i2c_deinited(self)) {
|
||||
return;
|
||||
}
|
||||
never_reset_i2c[self->index] = false;
|
||||
|
||||
common_hal_reset_pin(self->sda_pin);
|
||||
common_hal_reset_pin(self->scl_pin);
|
||||
self->sda_pin = NULL;
|
||||
self->scl_pin = NULL;
|
||||
}
|
||||
|
||||
bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) {
|
||||
uint8_t result = common_hal_busio_i2c_write(self, addr, NULL, 0, true);
|
||||
// mp_printf(&mp_plat_print, "result %d %d\n", addr, result);
|
||||
return result == 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;
|
||||
}
|
||||
|
||||
// Discussion of I2C implementation is here: https://github.com/raspberrypi/linux/issues/254
|
||||
|
||||
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) {
|
||||
COMPLETE_MEMORY_READS;
|
||||
self->peripheral->S_b.DONE = true;
|
||||
self->peripheral->A_b.ADDR = addr;
|
||||
size_t loop_len = len;
|
||||
// Prevent the stop bit by transmitting everything but the last byte. Doing
|
||||
// so is left up to the subsequent read.
|
||||
if (!transmit_stop_bit) {
|
||||
loop_len -= 1;
|
||||
}
|
||||
self->peripheral->DLEN_b.DLEN = len;
|
||||
self->peripheral->C = BSC0_C_ST_Msk | BSC0_C_I2CEN_Msk;
|
||||
// Wait for the transaction to start.
|
||||
while (self->peripheral->S_b.TA == 0) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
for (size_t i = 0; i < loop_len; i++) {
|
||||
if (self->peripheral->S_b.ERR) {
|
||||
break;
|
||||
}
|
||||
self->peripheral->FIFO_b.DATA = data[i];
|
||||
// Wait for the FIFO to empty enough that we can write more data.
|
||||
while (self->peripheral->S_b.TXE == 0) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
}
|
||||
// Wait for the FIFO to empty completely, not DONE, because we may not complete the
|
||||
// transaction with a write.
|
||||
while (self->peripheral->S_b.ERR == 0 &&
|
||||
((!transmit_stop_bit && self->peripheral->S_b.TXE == 0) ||
|
||||
(transmit_stop_bit && self->peripheral->S_b.TA == 1))) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
self->finish_write = false;
|
||||
if (self->peripheral->S_b.ERR) {
|
||||
// Wait for the transfer to finish.
|
||||
while (self->peripheral->S_b.TA == 1) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
// Clear the flag by writing it and wait for it to clear.
|
||||
self->peripheral->S_b.ERR = true;
|
||||
while (self->peripheral->S_b.ERR == 1) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
return MP_ENODEV;
|
||||
}
|
||||
|
||||
if (loop_len < len) {
|
||||
self->finish_write = true;
|
||||
self->last_write_data = data[len - 1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr,
|
||||
uint8_t *data, size_t len) {
|
||||
COMPLETE_MEMORY_READS;
|
||||
self->peripheral->A_b.ADDR = addr;
|
||||
if (self->finish_write) {
|
||||
self->finish_write = false;
|
||||
if (self->peripheral->S_b.ERR == 1) {
|
||||
return MP_ENODEV;
|
||||
}
|
||||
self->peripheral->FIFO_b.DATA = self->last_write_data;
|
||||
} else {
|
||||
self->peripheral->S_b.DONE = true;
|
||||
}
|
||||
self->peripheral->DLEN_b.DLEN = len;
|
||||
self->peripheral->C = BSC0_C_READ_Msk | BSC0_C_ST_Msk | BSC0_C_I2CEN_Msk;
|
||||
// Wait for the transaction to start.
|
||||
while (self->peripheral->S_b.TA == 0) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (self->peripheral->S_b.ERR) {
|
||||
break;
|
||||
}
|
||||
// Wait for the FIFO to have enough data that we can read it. RXR is low
|
||||
// once the transaction is done so we check the done bit too.
|
||||
while (!self->peripheral->S_b.RXR && !self->peripheral->S_b.DONE) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
data[i] = self->peripheral->FIFO_b.DATA;
|
||||
}
|
||||
// Wait for the transaction to finish.
|
||||
while (!self->peripheral->S_b.DONE && !self->peripheral->S_b.ERR) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
if (self->peripheral->S_b.ERR) {
|
||||
self->peripheral->S_b.ERR = true;
|
||||
while (self->peripheral->S_b.ERR == 1) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
return MP_ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) {
|
||||
never_reset_i2c[self->index] = true;
|
||||
|
||||
common_hal_never_reset_pin(self->scl_pin);
|
||||
common_hal_never_reset_pin(self->sda_pin);
|
||||
}
|
50
ports/broadcom/common-hal/busio/I2C.h
Normal file
50
ports/broadcom/common-hal/busio/I2C.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#include "peripherals/broadcom/defines.h"
|
||||
#include "peripherals/broadcom/gpio.h"
|
||||
|
||||
typedef struct {
|
||||
mp_obj_base_t base;
|
||||
const mcu_pin_obj_t *scl_pin;
|
||||
const mcu_pin_obj_t *sda_pin;
|
||||
BSC0_Type *peripheral;
|
||||
uint8_t index;
|
||||
bool has_lock;
|
||||
bool finish_write;
|
||||
uint8_t last_write_data;
|
||||
} busio_i2c_obj_t;
|
||||
|
||||
void reset_i2c(void);
|
||||
|
||||
#endif // MICROPY_INCLUDED_BROADCOM_COMMON_HAL_BUSIO_I2C_H
|
142
ports/broadcom/common-hal/busio/SPI.c
Normal file
142
ports/broadcom/common-hal/busio/SPI.c
Normal 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;
|
||||
}
|
49
ports/broadcom/common-hal/busio/SPI.h
Normal file
49
ports/broadcom/common-hal/busio/SPI.h
Normal 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
|
313
ports/broadcom/common-hal/busio/UART.c
Normal file
313
ports/broadcom/common-hal/busio/UART.c
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
// The Broadcom chips have two different types of UARTs. UART1 is the "mini-UART"
|
||||
// that is most available so we've implemented it first. The ARM PL011 UART
|
||||
// support will be added later. We set NUM_UARTS to 2 here so that we can match
|
||||
// the indexing even though UART0 isn't supported yet. We currently use this
|
||||
// UART for debugging so we don't support user use of UART yet.
|
||||
#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;
|
||||
}
|
47
ports/broadcom/common-hal/busio/UART.h
Normal file
47
ports/broadcom/common-hal/busio/UART.h
Normal 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
|
1
ports/broadcom/common-hal/busio/__init__.c
Normal file
1
ports/broadcom/common-hal/busio/__init__.c
Normal file
@ -0,0 +1 @@
|
||||
// No busio module functions.
|
163
ports/broadcom/common-hal/digitalio/DigitalInOut.c
Normal file
163
ports/broadcom/common-hal/digitalio/DigitalInOut.c
Normal 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;
|
||||
}
|
40
ports/broadcom/common-hal/digitalio/DigitalInOut.h
Normal file
40
ports/broadcom/common-hal/digitalio/DigitalInOut.h
Normal 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
|
1
ports/broadcom/common-hal/digitalio/__init__.c
Normal file
1
ports/broadcom/common-hal/digitalio/__init__.c
Normal file
@ -0,0 +1 @@
|
||||
// No digitalio module functions.
|
139
ports/broadcom/common-hal/microcontroller/Pin.c
Normal file
139
ports/broadcom/common-hal/microcontroller/Pin.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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(28)
|
||||
PIN(29)
|
||||
|
||||
PIN(30)
|
||||
PIN(31)
|
||||
PIN(32)
|
||||
PIN(33)
|
||||
PIN(34)
|
||||
PIN(35)
|
||||
PIN(36)
|
||||
PIN(37)
|
||||
PIN(38)
|
||||
PIN(39)
|
||||
|
||||
PIN(40)
|
||||
PIN(41)
|
||||
PIN(42)
|
||||
PIN(43)
|
||||
PIN(44)
|
||||
PIN(45)
|
||||
PIN(46)
|
||||
PIN(47)
|
||||
PIN(48)
|
||||
PIN(49)
|
||||
|
||||
PIN(50)
|
||||
PIN(51)
|
||||
PIN(52)
|
||||
PIN(53)
|
||||
PIN(54)
|
||||
PIN(55)
|
||||
PIN(56)
|
||||
PIN(57)
|
112
ports/broadcom/common-hal/microcontroller/Pin.h
Normal file
112
ports/broadcom/common-hal/microcontroller/Pin.h
Normal 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.
|
||||
*/
|
||||
|
||||
#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_GPIO30;
|
||||
extern const mcu_pin_obj_t pin_GPIO31;
|
||||
extern const mcu_pin_obj_t pin_GPIO32;
|
||||
extern const mcu_pin_obj_t pin_GPIO33;
|
||||
extern const mcu_pin_obj_t pin_GPIO34;
|
||||
extern const mcu_pin_obj_t pin_GPIO35;
|
||||
extern const mcu_pin_obj_t pin_GPIO36;
|
||||
extern const mcu_pin_obj_t pin_GPIO37;
|
||||
extern const mcu_pin_obj_t pin_GPIO38;
|
||||
extern const mcu_pin_obj_t pin_GPIO39;
|
||||
|
||||
extern const mcu_pin_obj_t pin_GPIO40;
|
||||
extern const mcu_pin_obj_t pin_GPIO41;
|
||||
extern const mcu_pin_obj_t pin_GPIO42;
|
||||
extern const mcu_pin_obj_t pin_GPIO43;
|
||||
extern const mcu_pin_obj_t pin_GPIO44;
|
||||
extern const mcu_pin_obj_t pin_GPIO45;
|
||||
extern const mcu_pin_obj_t pin_GPIO46;
|
||||
extern const mcu_pin_obj_t pin_GPIO47;
|
||||
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;
|
||||
extern const mcu_pin_obj_t pin_GPIO54;
|
||||
extern const mcu_pin_obj_t pin_GPIO55;
|
||||
extern const mcu_pin_obj_t pin_GPIO56;
|
||||
extern const mcu_pin_obj_t pin_GPIO57;
|
||||
|
||||
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
|
53
ports/broadcom/common-hal/microcontroller/Processor.c
Normal file
53
ports/broadcom/common-hal/microcontroller/Processor.c
Normal 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;
|
||||
}
|
39
ports/broadcom/common-hal/microcontroller/Processor.h
Normal file
39
ports/broadcom/common-hal/microcontroller/Processor.h
Normal 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
|
195
ports/broadcom/common-hal/microcontroller/__init__.c
Normal file
195
ports/broadcom/common-hal/microcontroller/__init__.c
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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/Pin.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.
|
||||
#define PIN(num) { MP_ROM_QSTR(MP_QSTR_GPIO##num), MP_ROM_PTR(&pin_GPIO##num) },
|
||||
const mp_rom_map_elem_t mcu_pin_global_dict_table[TOTAL_GPIO_COUNT] = {
|
||||
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(28)
|
||||
PIN(29)
|
||||
|
||||
PIN(30)
|
||||
PIN(31)
|
||||
PIN(32)
|
||||
PIN(33)
|
||||
PIN(34)
|
||||
PIN(35)
|
||||
PIN(36)
|
||||
PIN(37)
|
||||
PIN(38)
|
||||
PIN(39)
|
||||
|
||||
PIN(40)
|
||||
PIN(41)
|
||||
PIN(42)
|
||||
PIN(43)
|
||||
PIN(44)
|
||||
PIN(45)
|
||||
PIN(46)
|
||||
PIN(47)
|
||||
PIN(48)
|
||||
PIN(49)
|
||||
|
||||
PIN(50)
|
||||
PIN(51)
|
||||
PIN(52)
|
||||
PIN(53)
|
||||
PIN(54)
|
||||
PIN(55)
|
||||
PIN(56)
|
||||
PIN(57)
|
||||
};
|
||||
MP_DEFINE_CONST_DICT(mcu_pin_globals, mcu_pin_global_dict_table);
|
32
ports/broadcom/common-hal/microcontroller/__init__.h
Normal file
32
ports/broadcom/common-hal/microcontroller/__init__.h
Normal 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 58
|
||||
|
||||
#endif // MICROPY_INCLUDED_BROADCOM_COMMON_HAL_MICROCONTROLLER___INIT___H
|
62
ports/broadcom/common-hal/os/__init__.c
Normal file
62
ports/broadcom/common-hal/os/__init__.c
Normal 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;
|
||||
}
|
367
ports/broadcom/common-hal/sdioio/SDCard.c
Normal file
367
ports/broadcom/common-hal/sdioio/SDCard.c
Normal 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) {
|
||||
}
|
43
ports/broadcom/common-hal/sdioio/SDCard.h
Normal file
43
ports/broadcom/common-hal/sdioio/SDCard.h
Normal 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;
|
0
ports/broadcom/common-hal/sdioio/__init__.c
Normal file
0
ports/broadcom/common-hal/sdioio/__init__.c
Normal file
0
ports/broadcom/common-hal/sdioio/__init__.h
Normal file
0
ports/broadcom/common-hal/sdioio/__init__.h
Normal file
37
ports/broadcom/common-hal/supervisor/Runtime.c
Normal file
37
ports/broadcom/common-hal/supervisor/Runtime.c
Normal 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();
|
||||
}
|
37
ports/broadcom/common-hal/supervisor/Runtime.h
Normal file
37
ports/broadcom/common-hal/supervisor/Runtime.h
Normal 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
|
40
ports/broadcom/common-hal/supervisor/__init__.c
Normal file
40
ports/broadcom/common-hal/supervisor/__init__.c
Normal 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,
|
||||
},
|
||||
};
|
67
ports/broadcom/common-hal/videocore/Framebuffer.c
Normal file
67
ports/broadcom/common-hal/videocore/Framebuffer.c
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
#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,
|
||||
mp_uint_t width, mp_uint_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;
|
||||
}
|
||||
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;
|
||||
}
|
38
ports/broadcom/common-hal/videocore/Framebuffer.h
Normal file
38
ports/broadcom/common-hal/videocore/Framebuffer.h
Normal 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;
|
||||
mp_uint_t width;
|
||||
mp_uint_t height;
|
||||
uint32_t pitch;
|
||||
bool doublebuffer;
|
||||
} videocore_framebuffer_obj_t;
|
9
ports/broadcom/config.txt
Normal file
9
ports/broadcom/config.txt
Normal 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
|
48
ports/broadcom/fatfs_port.c
Normal file
48
ports/broadcom/fatfs_port.c
Normal 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
|
||||
|
||||
|
||||
}
|
1
ports/broadcom/firmware
Submodule
1
ports/broadcom/firmware
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 6a5207946edcd45813d1dd1572ca8bd8101b68b6
|
62
ports/broadcom/mpconfigport.h
Normal file
62
ports/broadcom/mpconfigport.h
Normal 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 (MICROPY_OBJ_REPR_A)
|
||||
#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
|
58
ports/broadcom/mpconfigport.mk
Normal file
58
ports/broadcom/mpconfigport.mk
Normal 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
|
34
ports/broadcom/mphalport.c
Normal file
34
ports/broadcom/mphalport.c
Normal 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;
|
||||
}
|
26
ports/broadcom/mphalport.h
Normal file
26
ports/broadcom/mphalport.h
Normal 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
|
1
ports/broadcom/peripherals
Submodule
1
ports/broadcom/peripherals
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 8057af2d614c27a1e103d5f4300428f341da6bcb
|
31
ports/broadcom/qstrdefsport.h
Normal file
31
ports/broadcom/qstrdefsport.h
Normal 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)
|
179
ports/broadcom/supervisor/internal_flash.c
Normal file
179
ports/broadcom/supervisor/internal_flash.c
Normal 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) {
|
||||
}
|
33
ports/broadcom/supervisor/internal_flash.h
Normal file
33
ports/broadcom/supervisor/internal_flash.h
Normal 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
|
31
ports/broadcom/supervisor/internal_flash_root_pointers.h
Normal file
31
ports/broadcom/supervisor/internal_flash_root_pointers.h
Normal 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
|
199
ports/broadcom/supervisor/port.c
Normal file
199
ports/broadcom/supervisor/port.c
Normal 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();
|
||||
}
|
48
ports/broadcom/supervisor/usb.c
Normal file
48
ports/broadcom/supervisor/usb.c
Normal 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();
|
||||
}
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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))));
|
||||
|
@ -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"));
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
@ -335,6 +338,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
|
||||
@ -344,9 +350,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
|
||||
|
@ -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,83 +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_IS31FL3741
|
||||
// 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
Loading…
Reference in New Issue
Block a user