From 30afd8a68e5e8a1c4e8e47ccd1051daebc7aa48e Mon Sep 17 00:00:00 2001 From: retrhelo Date: Sun, 17 Jan 2021 01:23:57 +0800 Subject: [PATCH] tmp commit --- Makefile | 2 +- kernel/include/sdcard.back.h | 90 +++++ kernel/include/sdcard.example.h | 166 +++++++++ kernel/include/spi.example.h | 210 ++++++++++++ kernel/include/vm.h | 25 ++ kernel/main.c | 8 +- kernel/sdcard.back.c | 527 +++++++++++++++++++++++++++++ kernel/sdcard.example.c | 582 ++++++++++++++++++++++++++++++++ kernel/spi.back.c | 303 +++++++++++++++++ kernel/spi.example.c | 195 +++++++++++ xv6-user/_test | Bin 0 -> 23208 bytes 11 files changed, 2103 insertions(+), 5 deletions(-) create mode 100644 kernel/include/sdcard.back.h create mode 100644 kernel/include/sdcard.example.h create mode 100644 kernel/include/spi.example.h create mode 100644 kernel/include/vm.h create mode 100644 kernel/sdcard.back.c create mode 100644 kernel/sdcard.example.c create mode 100644 kernel/spi.back.c create mode 100644 kernel/spi.example.c create mode 100755 xv6-user/_test diff --git a/Makefile b/Makefile index ff4790e..c307305 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,7 @@ else RUSTSBI = ./bootloader/SBI/sbi-qemu endif -TOOLPREFIX := riscv64-unknown-elf- +TOOLPREFIX := riscv64-linux-gnu- # TOOLPREFIX := riscv64-linux-gnu- CC = $(TOOLPREFIX)gcc AS = $(TOOLPREFIX)gas diff --git a/kernel/include/sdcard.back.h b/kernel/include/sdcard.back.h new file mode 100644 index 0000000..c904425 --- /dev/null +++ b/kernel/include/sdcard.back.h @@ -0,0 +1,90 @@ +#ifndef _SDCARD_H +#define _SDCARD_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * @brief Card Specific Data: CSD Register + */ +typedef struct { + uint8 CSDStruct; /*!< CSD structure */ + uint8 SysSpecVersion; /*!< System specification version */ + uint8 Reserved1; /*!< Reserved */ + uint8 TAAC; /*!< Data read access-time 1 */ + uint8 NSAC; /*!< Data read access-time 2 in CLK cycles */ + uint8 MaxBusClkFrec; /*!< Max. bus clock frequency */ + uint16 CardComdClasses; /*!< Card command classes */ + uint8 RdBlockLen; /*!< Max. read data block length */ + uint8 PartBlockRead; /*!< Partial blocks for read allowed */ + uint8 WrBlockMisalign; /*!< Write block misalignment */ + uint8 RdBlockMisalign; /*!< Read block misalignment */ + uint8 DSRImpl; /*!< DSR implemented */ + uint8 Reserved2; /*!< Reserved */ + uint32 DeviceSize; /*!< Device Size */ + uint8 MaxRdCurrentVDDMin; /*!< Max. read current @ VDD min */ + uint8 MaxRdCurrentVDDMax; /*!< Max. read current @ VDD max */ + uint8 MaxWrCurrentVDDMin; /*!< Max. write current @ VDD min */ + uint8 MaxWrCurrentVDDMax; /*!< Max. write current @ VDD max */ + uint8 DeviceSizeMul; /*!< Device size multiplier */ + uint8 EraseGrSize; /*!< Erase group size */ + uint8 EraseGrMul; /*!< Erase group size multiplier */ + uint8 WrProtectGrSize; /*!< Write protect group size */ + uint8 WrProtectGrEnable; /*!< Write protect group enable */ + uint8 ManDeflECC; /*!< Manufacturer default ECC */ + uint8 WrSpeedFact; /*!< Write speed factor */ + uint8 MaxWrBlockLen; /*!< Max. write data block length */ + uint8 WriteBlockPaPartial; /*!< Partial blocks for write allowed */ + uint8 Reserved3; /*!< Reserded */ + uint8 ContentProtectAppli; /*!< Content protection application */ + uint8 FileFormatGrouop; /*!< File format group */ + uint8 CopyFlag; /*!< Copy flag (OTP) */ + uint8 PermWrProtect; /*!< Permanent write protection */ + uint8 TempWrProtect; /*!< Temporary write protection */ + uint8 FileFormat; /*!< File Format */ + uint8 ECC; /*!< ECC code */ + uint8 CSD_CRC; /*!< CSD CRC */ + uint8 Reserved4; /*!< always 1*/ +} SD_CSD; + +/** + * @brief Card Identification Data: CID Register + */ +typedef struct { + uint8 ManufacturerID; /*!< ManufacturerID */ + uint16 OEM_AppliID; /*!< OEM/Application ID */ + uint32 ProdName1; /*!< Product Name part1 */ + uint8 ProdName2; /*!< Product Name part2*/ + uint8 ProdRev; /*!< Product Revision */ + uint32 ProdSN; /*!< Product Serial Number */ + uint8 Reserved1; /*!< Reserved1 */ + uint16 ManufactDate; /*!< Manufacturing Date */ + uint8 CID_CRC; /*!< CID CRC */ + uint8 Reserved2; /*!< always 1 */ +} SD_CID; + +/** + * @brief SD Card information + */ +typedef struct { + SD_CSD SD_csd; + SD_CID SD_cid; + uint64 CardCapacity; /*!< Card Capacity */ + uint32 CardBlockSize; /*!< Card Block Size */ +} SD_CardInfo; + +extern SD_CardInfo cardinfo; + +uint8 sd_init(void); +void sdcard_init(void); +uint8 sd_read_sector(uint8 *data_buff, uint32 sector, uint32 count); +uint8 sd_write_sector(uint8 *data_buff, uint32 sector, uint32 count); +uint8 sd_read_sector_dma(uint8 *data_buff, uint32 sector, uint32 count); +uint8 sd_write_sector_dma(uint8 *data_buff, uint32 sector, uint32 count); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kernel/include/sdcard.example.h b/kernel/include/sdcard.example.h new file mode 100644 index 0000000..5254065 --- /dev/null +++ b/kernel/include/sdcard.example.h @@ -0,0 +1,166 @@ +// +// Created by lumin on 2020/11/2. +// + +#ifndef LAB8_SDCARD_H +#define LAB8_SDCARD_H + +#include + +/* + * SD Card Commands + * @brief Commands: CMDxx = CMD-number | 0x40 + */ +#define SD_CMD_SIGN (0x40) +#define SD_CMD0 0 // chip reset +#define SD_CMD8 8 // voltage negotiation +#define SD_CMD9 9 // read CSD register +#define SD_CMD10 10 // read CID register +#define SD_CMD12 12 // end multiple continuous sector read +#define SD_CMD17 17 // start single sector read +#define SD_CMD18 18 // start multiple continuous sector read and send start sector +#define SD_ACMD23 23 // start multiple continuous sector write and send sector count +#define SD_CMD24 24 // start single sector write +#define SD_CMD25 25 // start multiple continuous sector write and send start sector +#define SD_ACMD41 41 // capacity mode set +#define SD_CMD55 55 // ACMD prefix +#define SD_CMD58 58 // read CCS(card capacity status) + +#define SD_INIT_MODE_RESULT_OK (0x01) +#define SD_TRANS_MODE_RESULT_OK (0x00) +#define SD_START_DATA_READ_RESPONSE (0xFE) +#define SD_START_DATA_SINGLE_BLOCK_WRITE_TOKEN (0xFE) +#define SD_START_DATA_MULTIPLE_BLOCK_WRITE_TOKEN (0xFC) + +/** + * CMD frame format + * | CRC | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | CMD | + * | byte 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | + */ +#define SD_CMD_CMD_BIT 0 +#define SD_CMD_ARG_MSB0 1 +#define SD_CMD_ARG_MSB1 2 +#define SD_CMD_ARG_MSB2 3 +#define SD_CMD_ARG_MSB3 4 +#define SD_CMD_CRC_BIT 5 +#define SD_CMD_FRAME_SIZE 6 + +#define SD_EMPTY_FILL (0xFF) + +#define SD_R3_RESPONSE_REST_LENGTH 4 +#define SD_R7_RESPONSE_REST_LENGTH 4 + +// SD card here uses SPI mode, pin +// names also inherit from it. +typedef struct +{ + int mosi_pin; // pin num of Master Out Slave In + int miso_pin; // pin num of Master In Slave Out + int sclk_pin; // pin num of SPI Clock + int cs_pin; // pin num of Chip Selector + int cs_gpio_num;// what is this..? +} sdcard_hardware_pin_config_t; + +typedef struct +{ + uint8_t CSDStruct; /*!< CSD structure */ + uint8_t SysSpecVersion; /*!< System specification version */ + uint8_t Reserved1; /*!< Reserved */ + uint8_t TAAC; /*!< Data read access-time 1 */ + uint8_t NSAC; /*!< Data read access-time 2 in CLK cycles */ + uint8_t MaxBusClkFrec; /*!< Max. bus clock frequency */ + uint16_t CardComdClasses; /*!< Card command classes */ + uint8_t RdBlockLen; /*!< Max. read data block length */ + uint8_t PartBlockRead; /*!< Partial blocks for read allowed */ + uint8_t WrBlockMisalign; /*!< Write block misalignment */ + uint8_t RdBlockMisalign; /*!< Read block misalignment */ + uint8_t DSRImpl; /*!< DSR implemented */ + uint8_t Reserved2; /*!< Reserved */ + uint32_t DeviceSize; /*!< Device Size */ + uint8_t MaxRdCurrentVDDMin; /*!< Max. read current @ VDD min */ + uint8_t MaxRdCurrentVDDMax; /*!< Max. read current @ VDD max */ + uint8_t MaxWrCurrentVDDMin; /*!< Max. write current @ VDD min */ + uint8_t MaxWrCurrentVDDMax; /*!< Max. write current @ VDD max */ + uint8_t DeviceSizeMul; /*!< Device size multiplier */ + uint8_t EraseGrSize; /*!< Erase group size */ + uint8_t EraseGrMul; /*!< Erase group size multiplier */ + uint8_t WrProtectGrSize; /*!< Write protect group size */ + uint8_t WrProtectGrEnable; /*!< Write protect group enable */ + uint8_t ManDeflECC; /*!< Manufacturer default ECC */ + uint8_t WrSpeedFact; /*!< Write speed factor */ + uint8_t MaxWrBlockLen; /*!< Max. write data block length */ + uint8_t WriteBlockPaPartial; /*!< Partial blocks for write allowed */ + uint8_t Reserved3; /*!< Reserded */ + uint8_t ContentProtectAppli; /*!< Content protection application */ + uint8_t FileFormatGrouop; /*!< File format group */ + uint8_t CopyFlag; /*!< Copy flag (OTP) */ + uint8_t PermWrProtect; /*!< Permanent write protection */ + uint8_t TempWrProtect; /*!< Temporary write protection */ + uint8_t FileFormat; /*!< File Format */ + uint8_t ECC; /*!< ECC code */ + uint8_t CSD_CRC; /*!< CSD CRC */ + uint8_t Reserved4; /*!< always 1*/ + uint8_t CSizeMlut; /*!< */ +} SD_CSD; + +/** +* @brief Card Identification Data: CID Register +*/ +typedef struct +{ + uint8_t ManufacturerID; /*!< ManufacturerID */ + uint16_t OEM_AppliID; /*!< OEM/Application ID */ + uint32_t ProdName1; /*!< Product Name part1 */ + uint8_t ProdName2; /*!< Product Name part2*/ + uint8_t ProdRev; /*!< Product Revision */ + uint32_t ProdSN; /*!< Product Serial Number */ + uint8_t Reserved1; /*!< Reserved1 */ + uint16_t ManufactDate; /*!< Manufacturing Date */ + uint8_t CID_CRC; /*!< CID CRC */ + uint8_t Reserved2; /*!< always 1 */ +} SD_CID; + +/** +* @brief SD Card information +*/ +typedef struct +{ + SD_CSD SD_csd; + SD_CID SD_cid; + uint64_t CardCapacity; /*!< Card Capacity */ + uint32_t CardBlockSize; /*!< Card Block Size */ + uint8_t active; +} SD_CardInfo; + +extern SD_CardInfo cardinfo; + +/** + * Initialize the SD Card + */ +void sd_init(); + +/** + * Synchronously read single/multiple sector(s) from SD card + * + * @param data_buff pointer to the buffer that receives the data read from the SD. + * @param sector SD's internal address to read from. + * @param count count of sectors to be read + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ +uint8_t sd_read_sector(uint8_t *data_buff, uint32_t sector, uint32_t count); + +/** + * Synchronously write single/multiple sector(s) from SD card + * + * @param data_buff pointer to the buffer to be written to the SD. + * @param sector SD's internal address to write to. + * @param count count of sectors to be read + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ +uint8_t sd_write_sector(uint8_t *data_buff, uint32_t sector, uint32_t count); + +#endif //LAB8_SDCARD_H diff --git a/kernel/include/spi.example.h b/kernel/include/spi.example.h new file mode 100644 index 0000000..9f4f3bd --- /dev/null +++ b/kernel/include/spi.example.h @@ -0,0 +1,210 @@ +// +// Created by lumin on 2020/11/2. +// + +#ifndef LAB8_SPI_H +#define LAB8_SPI_H + +#include +#include + +#define SPI01_WORK_MODE_OFFSET 6 +#define SPI012_TRANSFER_MODE_OFFSET 8 +#define SPI01_DATA_BIT_LENGTH_OFFSET 16 +#define SPI01_FRAME_FORMAT_OFFSET 21 + +#define SPI3_WORK_MODE_OFFSET 8 +#define SPI3_TRANSFER_MODE_OFFSET 10 +#define SPI3_DATA_BIT_LENGTH_OFFSET 0 +#define SPI3_FRAME_FORMAT_OFFSET 22 + +#define SPI_DATA_BIT_LENGTH_BIT 5 +#define SPI_MIN_DATA_BIT_LENGTH 4 +#define SPI_MAX_DATA_BIT_LENGTH (1 << SPI_DATA_BIT_LENGTH_BIT) + +#define SPI_BAUDRATE_DEFAULT_VAL (0x14) +#define SPI_INTERRUPT_DISABLE (0x00) +#define SPI_DMACR_DEFAULT_VAL (0x00) +#define SPI_DMATDLR_DEFAULT_VAL (0x00) +#define SPI_DMARDLR_DEFAULT_VAL (0x00) +#define SPI_SLAVE_DISABLE (0x00) +#define SPI_MASTER_DISABLE (0x00) +#define SPI_MASTER_ENABLE (0x01) +#define SPI_TMOD_DEFAULT_VAL 0 + +#define SPI_FIFO_CAPCITY_IN_BYTE (32) + +typedef struct +{ + /* SPI Control Register 0 (0x00)*/ + volatile uint32_t ctrlr0; + /* SPI Control Register 1 (0x04)*/ + volatile uint32_t ctrlr1; + /* SPI Enable Register (0x08)*/ + volatile uint32_t ssienr; + /* SPI Microwire Control Register (0x0c)*/ + volatile uint32_t mwcr; + /* SPI Slave Enable Register (0x10)*/ + volatile uint32_t ser; + /* SPI Baud Rate Select (0x14)*/ + volatile uint32_t baudr; + /* SPI Transmit FIFO Threshold Level (0x18)*/ + volatile uint32_t txftlr; + /* SPI Receive FIFO Threshold Level (0x1c)*/ + volatile uint32_t rxftlr; + /* SPI Transmit FIFO Level Register (0x20)*/ + volatile uint32_t txflr; + /* SPI Receive FIFO Level Register (0x24)*/ + volatile uint32_t rxflr; + /* SPI Status Register (0x28)*/ + volatile uint32_t sr; + /* SPI Interrupt Mask Register (0x2c)*/ + volatile uint32_t imr; + /* SPI Interrupt Status Register (0x30)*/ + volatile uint32_t isr; + /* SPI Raw Interrupt Status Register (0x34)*/ + volatile uint32_t risr; + /* SPI Transmit FIFO Overflow Interrupt Clear Register (0x38)*/ + volatile uint32_t txoicr; + /* SPI Receive FIFO Overflow Interrupt Clear Register (0x3c)*/ + volatile uint32_t rxoicr; + /* SPI Receive FIFO Underflow Interrupt Clear Register (0x40)*/ + volatile uint32_t rxuicr; + /* SPI Multi-Master Interrupt Clear Register (0x44)*/ + volatile uint32_t msticr; + /* SPI Interrupt Clear Register (0x48)*/ + volatile uint32_t icr; + /* SPI DMA Control Register (0x4c)*/ + volatile uint32_t dmacr; + /* SPI DMA Transmit Data Level (0x50)*/ + volatile uint32_t dmatdlr; + /* SPI DMA Receive Data Level (0x54)*/ + volatile uint32_t dmardlr; + /* SPI Identification Register (0x58)*/ + volatile uint32_t idr; + /* SPI DWC_ssi component version (0x5c)*/ + volatile uint32_t ssic_version_id; + /* SPI Data Register 0-36 (0x60 -- 0xec)*/ + volatile uint32_t dr[36]; + /* SPI RX Sample Delay Register (0xf0)*/ + volatile uint32_t rx_sample_delay; + /* SPI SPI Control Register (0xf4)*/ + volatile uint32_t spi_ctrlr0; + /* reserved (0xf8)*/ + volatile uint32_t resv; + /* SPI XIP Mode bits (0xfc)*/ + volatile uint32_t xip_mode_bits; + /* SPI XIP INCR transfer opcode (0x100)*/ + volatile uint32_t xip_incr_inst; + /* SPI XIP WRAP transfer opcode (0x104)*/ + volatile uint32_t xip_wrap_inst; + /* SPI XIP Control Register (0x108)*/ + volatile uint32_t xip_ctrl; + /* SPI XIP Slave Enable Register (0x10c)*/ + volatile uint32_t xip_ser; + /* SPI XIP Receive FIFO Overflow Interrupt Clear Register (0x110)*/ + volatile uint32_t xrxoicr; + /* SPI XIP time out register for continuous transfers (0x114)*/ + volatile uint32_t xip_cnt_time_out; + volatile uint32_t endian; +} __attribute__((packed, aligned(4))) spi_t; + +typedef enum +{ + SPI_DEVICE_0, + SPI_DEVICE_1, + SPI_DEVICE_2, + SPI_DEVICE_3, + SPI_DEVICE_MAX, +} spi_device_num_t; + +typedef enum +{ + SPI_WORK_MODE_0, + SPI_WORK_MODE_1, + SPI_WORK_MODE_2, + SPI_WORK_MODE_3, +} spi_work_mode_t; + +typedef enum +{ + SPI_FF_STANDARD, + SPI_FF_DUAL, + SPI_FF_QUAD, + SPI_FF_OCTAL, +} spi_frame_format_t; + +typedef enum +{ + SPI_TMODE_TRANS_RECV, + SPI_TMODE_TRANS, + SPI_TMODE_RECV, + SPI_TMODE_EEROM, +} spi_transfer_mode_t; + +typedef enum +{ + SPI_CHIP_SELECT_0, + SPI_CHIP_SELECT_1, + SPI_CHIP_SELECT_2, + SPI_CHIP_SELECT_3, + SPI_CHIP_SELECT_MAX, +} spi_chip_select_t; + +extern volatile spi_t *const spi[4]; + +/** + * @brief Set spi configuration + * + * @param[in] spi_num Spi bus number + * @param[in] mode Spi mode + * @param[in] frame_format Spi frame format + * @param[in] data_bit_length Spi data bit length + * @param[in] endian 0:little-endian 1:big-endian + * + * @return Void + */ +void spi_init(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_format_t frame_format, + size_t data_bit_length, uint32_t endian); + +/** + * @brief Spi send data + * + * @param[in] spi_num Spi bus number + * @param[in] slave Spi chip select + * @param[in] cmd_buff Spi command buffer point + * @param[in] cmd_len Spi command length + * @param[in] tx_buff Spi transmit buffer point + * @param[in] tx_len Spi transmit buffer length + * + * @return Result + * - 0 Success + * - Other Fail + */ +void spi_send_data_standard(spi_device_num_t spi_num, spi_chip_select_t slave, const uint8_t *tx_buff, size_t tx_len); + +/** + * @brief Spi receive data + * + * @param[in] spi_num Spi bus number + * @param[in] chip_select Spi chip select + * @param[in] rx_buff Spi receive buffer point + * @param[in] rx_len Spi receive buffer length + * + * @return Result + * - 0 Success + * - Other Fail + */ +void spi_receive_data_standard(spi_device_num_t spi_num, spi_chip_select_t chip_select, uint8_t *rx_buff, size_t rx_len); + +/** + * @brief Spi normal send by dma + * + * @param[in] spi_num Spi bus number + * @param[in] spi_clk Spi clock rate + * + * @return The real spi clock rate + */ +uint32_t spi_set_clk_rate(spi_device_num_t spi_num, uint32_t spi_clk); + +#endif //LAB8_SPI_H diff --git a/kernel/include/vm.h b/kernel/include/vm.h new file mode 100644 index 0000000..0fa89d2 --- /dev/null +++ b/kernel/include/vm.h @@ -0,0 +1,25 @@ +#ifndef __VM_H +#define __VM_H + +#include "types.h" +#include "riscv.h" + +void kvminit(void); +void kvminithart(void); +uint64 kvmpa(uint64); +void kvmmap(uint64, uint64, uint64, int); +int mappages(pagetable_t, uint64, uint64, uint64, int); +pagetable_t uvmcreate(void); +void uvminit(pagetable_t, uchar *, uint); +uint64 uvmalloc(pagetable_t, uint64, uint64); +uint64 uvmdealloc(pagetable_t, uint64, uint64); +int uvmcopy(pagetable_t, pagetable_t, uint64); +void uvmfree(pagetable_t, uint64); +void uvmunmap(pagetable_t, uint64, uint64, int); +void uvmclear(pagetable_t, uint64); +uint64 walkaddr(pagetable_t, uint64); +int copyout(pagetable_t, uint64, char *, uint64); +int copyin(pagetable_t, char *, uint64, uint64); +int copyinstr(pagetable_t, char *, uint64, uint64); + +#endif diff --git a/kernel/main.c b/kernel/main.c index bee8764..94693e3 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -36,12 +36,12 @@ main(unsigned long hartid, unsigned long dtb_pa) timerinit(); // set up timer interrupt handler procinit(); #ifndef QEMU - device_init(dtb_pa, hartid); + //device_init(dtb_pa, hartid); fpioa_pin_init(); - // sdcard_init(); + sdcard_init(); - test_proc_init(8); // test porc init - // test_sdcard(); + //test_proc_init(8); // test porc init + test_sdcard(); // test_kalloc(); // test kalloc // test_vm(hartid); // test kernel pagetable #else diff --git a/kernel/sdcard.back.c b/kernel/sdcard.back.c new file mode 100644 index 0000000..b65b065 --- /dev/null +++ b/kernel/sdcard.back.c @@ -0,0 +1,527 @@ +#include "include/types.h" +#include "include/sdcard.h" +#include "include/spi.h" +#include "include/riscv.h" +#include "include/gpiohs.h" +#include "include/defs.h" +/* + * @brief Start Data tokens: + * Tokens (necessary because at nop/idle (and CS active) only 0xff is + * on the data/command line) + */ +#define SD_START_DATA_SINGLE_BLOCK_READ 0xFE /*!< Data token start byte, Start Single Block Read */ +#define SD_START_DATA_MULTIPLE_BLOCK_READ 0xFE /*!< Data token start byte, Start Multiple Block Read */ +#define SD_START_DATA_SINGLE_BLOCK_WRITE 0xFE /*!< Data token start byte, Start Single Block Write */ +#define SD_START_DATA_MULTIPLE_BLOCK_WRITE 0xFC /*!< Data token start byte, Start Multiple Block Write */ + +/* + * @brief Commands: CMDxx = CMD-number | 0x40 + */ +#define SD_CMD0 0 /*!< CMD0 = 0x40 */ +#define SD_CMD8 8 /*!< CMD8 = 0x48 */ +#define SD_CMD9 9 /*!< CMD9 = 0x49 */ +#define SD_CMD10 10 /*!< CMD10 = 0x4A */ +#define SD_CMD12 12 /*!< CMD12 = 0x4C */ +#define SD_CMD16 16 /*!< CMD16 = 0x50 */ +#define SD_CMD17 17 /*!< CMD17 = 0x51 */ +#define SD_CMD18 18 /*!< CMD18 = 0x52 */ +#define SD_ACMD23 23 /*!< CMD23 = 0x57 */ +#define SD_CMD24 24 /*!< CMD24 = 0x58 */ +#define SD_CMD25 25 /*!< CMD25 = 0x59 */ +#define SD_ACMD41 41 /*!< ACMD41 = 0x41 */ +#define SD_CMD55 55 /*!< CMD55 = 0x55 */ +#define SD_CMD58 58 /*!< CMD58 = 0x58 */ +#define SD_CMD59 59 /*!< CMD59 = 0x59 */ + +SD_CardInfo cardinfo; + +void sdcard_init() { + uint8 cardinfo = sd_init(); + if(cardinfo) { + panic("sd card init error\n"); + } else + { + printf("sdcard init: %d\n", cardinfo); + } +} +void SD_CS_HIGH(void) +{ + gpiohs_set_pin(7, GPIO_PV_HIGH); +} + +void SD_CS_LOW(void) +{ + gpiohs_set_pin(7, GPIO_PV_LOW); +} + +void SD_HIGH_SPEED_ENABLE(void) +{ + // spi_set_clk_rate(SPI_DEVICE_0, 10000000); +} + +static void sd_lowlevel_init(uint8 spi_index) +{ + gpiohs_set_drive_mode(7, GPIO_DM_OUTPUT); + // spi_set_clk_rate(SPI_DEVICE_0, 200000); /*set clk rate*/ +} + +static void sd_write_data(uint8 *data_buff, uint32 length) +{ + spi_init(SPI_DEVICE_0, SPI_WORK_MODE_0, SPI_FF_STANDARD, 8, 0); + spi_send_data_standard(SPI_DEVICE_0, SPI_CHIP_SELECT_3, NULL, 0, data_buff, length); +} + +static void sd_read_data(uint8 *data_buff, uint32 length) +{ + + spi_init(SPI_DEVICE_0, SPI_WORK_MODE_0, SPI_FF_STANDARD, 8, 0); + spi_receive_data_standard(SPI_DEVICE_0, SPI_CHIP_SELECT_3, NULL, 0, data_buff, length); + +} + + +/* + * @brief Send 5 bytes command to the SD card. + * @param Cmd: The user expected command to send to SD card. + * @param Arg: The command argument. + * @param Crc: The CRC. + * @retval None + */ +static void sd_send_cmd(uint8 cmd, uint32 arg, uint8 crc) +{ + uint8 frame[6]; + /*!< Construct byte 1 */ + frame[0] = (cmd | 0x40); + /*!< Construct byte 2 */ + frame[1] = (uint8)(arg >> 24); + /*!< Construct byte 3 */ + frame[2] = (uint8)(arg >> 16); + /*!< Construct byte 4 */ + frame[3] = (uint8)(arg >> 8); + /*!< Construct byte 5 */ + frame[4] = (uint8)(arg); + /*!< Construct CRC: byte 6 */ + frame[5] = (crc); + /*!< SD chip select low */ + SD_CS_LOW(); + /*!< Send the Cmd bytes */ + sd_write_data(frame, 6); +} + +/* + * @brief Send 5 bytes command to the SD card. + * @param Cmd: The user expected command to send to SD card. + * @param Arg: The command argument. + * @param Crc: The CRC. + * @retval None + */ +static void sd_end_cmd(void) +{ + uint8 frame[1] = {0xFF}; + /*!< SD chip select high */ + SD_CS_HIGH(); + /*!< Send the Cmd bytes */ + sd_write_data(frame, 1); +} + +/* + * @brief Returns the SD response. + * @param None + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ + static uint8 sd_get_response(void) +{ + uint8 result; + uint16 timeout = 0x0FFF; + /*!< Check if response is got or a timeout is happen */ + while (timeout--) { + sd_read_data(&result, 1); + // sd_read_data_dma(&result); + /*!< Right response got */ + if (result != 0xFF) + return result; + } + /*!< After time out */ + return 0xFF; +} + +/* + * @brief Get SD card data response. + * @param None + * @retval The SD status: Read data response xxx01 + * - status 010: Data accecpted + * - status 101: Data rejected due to a crc error + * - status 110: Data rejected due to a Write error. + * - status 111: Data rejected due to other error. + */ +static uint8 sd_get_dataresponse(void) +{ + uint8 response; + /*!< Read resonse */ + sd_read_data(&response, 1); + /*!< Mask unused bits */ + response &= 0x1F; + if (response != 0x05) + return 0xFF; + /*!< Wait null data */ + sd_read_data(&response, 1); + while (response == 0) + sd_read_data(&response, 1); + /*!< Return response */ + return 0; +} + +/* + * @brief Read the CSD card register + * Reading the contents of the CSD register in SPI mode is a simple + * read-block transaction. + * @param SD_csd: pointer on an SCD register structure + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ +static uint8 sd_get_csdregister(SD_CSD *SD_csd) +{ + uint8 csd_tab[18]; + /*!< Send CMD9 (CSD register) or CMD10(CSD register) */ + sd_send_cmd(SD_CMD9, 0, 0); + /*!< Wait for response in the R1 format (0x00 is no errors) */ + if (sd_get_response() != 0x00) { + sd_end_cmd(); + return 0xFF; + } + if (sd_get_response() != SD_START_DATA_SINGLE_BLOCK_READ) { + sd_end_cmd(); + return 0xFF; + } + /*!< Store CSD register value on csd_tab */ + /*!< Get CRC bytes (not really needed by us, but required by SD) */ + sd_read_data(csd_tab, 18); + sd_end_cmd(); + /*!< Byte 0 */ + SD_csd->CSDStruct = (csd_tab[0] & 0xC0) >> 6; + SD_csd->SysSpecVersion = (csd_tab[0] & 0x3C) >> 2; + SD_csd->Reserved1 = csd_tab[0] & 0x03; + /*!< Byte 1 */ + SD_csd->TAAC = csd_tab[1]; + /*!< Byte 2 */ + SD_csd->NSAC = csd_tab[2]; + /*!< Byte 3 */ + SD_csd->MaxBusClkFrec = csd_tab[3]; + /*!< Byte 4 */ + SD_csd->CardComdClasses = csd_tab[4] << 4; + /*!< Byte 5 */ + SD_csd->CardComdClasses |= (csd_tab[5] & 0xF0) >> 4; + SD_csd->RdBlockLen = csd_tab[5] & 0x0F; + /*!< Byte 6 */ + SD_csd->PartBlockRead = (csd_tab[6] & 0x80) >> 7; + SD_csd->WrBlockMisalign = (csd_tab[6] & 0x40) >> 6; + SD_csd->RdBlockMisalign = (csd_tab[6] & 0x20) >> 5; + SD_csd->DSRImpl = (csd_tab[6] & 0x10) >> 4; + SD_csd->Reserved2 = 0; /*!< Reserved */ + SD_csd->DeviceSize = (csd_tab[6] & 0x03) << 10; + /*!< Byte 7 */ + SD_csd->DeviceSize = (csd_tab[7] & 0x3F) << 16; + /*!< Byte 8 */ + SD_csd->DeviceSize |= csd_tab[8] << 8; + /*!< Byte 9 */ + SD_csd->DeviceSize |= csd_tab[9]; + /*!< Byte 10 */ + SD_csd->EraseGrSize = (csd_tab[10] & 0x40) >> 6; + SD_csd->EraseGrMul = (csd_tab[10] & 0x3F) << 1; + /*!< Byte 11 */ + SD_csd->EraseGrMul |= (csd_tab[11] & 0x80) >> 7; + SD_csd->WrProtectGrSize = (csd_tab[11] & 0x7F); + /*!< Byte 12 */ + SD_csd->WrProtectGrEnable = (csd_tab[12] & 0x80) >> 7; + SD_csd->ManDeflECC = (csd_tab[12] & 0x60) >> 5; + SD_csd->WrSpeedFact = (csd_tab[12] & 0x1C) >> 2; + SD_csd->MaxWrBlockLen = (csd_tab[12] & 0x03) << 2; + /*!< Byte 13 */ + SD_csd->MaxWrBlockLen |= (csd_tab[13] & 0xC0) >> 6; + SD_csd->WriteBlockPaPartial = (csd_tab[13] & 0x20) >> 5; + SD_csd->Reserved3 = 0; + SD_csd->ContentProtectAppli = (csd_tab[13] & 0x01); + /*!< Byte 14 */ + SD_csd->FileFormatGrouop = (csd_tab[14] & 0x80) >> 7; + SD_csd->CopyFlag = (csd_tab[14] & 0x40) >> 6; + SD_csd->PermWrProtect = (csd_tab[14] & 0x20) >> 5; + SD_csd->TempWrProtect = (csd_tab[14] & 0x10) >> 4; + SD_csd->FileFormat = (csd_tab[14] & 0x0C) >> 2; + SD_csd->ECC = (csd_tab[14] & 0x03); + /*!< Byte 15 */ + SD_csd->CSD_CRC = (csd_tab[15] & 0xFE) >> 1; + SD_csd->Reserved4 = 1; + /*!< Return the reponse */ + return 0; +} + +/* + * @brief Read the CID card register. + * Reading the contents of the CID register in SPI mode is a simple + * read-block transaction. + * @param SD_cid: pointer on an CID register structure + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ +static uint8 sd_get_cidregister(SD_CID *SD_cid) +{ + uint8 cid_tab[18]; + /*!< Send CMD10 (CID register) */ + sd_send_cmd(SD_CMD10, 0, 0); + /*!< Wait for response in the R1 format (0x00 is no errors) */ + if (sd_get_response() != 0x00) { + sd_end_cmd(); + return 0xFF; + } + if (sd_get_response() != SD_START_DATA_SINGLE_BLOCK_READ) { + sd_end_cmd(); + return 0xFF; + } + /*!< Store CID register value on cid_tab */ + /*!< Get CRC bytes (not really needed by us, but required by SD) */ + sd_read_data(cid_tab, 18); + sd_end_cmd(); + /*!< Byte 0 */ + SD_cid->ManufacturerID = cid_tab[0]; + /*!< Byte 1 */ + SD_cid->OEM_AppliID = cid_tab[1] << 8; + /*!< Byte 2 */ + SD_cid->OEM_AppliID |= cid_tab[2]; + /*!< Byte 3 */ + SD_cid->ProdName1 = cid_tab[3] << 24; + /*!< Byte 4 */ + SD_cid->ProdName1 |= cid_tab[4] << 16; + /*!< Byte 5 */ + SD_cid->ProdName1 |= cid_tab[5] << 8; + /*!< Byte 6 */ + SD_cid->ProdName1 |= cid_tab[6]; + /*!< Byte 7 */ + SD_cid->ProdName2 = cid_tab[7]; + /*!< Byte 8 */ + SD_cid->ProdRev = cid_tab[8]; + /*!< Byte 9 */ + SD_cid->ProdSN = cid_tab[9] << 24; + /*!< Byte 10 */ + SD_cid->ProdSN |= cid_tab[10] << 16; + /*!< Byte 11 */ + SD_cid->ProdSN |= cid_tab[11] << 8; + /*!< Byte 12 */ + SD_cid->ProdSN |= cid_tab[12]; + /*!< Byte 13 */ + SD_cid->Reserved1 |= (cid_tab[13] & 0xF0) >> 4; + SD_cid->ManufactDate = (cid_tab[13] & 0x0F) << 8; + /*!< Byte 14 */ + SD_cid->ManufactDate |= cid_tab[14]; + /*!< Byte 15 */ + SD_cid->CID_CRC = (cid_tab[15] & 0xFE) >> 1; + SD_cid->Reserved2 = 1; + /*!< Return the reponse */ + return 0; +} + +/* + * @brief Returns information about specific card. + * @param cardinfo: pointer to a SD_CardInfo structure that contains all SD + * card information. + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ +static uint8 sd_get_cardinfo(SD_CardInfo *cardinfo) +{ + if (sd_get_csdregister(&(cardinfo->SD_csd))) + return 0xFF; + if (sd_get_cidregister(&(cardinfo->SD_cid))) + return 0xFF; + cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) * 1024; + cardinfo->CardBlockSize = 1 << (cardinfo->SD_csd.RdBlockLen); + cardinfo->CardCapacity *= cardinfo->CardBlockSize; + /*!< Returns the reponse */ + return 0; +} + +/* + * @brief Initializes the SD/SD communication. + * @param None + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ +uint8 sd_init(void) +{ + uint8 frame[10], index, result; + /*!< Initialize SD_SPI */ + sd_lowlevel_init(0); + /*!< SD chip select high */ + SD_CS_HIGH(); + /*!< Send dummy byte 0xFF, 10 times with CS high */ + /*!< Rise CS and MOSI for 80 clocks cycles */ + /*!< Send dummy byte 0xFF */ + for (index = 0; index < 10; index++) + frame[index] = 0xFF; + sd_write_data(frame, 10); + /*------------Put SD in SPI mode--------------*/ + /*!< SD initialized and set to SPI mode properly */ + + index = 0xFF; + while (index--) { + sd_send_cmd(SD_CMD0, 0, 0x95); + // printf("get_response: %d\n", index); + result = sd_get_response(); + sd_end_cmd(); + if (result == 0x01) + break; + } + if (index == 0) + { + printf("SD_CMD0 is %X\n", result); + return 0xFF; + } + + sd_send_cmd(SD_CMD8, 0x01AA, 0x87); + /*!< 0x01 or 0x05 */ + result = sd_get_response(); + sd_read_data(frame, 4); + sd_end_cmd(); + if (result != 0x01) + { + printf("SD_CMD8 is %X\n", result); + return 0xFF; + } + index = 0xFF; + while (index--) { + sd_send_cmd(SD_CMD55, 0, 0); + result = sd_get_response(); + sd_end_cmd(); + if (result != 0x01) + return 0xFF; + sd_send_cmd(SD_ACMD41, 0x40000000, 0); + result = sd_get_response(); + sd_end_cmd(); + if (result == 0x00) + break; + } + if (index == 0) + { + printf("SD_CMD55 is %X\n", result); + return 0xFF; + } + index = 255; + while(index--){ + sd_send_cmd(SD_CMD58, 0, 1); + result = sd_get_response(); + sd_read_data(frame, 4); + sd_end_cmd(); + if(result == 0){ + break; + } + } + if(index == 0) + { + printf("SD_CMD58 is %X\n", result); + return 0xFF; + } + if ((frame[0] & 0x40) == 0) + return 0xFF; + SD_HIGH_SPEED_ENABLE(); + return sd_get_cardinfo(&cardinfo); +} + +/* + * @brief Reads a block of data from the SD. + * @param data_buff: pointer to the buffer that receives the data read from the + * SD. + * @param sector: SD's internal address to read from. + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ +uint8 sd_read_sector(uint8 *data_buff, uint32 sector, uint32 count) +{ + uint8 frame[2], flag; + /*!< Send CMD17 (SD_CMD17) to read one block */ + if (count == 1) { + flag = 0; + sd_send_cmd(SD_CMD17, sector, 0); + } else { + flag = 1; + sd_send_cmd(SD_CMD18, sector, 0); + } + /*!< Check if the SD acknowledged the read block command: R1 response (0x00: no errors) */ + if (sd_get_response() != 0x00) { + sd_end_cmd(); + return 0xFF; + } + while (count) { + if (sd_get_response() != SD_START_DATA_SINGLE_BLOCK_READ) + break; + /*!< Read the SD block data : read NumByteToRead data */ + sd_read_data(data_buff, 512); + /*!< Get CRC bytes (not really needed by us, but required by SD) */ + sd_read_data(frame, 2); + data_buff += 512; + count--; + } + sd_end_cmd(); + if (flag) { + sd_send_cmd(SD_CMD12, 0, 0); + sd_get_response(); + sd_end_cmd(); + sd_end_cmd(); + } + /*!< Returns the reponse */ + return count > 0 ? 0xFF : 0; +} + +/* + * @brief Writes a block on the SD + * @param data_buff: pointer to the buffer containing the data to be written on + * the SD. + * @param sector: address to write on. + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ +uint8 sd_write_sector(uint8 *data_buff, uint32 sector, uint32 count) +{ + uint8 frame[2] = {0xFF}; + + if (count == 1) { + frame[1] = SD_START_DATA_SINGLE_BLOCK_WRITE; + sd_send_cmd(SD_CMD24, sector, 0); + } else { + frame[1] = SD_START_DATA_MULTIPLE_BLOCK_WRITE; + sd_send_cmd(SD_ACMD23, count, 0); + sd_get_response(); + sd_end_cmd(); + sd_send_cmd(SD_CMD25, sector, 0); + } + /*!< Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */ + if (sd_get_response() != 0x00) { + sd_end_cmd(); + return 0xFF; + } + while (count--) { + /*!< Send the data token to signify the start of the data */ + sd_write_data(frame, 2); + /*!< Write the block data to SD : write count data by block */ + sd_write_data(data_buff, 512); + /*!< Put CRC bytes (not really needed by us, but required by SD) */ + sd_write_data(frame, 2); + data_buff += 512; + /*!< Read data response */ + if (sd_get_dataresponse() != 0x00) { + sd_end_cmd(); + return 0xFF; + } + } + sd_end_cmd(); + sd_end_cmd(); + /*!< Returns the reponse */ + return 0; +} + diff --git a/kernel/sdcard.example.c b/kernel/sdcard.example.c new file mode 100644 index 0000000..11beca1 --- /dev/null +++ b/kernel/sdcard.example.c @@ -0,0 +1,582 @@ +// +// Created by lumin on 2020/11/2. +// +#include +#include +#include +#include +#include +#include + +#define SECTOR_SIZE (512) + +#define SD_SPI_CLK (200000UL) +#define SD_SPI_HIGH_SPEED_CLK (25000000UL) +#define SD_SPI_DEVICE SPI_DEVICE_1 +#define SD_SPI_WORK_MODE SPI_WORK_MODE_0 +#define SD_SPI_FRAME_FORMAT SPI_FF_STANDARD +#define SD_SPI_DATA_BIT_LENGTH 8 +#define SD_SPI_ENDIAN 0 +#define SD_SPI_CHIP_SELECT SPI_CHIP_SELECT_1 + +#define SD_CMD0_ARGS (0) +#define SD_CMD0_CRC (0x95) +#define SD_CMD55_ARGS (0) +#define SD_CMD55_CRC (0) +#define SD_ACMD41_CRC (0) +#define SD_CMD58_ARGS (0) +#define SD_CMD58_CRC (1) +#define SD_CMD10_ARGS (0) +#define SD_CMD10_CRC (0) +#define SD_VOLTAGE_SELECT (0x0100) +#define SD_CHECK_PATTERN (0xAA) +#define SD_CMD8_CRC (0x87) +#define SD_HIGH_CAPACITY (0x40000000) +#define SD_ERROR_RES (0xFF) +#define SD_TIMEMOUT_COUNT (0xFF) + +static void sdcard_pin_mapping_init(); +static void sdcard_gpiohs_init(); +static void sdcard_spi_init(); +static void sd_SPI_mode_prepare(); +static void SD_CS_HIGH(); +static void SD_CS_LOW(); +static void SD_HIGH_SPEED_ENABLE(); +static void sd_write_data(uint8_t *frame, uint32_t length); +static void sd_read_data(uint8_t *frame, uint32_t length); +static void sd_send_cmd(uint8_t cmd, uint32_t args, uint8_t crc); +static void sd_end_cmd(); +static uint8_t sd_get_R1_response(); +static void sd_get_R3_rest_response(uint8_t *frame); +static void sd_get_R7_rest_response(uint8_t *frame); +static uint8_t sd_get_data_write_response(); +static int sd_switch_to_SPI_mode(); +static int sd_negotiate_voltage(); +static int sd_set_SDXC_capacity(); +static int sd_query_capacity_status(); +static int sd_get_cardinfo(SD_CardInfo *cardinfo); +static int sd_get_csdregister(SD_CSD *SD_csd); +static int sd_get_cidregister(SD_CID *SD_cid); +static void sd_test(); + +sdcard_hardware_pin_config_t config = { + 28, 26, 27, 29, 29 +}; + +SD_CardInfo cardinfo; + +void sd_init() +{ + cardinfo.active = 0; + + // Part 1: Hardware infrastructure configuration + sdcard_pin_mapping_init(); + sdcard_gpiohs_init(); + sdcard_spi_init(); + + // Part 2: SD protocol(SPI Mode) + sd_SPI_mode_prepare(); + + if(sd_switch_to_SPI_mode() != 0) + return; + if(sd_negotiate_voltage() != 0) + return; + if(sd_set_SDXC_capacity() != 0) + return; + if(sd_query_capacity_status() != 0) + return; + + SD_HIGH_SPEED_ENABLE(); + + if(sd_get_cardinfo(&cardinfo) != 0) + return; + + // Part 3: Read/Write test +// sd_test(); + cprintf("Successfully initialize SD card!\n"); +} + +uint8_t sd_read_sector(uint8_t *data_buff, uint32_t sector, uint32_t count) +{ + uint8_t dummy_crc[2], multiple; + if (count == 1){ + multiple = 0; + sd_send_cmd(SD_CMD17, sector, 0); + } else{ + multiple = 1; + sd_send_cmd(SD_CMD18, sector, 0); + } + + uint32_t result = sd_get_R1_response(); + if (result != SD_TRANS_MODE_RESULT_OK) { + cprintf("Read sector(s) CMD error! result = %d\n", result); + return SD_ERROR_RES; + } + + while (count) + { + result = sd_get_R1_response(); + if (result != SD_START_DATA_READ_RESPONSE) { + cprintf("Data read response error! result = %d\n", result); + return SD_ERROR_RES; + } + + sd_read_data(data_buff, SECTOR_SIZE); + sd_read_data(dummy_crc, 2); + + data_buff += SECTOR_SIZE; + count--; + } + + sd_end_cmd(); + if (multiple){ + sd_send_cmd(SD_CMD12, 0, 0); + sd_get_R1_response(); + sd_end_cmd(); + sd_end_cmd(); + } + + if (count > 0) { + cprintf("Not all sectors are read!\n"); + return SD_ERROR_RES; + } + + return 0; +} + +uint8_t sd_write_sector(uint8_t *data_buff, uint32_t sector, uint32_t count) +{ + assert(((uint32_t)data_buff) % 4 == 0); + uint8_t token[2] = {0xFF, 0x00}, dummpy_crc[2] = {0xFF, 0xFF}; + + if (count == 1){ + token[1] = SD_START_DATA_SINGLE_BLOCK_WRITE_TOKEN; + sd_send_cmd(SD_CMD24, sector, 0); + } else{ + token[1] = SD_START_DATA_MULTIPLE_BLOCK_WRITE_TOKEN; + sd_send_cmd(SD_ACMD23, count, 0); + sd_get_R1_response(); + sd_end_cmd(); + sd_send_cmd(SD_CMD25, sector, 0); + } + + if (sd_get_R1_response() != SD_TRANS_MODE_RESULT_OK) { + cprintf("Write sector(s) CMD error!\n"); + return SD_ERROR_RES; + } + + while (count) + { + sd_write_data(token, 2); + sd_write_data(data_buff, SECTOR_SIZE); + sd_write_data(dummpy_crc, 2); + + if (sd_get_data_write_response() != SD_TRANS_MODE_RESULT_OK) { + cprintf("Data write response error!\n"); + return SD_ERROR_RES; + } + + data_buff += SECTOR_SIZE; + count--; + } + + sd_end_cmd(); + sd_end_cmd(); + + if (count > 0) { + cprintf("Not all sectors are written!\n"); + return SD_ERROR_RES; + } + + return 0; +} + +static void sdcard_pin_mapping_init() +{ + fpioa_set_function(config.sclk_pin, FUNC_SPI1_SCLK); + fpioa_set_function(config.mosi_pin, FUNC_SPI1_D0); + fpioa_set_function(config.miso_pin, FUNC_SPI1_D1); + fpioa_set_function(config.cs_pin, FUNC_GPIOHS0 + config.cs_gpio_num); +} + +static void sdcard_gpiohs_init() +{ + gpiohs_set_drive_mode(config.cs_gpio_num, GPIO_DM_OUTPUT); +} + +static void sdcard_spi_init() +{ + spi_set_clk_rate(SD_SPI_DEVICE, SD_SPI_CLK); +} + +static void sd_SPI_mode_prepare() +{ + /*!< Send dummy byte 0xFF, 10 times with CS high */ + /*!< Rise CS and MOSI for 80 clocks cycles */ + /*!< Send dummy byte 0xFF */ + uint8_t frame[10]; + for (int i = 0; i < 10; i++) + frame[i] = SD_EMPTY_FILL; + /*!< SD chip select high */ + SD_CS_HIGH(); + sd_write_data(frame, 10); +} + +static void SD_CS_HIGH() +{ + gpiohs_set_pin_output_value(config.cs_gpio_num, GPIO_PV_HIGH); +} + +static void SD_CS_LOW() +{ + gpiohs_set_pin_output_value(config.cs_gpio_num, GPIO_PV_LOW); +} + +static void SD_HIGH_SPEED_ENABLE() +{ + spi_set_clk_rate(SD_SPI_DEVICE, SD_SPI_HIGH_SPEED_CLK); +} + +static void sd_write_data(uint8_t *frame, uint32_t length) +{ + spi_init(SD_SPI_DEVICE, SD_SPI_WORK_MODE, + SD_SPI_FRAME_FORMAT, SD_SPI_DATA_BIT_LENGTH, SD_SPI_ENDIAN); + spi_send_data_standard(SD_SPI_DEVICE, SD_SPI_CHIP_SELECT, frame, length); +} + +static void sd_read_data(uint8_t *frame, uint32_t length) +{ + spi_init(SD_SPI_DEVICE, SD_SPI_WORK_MODE, + SD_SPI_FRAME_FORMAT, SD_SPI_DATA_BIT_LENGTH, SD_SPI_ENDIAN); + spi_receive_data_standard(SD_SPI_DEVICE, SD_SPI_CHIP_SELECT, frame, length); +} + +static void sd_send_cmd(uint8_t cmd, uint32_t args, uint8_t crc) +{ + // prepare cmd frame + uint8_t frame[SD_CMD_FRAME_SIZE]; + frame[SD_CMD_CMD_BIT] = (SD_CMD_SIGN | cmd); + frame[SD_CMD_ARG_MSB0] = (uint8_t)(args >> 24); + frame[SD_CMD_ARG_MSB1] = (uint8_t)(args >> 16); + frame[SD_CMD_ARG_MSB2] = (uint8_t)(args >> 8); + frame[SD_CMD_ARG_MSB3] = (uint8_t)args; + frame[SD_CMD_CRC_BIT] = crc; + + // real send operation + SD_CS_LOW(); + sd_write_data(frame,SD_CMD_FRAME_SIZE); +} + +static void sd_end_cmd() +{ + uint8_t fill = SD_EMPTY_FILL; + SD_CS_HIGH(); + sd_write_data(&fill, 1); +} + +static uint8_t sd_get_R1_response() +{ + uint8_t result; + + uint16_t timeout = SD_TIMEMOUT_COUNT; + while (timeout--) + { + sd_read_data(&result, 1); + if (result != SD_EMPTY_FILL) + return result; + } + return SD_ERROR_RES; +} + +static void sd_get_R3_rest_response(uint8_t *frame) +{ + sd_read_data(frame, SD_R3_RESPONSE_REST_LENGTH); +} + +static void sd_get_R7_rest_response(uint8_t *frame) +{ + sd_read_data(frame, SD_R7_RESPONSE_REST_LENGTH); +} + +static uint8_t sd_get_data_write_response() +{ + uint8_t result; + sd_read_data(&result, 1); + + // protocol defined correct response + if ((result & 0x1F) != 0x05) + return SD_ERROR_RES; + + do { + sd_read_data(&result, 1); + } while (result == SD_TRANS_MODE_RESULT_OK); + + return SD_TRANS_MODE_RESULT_OK; +} + +static int sd_switch_to_SPI_mode() +{ + uint8_t result; + sd_send_cmd(SD_CMD0, SD_CMD0_ARGS, SD_CMD0_CRC); + result = sd_get_R1_response(); + sd_end_cmd(); + + if (result != SD_INIT_MODE_RESULT_OK){ + cprintf("Fail to connect to SD card!\n"); + return SD_ERROR_RES; + } + return 0; +} + +static int sd_negotiate_voltage() +{ + uint8_t frame[SD_R7_RESPONSE_REST_LENGTH],result; + sd_send_cmd(SD_CMD8, SD_VOLTAGE_SELECT | SD_CHECK_PATTERN, SD_CMD8_CRC); + result = sd_get_R1_response(); + sd_get_R7_rest_response(frame); + sd_end_cmd(); + + if (result != SD_INIT_MODE_RESULT_OK){ + cprintf("Fail to negotiate voltage with SD card!\n"); + return SD_ERROR_RES; + } + return 0; +} + +static int sd_set_SDXC_capacity() +{ + uint8_t result; + + uint16_t timeout = SD_TIMEMOUT_COUNT; + while (timeout--) + { + sd_send_cmd(SD_CMD55, SD_CMD55_ARGS, SD_CMD55_CRC); + result = sd_get_R1_response(); + sd_end_cmd(); + if (result != SD_INIT_MODE_RESULT_OK){ + cprintf("Fail to prepare application command when set SDXC capacity!\n"); + return SD_ERROR_RES; + } + + sd_send_cmd(SD_ACMD41, SD_HIGH_CAPACITY, SD_ACMD41_CRC); + result = sd_get_R1_response(); + sd_end_cmd(); + if (result == SD_TRANS_MODE_RESULT_OK) + return 0; + } + cprintf("Timeout to set card capacity!\n"); + return SD_ERROR_RES; +} + +static int sd_query_capacity_status() +{ + uint8_t frame[SD_R3_RESPONSE_REST_LENGTH], result; + + uint16_t timeout = SD_TIMEMOUT_COUNT; + while (timeout--) + { + sd_send_cmd(SD_CMD58, SD_CMD58_ARGS, SD_CMD58_CRC); + result = sd_get_R1_response(); + sd_get_R3_rest_response(frame); + sd_end_cmd(); + if (result == SD_TRANS_MODE_RESULT_OK) + break; + } + if (timeout == 0) { + cprintf("Timeout to query card capacity status!\n"); + return SD_ERROR_RES; + } + // protocol defined correct response format + if ((frame[0] & 0x40) == 0) { + cprintf("SDXC card capacity status wrong!\n"); + return SD_ERROR_RES; + } + return 0; +} + +static int sd_get_cardinfo(SD_CardInfo *cardinfo) +{ + if(sd_get_csdregister(&cardinfo->SD_csd) != 0) + return SD_ERROR_RES; + if(sd_get_cidregister(&cardinfo->SD_cid) != 0) + return SD_ERROR_RES; + + cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) * 2 * SECTOR_SIZE; + cardinfo->CardBlockSize = (1 << (cardinfo->SD_csd.RdBlockLen)); + cardinfo->CardCapacity *= cardinfo->CardBlockSize; + cardinfo->active = 1; + + return 0; +} + +static int sd_get_csdregister(SD_CSD *SD_csd) +{ + uint8_t csd_frame[18]; + + // protocol + sd_send_cmd(SD_CMD9, 0, 0); + if (sd_get_R1_response() != SD_TRANS_MODE_RESULT_OK) { + cprintf("Get CSD cmd error!\n"); + return SD_ERROR_RES; + } + if (sd_get_R1_response() != SD_START_DATA_READ_RESPONSE) { + cprintf("Get CSD data response error!\n"); + return SD_ERROR_RES; + } + + // retrieve data, 16 for SD_CID, 2 for CRC + sd_read_data(csd_frame, 18); + sd_end_cmd(); + + // set field + SD_csd->CSDStruct = csd_frame[0] >> 6; + SD_csd->SysSpecVersion = (csd_frame[0] & 0x3C) >> 2; + SD_csd->Reserved1 = csd_frame[0] & 0x03; + + SD_csd->TAAC = csd_frame[1]; + + SD_csd->NSAC = csd_frame[2]; + + SD_csd->MaxBusClkFrec = csd_frame[3]; + + SD_csd->CardComdClasses = (csd_frame[4] << 4); + + SD_csd->CardComdClasses |= (csd_frame[5] >> 4); + SD_csd->RdBlockLen = csd_frame[5] & 0x0F; + + SD_csd->PartBlockRead = csd_frame[6] >> 7; + SD_csd->WrBlockMisalign = (csd_frame[6] & 0x40) >> 6; + SD_csd->RdBlockMisalign = (csd_frame[6] & 0x20) >> 5; + SD_csd->DSRImpl = (csd_frame[6] & 0x10) >> 4; + SD_csd->Reserved2 = 0; + + SD_csd->DeviceSize = (csd_frame[7] & 0x3F) << 16; + SD_csd->DeviceSize |= csd_frame[8] << 8; + SD_csd->DeviceSize |= csd_frame[9]; + + SD_csd->EraseGrSize = (csd_frame[10] & 0x40) >> 6; + SD_csd->EraseGrMul = ((csd_frame[10] & 0x3F) << 1); + + SD_csd->EraseGrMul |= (csd_frame[11] >> 7); + SD_csd->WrProtectGrSize = csd_frame[11] & 0x7F; + + SD_csd->WrProtectGrEnable = csd_frame[12] >> 7; + SD_csd->ManDeflECC = (csd_frame[12] & 0x60) >> 5; + SD_csd->WrSpeedFact = (csd_frame[12] & 0x1C) >> 2; + SD_csd->MaxWrBlockLen = ((csd_frame[12] & 0x03) << 2); + + SD_csd->MaxWrBlockLen |= (csd_frame[13] >> 6); + SD_csd->WriteBlockPaPartial = (csd_frame[13] & 0x20) >> 5; + SD_csd->Reserved3 = 0; + SD_csd->ContentProtectAppli = csd_frame[13] & 0x01; + + SD_csd->FileFormatGrouop = csd_frame[14] >> 7; + SD_csd->CopyFlag = (csd_frame[14] & 0x40) >> 6; + SD_csd->PermWrProtect = (csd_frame[14] & 0x20) >> 5; + SD_csd->TempWrProtect = (csd_frame[14] & 0x10) >> 4; + SD_csd->FileFormat = (csd_frame[14] & 0x0C) >> 2; + SD_csd->ECC = csd_frame[14] & 0x03; + + SD_csd->CSD_CRC = csd_frame[15] >> 1; + SD_csd->Reserved4 = 1; + + return 0; +} + +static int sd_get_cidregister(SD_CID *SD_cid) { + uint8_t cid_frame[18]; + + // protocol + sd_send_cmd(SD_CMD10, SD_CMD10_ARGS, SD_CMD10_CRC); + if (sd_get_R1_response() != SD_TRANS_MODE_RESULT_OK) { + cprintf("Get CID cmd error!\n"); + return SD_ERROR_RES; + } + if (sd_get_R1_response() != SD_START_DATA_READ_RESPONSE) { + cprintf("Get CID data response error!\n"); + return SD_ERROR_RES; + } + + // retrieve data, 16 for SD_CID, 2 for CRC + sd_read_data(cid_frame, 18); + sd_end_cmd(); + + // set field + SD_cid->ManufacturerID = cid_frame[0]; + SD_cid->OEM_AppliID = (cid_frame[1] << 8); + SD_cid->OEM_AppliID |= cid_frame[2]; + SD_cid->ProdName1 = (cid_frame[3] << 24); + SD_cid->ProdName1 |= (cid_frame[4] << 16); + SD_cid->ProdName1 |= (cid_frame[5] << 8); + SD_cid->ProdName1 |= cid_frame[6]; + SD_cid->ProdName2 = cid_frame[7]; + SD_cid->ProdRev = cid_frame[8]; + SD_cid->ProdSN = (cid_frame[9] << 24); + SD_cid->ProdSN |= (cid_frame[10] << 16); + SD_cid->ProdSN |= (cid_frame[11] << 8); + SD_cid->ProdSN |= cid_frame[12]; + SD_cid->Reserved1 = cid_frame[13] >> 4; + SD_cid->ManufactDate = ((cid_frame[13] & 0x0F) << 8); + SD_cid->ManufactDate |= cid_frame[14]; + SD_cid->CID_CRC = cid_frame[15] >> 1; + SD_cid->Reserved2 = 1; + + return 0; +} + +static void sd_test() +{ + uint64_t num_sectors = cardinfo.CardCapacity / SECTOR_SIZE; + uint32_t sector = num_sectors - 10; + + uint8_t buffer0[2 * SECTOR_SIZE], buffer1[2 * SECTOR_SIZE], buffer2[2 * SECTOR_SIZE], buffer3[2 * SECTOR_SIZE]; + const char *msg = "Well! I've often seen a cat without a grin', thought Alice, 'but a grin without a cat! It's the most curious thing I ever saw in my life!'"; + + cprintf("Start SD card read/write test\n", sector); + + memset(buffer0, 0, SECTOR_SIZE); + memset(buffer1, 0, SECTOR_SIZE); + memset(buffer2, 0, SECTOR_SIZE); + memset(buffer3, 0, SECTOR_SIZE); + cprintf("- single sector read/write test on sector 0x%x\n", sector); + assert(sd_read_sector(buffer0, sector, 1) == 0); + cprintf("\tread sector 0x%x successfully!\n", sector); + + memcpy(buffer1, msg, strlen(msg) + 1 ); + assert(memcmp(buffer1, buffer2, SECTOR_SIZE) != 0); + assert(sd_write_sector(buffer1, sector, 1) == 0); + cprintf("\twrite sector 0x%x successfully!\n", sector); + + assert(sd_read_sector(buffer2, sector, 1) == 0); + assert(memcmp(buffer1, buffer2, SECTOR_SIZE) == 0); + cprintf("\treread sector 0x%x successfully!\n", sector); + + assert(sd_write_sector(buffer0, sector, 1) == 0); + assert(sd_read_sector(buffer3, sector, 1) == 0); + assert(memcmp(buffer0, buffer3, SECTOR_SIZE) == 0); + cprintf("\trestore sector 0x%x successfully!\n", sector); + + memset(buffer0, 0, 2 * SECTOR_SIZE); + memset(buffer1, 0, 2 * SECTOR_SIZE); + memset(buffer2, 0, 2 * SECTOR_SIZE); + memset(buffer3, 0, 2 * SECTOR_SIZE); + cprintf("- multiple sector read/write test on sector 0x%x\n", sector); + assert(sd_read_sector(buffer0, sector, 2) == 0); + cprintf("\tread sector 0x%x successfully!\n", sector); + + memcpy(buffer1, msg, strlen(msg) + 1 ); + assert(memcmp(buffer1, buffer2, 2 * SECTOR_SIZE) != 0); + assert(sd_write_sector(buffer1, sector, 2) == 0); + cprintf("\twrite sector 0x%x successfully!\n", sector); + + assert(sd_read_sector(buffer2, sector, 2) == 0); + assert(memcmp(buffer1, buffer2, 2 * SECTOR_SIZE) == 0); + cprintf("\treread sector 0x%x successfully!\n", sector); + + assert(sd_write_sector(buffer0, sector, 2) == 0); + assert(sd_read_sector(buffer3, sector, 2) == 0); + assert(memcmp(buffer0, buffer3, 2 * SECTOR_SIZE) == 0); + cprintf("\trestore sector 0x%x successfully!\n", sector); + + cprintf("SD card initialized successfully!\n"); +} \ No newline at end of file diff --git a/kernel/spi.back.c b/kernel/spi.back.c new file mode 100644 index 0000000..27a3089 --- /dev/null +++ b/kernel/spi.back.c @@ -0,0 +1,303 @@ +// SPI Protocol Implementation + +#include "include/types.h" +#include "include/spi.h" +#include "include/platform.h" +#include "include/riscv.h" +#include "include/utils.h" +#include "include/defs.h" + +volatile spi_t *const spi[4] = + { + (volatile spi_t *)SPI0_BASE_ADDR, + (volatile spi_t *)SPI1_BASE_ADDR, + (volatile spi_t *)SPI_SLAVE_BASE_ADDR, + (volatile spi_t *)SPI3_BASE_ADDR}; + +void spi_init(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_format_t frame_format, + uint64 data_bit_length, uint32 endian) +{ + // configASSERT(data_bit_length >= 4 && data_bit_length <= 32); + // configASSERT(spi_num < SPI_DEVICE_MAX && spi_num != 2); + // spi_clk_init(spi_num); + + // uint8 dfs_offset, frf_offset, work_mode_offset; + uint8 dfs_offset = 0; + uint8 frf_offset = 0; + uint8 work_mode_offset = 0; + switch(spi_num) + { + case 0: + case 1: + dfs_offset = 16; + frf_offset = 21; + work_mode_offset = 6; + break; + case 2: + // configASSERT(!"Spi Bus 2 Not Support!"); + break; + case 3: + default: + dfs_offset = 0; + frf_offset = 22; + work_mode_offset = 8; + break; + } + + switch(frame_format) + { + case SPI_FF_DUAL: + // configASSERT(data_bit_length % 2 == 0); + break; + case SPI_FF_QUAD: + // configASSERT(data_bit_length % 4 == 0); + break; + case SPI_FF_OCTAL: + // configASSERT(data_bit_length % 8 == 0); + break; + default: + break; + } + volatile spi_t *spi_adapter = spi[spi_num]; + if(spi_adapter->baudr == 0) + spi_adapter->baudr = 0x14; + spi_adapter->imr = 0x00; + spi_adapter->dmacr = 0x00; + spi_adapter->dmatdlr = 0x10; + spi_adapter->dmardlr = 0x00; + spi_adapter->ser = 0x00; + spi_adapter->ssienr = 0x00; + spi_adapter->ctrlr0 = (work_mode << work_mode_offset) | (frame_format << frf_offset) | ((data_bit_length - 1) << dfs_offset); + spi_adapter->spi_ctrlr0 = 0; + spi_adapter->endian = endian; +} + + +static void spi_set_tmod(uint8 spi_num, uint32 tmod) +{ + // configASSERT(spi_num < SPI_DEVICE_MAX); + volatile spi_t *spi_handle = spi[spi_num]; + uint8 tmod_offset = 0; + switch(spi_num) + { + case 0: + case 1: + case 2: + tmod_offset = 8; + break; + case 3: + default: + tmod_offset = 10; + break; + } + set_bit(&spi_handle->ctrlr0, 3 << tmod_offset, tmod << tmod_offset); +} + +static spi_transfer_width_t spi_get_frame_size(uint64 data_bit_length) +{ + if(data_bit_length < 8) + return SPI_TRANS_CHAR; + else if(data_bit_length < 16) + return SPI_TRANS_SHORT; + return SPI_TRANS_INT; +} + +void spi_send_data_normal(spi_device_num_t spi_num, spi_chip_select_t chip_select, const uint8 *tx_buff, uint64 tx_len) +{ + // configASSERT(spi_num < SPI_DEVICE_MAX && spi_num != 2); + + uint64 index, fifo_len; + spi_set_tmod(spi_num, SPI_TMOD_TRANS); + + volatile spi_t *spi_handle = spi[spi_num]; + + // uint8 dfs_offset; + uint8 dfs_offset = 0; + switch(spi_num) + { + case 0: + case 1: + dfs_offset = 16; + break; + case 2: + // configASSERT(!"Spi Bus 2 Not Support!"); + break; + case 3: + default: + dfs_offset = 0; + break; + } + uint32 data_bit_length = (spi_handle->ctrlr0 >> dfs_offset) & 0x1F; + spi_transfer_width_t frame_width = spi_get_frame_size(data_bit_length); + + uint8 v_misalign_flag = 0; + uint32 v_send_data; + if((uintptr_t)tx_buff % frame_width) + v_misalign_flag = 1; + + spi_handle->ssienr = 0x01; + spi_handle->ser = 1U << chip_select; + uint32 i = 0; + while(tx_len) + { + fifo_len = 32 - spi_handle->txflr; + fifo_len = fifo_len < tx_len ? fifo_len : tx_len; + switch(frame_width) + { + case SPI_TRANS_INT: + fifo_len = fifo_len / 4 * 4; + if(v_misalign_flag) + { + for(index = 0; index < fifo_len; index += 4) + { + // memcpy(&v_send_data, tx_buff + i, 4); + memmove(&v_send_data, tx_buff + i, 4); + spi_handle->dr[0] = v_send_data; + i += 4; + } + } else + { + for(index = 0; index < fifo_len / 4; index++) + spi_handle->dr[0] = ((uint32 *)tx_buff)[i++]; + } + break; + case SPI_TRANS_SHORT: + fifo_len = fifo_len / 2 * 2; + if(v_misalign_flag) + { + for(index = 0; index < fifo_len; index += 2) + { + // memcpy(&v_send_data, tx_buff + i, 2); + memmove(&v_send_data, tx_buff + i, 2); + spi_handle->dr[0] = v_send_data; + i += 2; + } + } else + { + for(index = 0; index < fifo_len / 2; index++) + spi_handle->dr[0] = ((uint16 *)tx_buff)[i++]; + } + break; + default: + for(index = 0; index < fifo_len; index++) + spi_handle->dr[0] = tx_buff[i++]; + break; + } + tx_len -= fifo_len; + } + while((spi_handle->sr & 0x05) != 0x04) + ; + spi_handle->ser = 0x00; + spi_handle->ssienr = 0x00; +} + +void spi_send_data_standard(spi_device_num_t spi_num, spi_chip_select_t chip_select, const uint8 *cmd_buff, + uint64 cmd_len, const uint8 *tx_buff, uint64 tx_len) +{ + // configASSERT(spi_num < SPI_DEVICE_MAX && spi_num != 2); + // uint8 *v_buf = malloc(cmd_len + tx_len); + uint8 *v_buf = kalloc(); + uint64 i; + for(i = 0; i < cmd_len; i++) + v_buf[i] = cmd_buff[i]; + for(i = 0; i < tx_len; i++) + v_buf[cmd_len + i] = tx_buff[i]; + + spi_send_data_normal(spi_num, chip_select, v_buf, cmd_len + tx_len); + // free((void *)v_buf); + kfree((void *)v_buf); +} + +void spi_receive_data_standard(spi_device_num_t spi_num, spi_chip_select_t chip_select, const uint8 *cmd_buff, + uint64 cmd_len, uint8 *rx_buff, uint64 rx_len) +{ + // configASSERT(spi_num < SPI_DEVICE_MAX && spi_num != 2); + uint64 index, fifo_len; + if(cmd_len == 0) + spi_set_tmod(spi_num, SPI_TMOD_RECV); + else + spi_set_tmod(spi_num, SPI_TMOD_EEROM); + volatile spi_t *spi_handle = spi[spi_num]; + + // uint8 dfs_offset; + uint8 dfs_offset = 0; + switch(spi_num) + { + case 0: + case 1: + dfs_offset = 16; + break; + case 2: + // configASSERT(!"Spi Bus 2 Not Support!"); + break; + case 3: + default: + dfs_offset = 0; + break; + } + uint32 data_bit_length = (spi_handle->ctrlr0 >> dfs_offset) & 0x1F; + spi_transfer_width_t frame_width = spi_get_frame_size(data_bit_length); + + uint32 i = 0; + uint64 v_cmd_len = cmd_len / frame_width; + uint32 v_rx_len = rx_len / frame_width; + + spi_handle->ctrlr1 = (uint32)(v_rx_len - 1); + spi_handle->ssienr = 0x01; + + while(v_cmd_len) + { + fifo_len = 32 - spi_handle->txflr; + fifo_len = fifo_len < v_cmd_len ? fifo_len : v_cmd_len; + switch(frame_width) + { + case SPI_TRANS_INT: + for(index = 0; index < fifo_len; index++) + spi_handle->dr[0] = ((uint32 *)cmd_buff)[i++]; + break; + case SPI_TRANS_SHORT: + for(index = 0; index < fifo_len; index++) + spi_handle->dr[0] = ((uint16 *)cmd_buff)[i++]; + break; + default: + for(index = 0; index < fifo_len; index++) + spi_handle->dr[0] = cmd_buff[i++]; + break; + } + spi_handle->ser = 1U << chip_select; + v_cmd_len -= fifo_len; + } + + if(cmd_len == 0) + { + spi_handle->dr[0] = 0xffffffff; + spi_handle->ser = 1U << chip_select; + } + + i = 0; + while(v_rx_len) + { + fifo_len = spi_handle->rxflr; + fifo_len = fifo_len < v_rx_len ? fifo_len : v_rx_len; + switch(frame_width) + { + case SPI_TRANS_INT: + for(index = 0; index < fifo_len; index++) + ((uint32 *)rx_buff)[i++] = spi_handle->dr[0]; + break; + case SPI_TRANS_SHORT: + for(index = 0; index < fifo_len; index++) + ((uint16 *)rx_buff)[i++] = (uint16)spi_handle->dr[0]; + break; + default: + for(index = 0; index < fifo_len; index++) + rx_buff[i++] = (uint8)spi_handle->dr[0]; + break; + } + + v_rx_len -= fifo_len; + } + + spi_handle->ser = 0x00; + spi_handle->ssienr = 0x00; +} \ No newline at end of file diff --git a/kernel/spi.example.c b/kernel/spi.example.c new file mode 100644 index 0000000..6dcb4cd --- /dev/null +++ b/kernel/spi.example.c @@ -0,0 +1,195 @@ +// +// Created by lumin on 2020/11/2. +// +#include +#include +#include +#include + +volatile spi_t *const spi[4] = + { + (volatile spi_t *)SPI0_BASE_ADDR, + (volatile spi_t *)SPI1_BASE_ADDR, + (volatile spi_t *)SPI_SLAVE_BASE_ADDR, + (volatile spi_t *)SPI3_BASE_ADDR + }; + +static void spi_clk_init(uint8_t spi_num); +static uint32_t spi_get_ctrlr0(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_format_t frame_format, size_t data_bit_length); +static void spi_set_transfer_mode(uint8_t spi_num, spi_transfer_mode_t tmode); + +void spi_init(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_format_t frame_format, + size_t data_bit_length, uint32_t endian) +{ + assert(data_bit_length >= SPI_MIN_DATA_BIT_LENGTH && data_bit_length <= SPI_MAX_DATA_BIT_LENGTH); + assert(spi_num < SPI_DEVICE_MAX && spi_num != 2); + + spi_clk_init(spi_num); + + // init spi controller + volatile spi_t *spi_controller = spi[spi_num]; + if (spi_controller->baudr == 0) + spi_controller->baudr = SPI_BAUDRATE_DEFAULT_VAL; + spi_controller->imr = SPI_INTERRUPT_DISABLE; // default disable interrupt mode + spi_controller->dmacr = SPI_DMACR_DEFAULT_VAL; + spi_controller->dmatdlr = SPI_DMATDLR_DEFAULT_VAL; + spi_controller->dmardlr = SPI_DMARDLR_DEFAULT_VAL; + spi_controller->ser = SPI_SLAVE_DISABLE; // default disable slave + spi_controller->ssienr = SPI_MASTER_DISABLE; // default disable master + spi_controller->ctrlr0 = spi_get_ctrlr0(spi_num, work_mode, frame_format, data_bit_length); + spi_controller->spi_ctrlr0 = SPI_TMOD_DEFAULT_VAL; // default transmit mode + spi_controller->endian = endian; // MSB or LSB +} + +void spi_send_data_standard(spi_device_num_t spi_num, spi_chip_select_t slave, const uint8_t *tx_buff, size_t tx_len) +{ + assert(spi_num < SPI_DEVICE_MAX && spi_num != 2); + assert(tx_len != 0); + + // set transfer mode + spi_set_transfer_mode(spi_num,SPI_TMODE_TRANS); + + // set register status, begin to transfer + volatile spi_t *spi_controller = spi[spi_num]; + spi_controller->ser = 1U << slave; + spi_controller->ssienr = SPI_MASTER_ENABLE; + + // data transmission + size_t fifo_len, i; + uint32_t cur = 0; + while (tx_len) + { + fifo_len = SPI_FIFO_CAPCITY_IN_BYTE - spi_controller->txflr; + if (tx_len < fifo_len) + fifo_len = tx_len; + + for (i = 0; i < fifo_len; i++) + spi_controller->dr[0] = tx_buff[cur++]; + tx_len -= fifo_len; + } + // finish sign + while ((spi_controller->sr & 0x05) != 0x04) + ; + + spi_controller->ser = SPI_SLAVE_DISABLE; + spi_controller->ser = SPI_MASTER_DISABLE; +} + +void spi_receive_data_standard(spi_device_num_t spi_num, spi_chip_select_t chip_select, uint8_t *rx_buff, size_t rx_len) +{ + assert(spi_num < SPI_DEVICE_MAX && spi_num != 2); + assert(rx_len != 0); + + // set transfer mode + spi_set_transfer_mode(spi_num, SPI_TMODE_RECV); + + // set register status, begin to transfer + volatile spi_t *spi_controller = spi[spi_num]; + spi_controller->ctrlr1 = (uint32_t)(rx_len - 1); + spi_controller->ssienr = SPI_MASTER_ENABLE; + spi_controller->dr[0] = 0xffffffff; + spi_controller->ser = 1U << chip_select; + + // data transmission + size_t fifo_len; + uint32_t cur = 0, i; + while (rx_len) + { + fifo_len = spi_controller->rxflr; + if (rx_len < fifo_len) + fifo_len = rx_len; + + for (i = 0; i < fifo_len; i++) + rx_buff[cur++] = spi_controller->dr[0]; + rx_len -= fifo_len; + } + + spi_controller->ser = SPI_SLAVE_DISABLE; + spi_controller->ssienr = SPI_MASTER_DISABLE; +} + +uint32_t spi_set_clk_rate(spi_device_num_t spi_num, uint32_t spi_clk) +{ + uint32_t spi_baudr = sysctl_clock_get_freq(SYSCTL_CLOCK_SPI0 + spi_num) / spi_clk; + if (spi_baudr < 2) + spi_baudr = 2; + else if (spi_baudr > 65534) + spi_baudr = 65534; + volatile spi_t *spi_controller = spi[spi_num]; + spi_controller->baudr = spi_baudr; + return sysctl_clock_get_freq(SYSCTL_CLOCK_SPI0 + spi_num) / spi_clk; +} + +static void spi_clk_init(uint8_t spi_num) +{ + assert(spi_num < SPI_DEVICE_MAX && spi_num != 2); + if (spi_num == 3) + sysctl_clock_set_clock_select(SYSCTL_CLOCK_SELECT_SPI3, 1); + sysctl_clock_enable(SYSCTL_CLOCK_SPI0 + spi_num); + sysctl_clock_set_threshold(SYSCTL_CLOCK_SPI0 + spi_num, 0); +} + +/* + * calculate ctrlr0 from passed arguments + */ +static uint32_t spi_get_ctrlr0(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_format_t frame_format, size_t data_bit_length) +{ + uint8_t work_mode_offset, data_bit_length_offset, frame_format_offset; + switch (spi_num) + { + case 0: + case 1: + work_mode_offset = SPI01_WORK_MODE_OFFSET; + data_bit_length_offset = SPI01_DATA_BIT_LENGTH_OFFSET; + frame_format_offset = SPI01_FRAME_FORMAT_OFFSET; + break; + case 3: + work_mode_offset = SPI3_WORK_MODE_OFFSET; + data_bit_length_offset = SPI3_DATA_BIT_LENGTH_OFFSET; + frame_format_offset = SPI3_FRAME_FORMAT_OFFSET; + break; + default: + break; + } + + switch (frame_format) + { + case SPI_FF_DUAL: + assert(data_bit_length % 2 == 0); + break; + case SPI_FF_QUAD: + assert(data_bit_length % 4 == 0); + break; + case SPI_FF_OCTAL: + assert(data_bit_length % 8 == 0); + break; + default: + break; + } + + return (frame_format << frame_format_offset) + | ((data_bit_length - 1) << data_bit_length_offset) + | (work_mode << work_mode_offset); +} + +static void spi_set_transfer_mode(uint8_t spi_num, spi_transfer_mode_t tmode) +{ + assert(spi_num < SPI_DEVICE_MAX); + + uint8_t tmode_offset; + switch (spi_num) + { + case 0: + case 1: + case 2: + tmode_offset = SPI012_TRANSFER_MODE_OFFSET; + break; + case 3: + tmode_offset = SPI3_TRANSFER_MODE_OFFSET; + break; + default: + break; + } + + set_bits_value_offset(&spi[spi_num]->ctrlr0, 3, tmode, tmode_offset); +} \ No newline at end of file diff --git a/xv6-user/_test b/xv6-user/_test new file mode 100755 index 0000000000000000000000000000000000000000..027a879ee1e1467fde4ac85eaebf15ccd6734aff GIT binary patch literal 23208 zcmeHvdwkW^mGAzYU(Peh$w^Mg3y??v5hV~rKu{(`2#~Q{2CLP8hUDY~0(p6If}mo; zBS4^9F>176VX&eyoz~81b@WzkuV}S1onG7enqG^9hjsY$;z&iUg!^54@3qf4zZ|`O z?)_)}nEmZrMlLY_?zSLMvs&DE7~F5XXScMjs0N4v6>?y^u+u^zYt;n6Wzu8 z&rQGUK2j0v%>A}bra_J zU+lwfF{k&j%3-ws>UKk3b>H{q^nM~p>e=%fwM1*d@fVfU;@1ytcNJLoUgkY0%6gsw z-QdF~hn%t=`ekJMpP`d9p8JcnK=qT9(YI_nb>M@8$6i0Ox98jL>VEf`&w0cz6-RE~FSaZ^^Ld&0OhmSS%X0e%_pCa1 zurGK2!M^t%D(mr!n|n{JtZqEm=xIK>q3X5+x4CXVx^{g1=APu(l=p4YJ}fPaEO5IH zZGBxwVt?VJz6lyW60Yezy0WVAK%=YK4G9}>JgmoWSG&a?ujgF<*|Rz07e<=Tx(;o# z7CgG3*R!W^ym(^Yo`FK~<9#E0z31##JYpm$!?&Kz8T{zD_EExGp1U1+mO|YiRJh?A6QA_Ye0E*1j~f$NS0o&4ru#`w^Mv2F{JxSh(dCwYTj+j$j|X%Go9^xP9D0BvAamfslQIVei+(p`EhyeUr}w1J0A-Qq*w`b&r?cym z11nuJlX@3*B6m0U^y&I8UGLWQ{kr~{u79WNhjjh0u79fQ!@7P=*MF<)f6?{-)b*pf zenQuOuj?P{`e|MNldj*<_5aefins_p;(cmJ=FyNE*^xg}R4JlY+~WCS?57jX)SO;& zsz)5!`q0lKzi|6Djo-AW-;?o$wa}WN=HT)9vv;^Jdj9NT7kU2Bz?N6N$oo&kp6zb& z{O=CCdOd@uUmc3<@`-+L#<{rz{oY@@%6w;sMtUcT$PGtazi@^SKUQv8@Y#9Jww^z4EFLVn;{4H$s`!DpYs1mz3t{2&?k6jI7v1$@@h#iFIPmGg zGq0ci)u}f=c=Oo1M{fS2;nSuwYfiVF5|;fM=68<2=+Mq-yZX0|5Stc<14~`utV&OR z)=Qpnc8|9|TX+XDh3MaP$*e0Lf4%p5o#Ul{D&8OY&SI-K!+!0|`AD`ue~;HM1_%5@ zgWp?t?uEYN&qR8*yCdHoa7T7L=8bH9#vk@h^+vXx%tGyn+P9c`x;AI`FppYFvmYz0m2ac{H1U4p=@T8*@q=;Ch7--zYYwjQtUY>r)g1@!aBV!=>PM7sx`p-)5egm2 zKUVa?s8eNAGwibI`S{y9GdFG;G_v1y?|MYO#rB4q%7%7UB%{o4`d%t~A z@A)gfch9T#O!xT*2Orx1U?g(vq=)TdD*PPL-#BU7kVUa?!aK*iO=sFEQG$YinDoY{ZF2A%RadNSnZJ>(Vy>j zZMx^D8M}M-^yQ1*44QTJvxG>T^;99bCt>zHefcjmkYU!_@>=Y4LV?E5nG;NgnAq0U(sHp7Q!A!jHvRG$GiO~fuj=ag z3#!G!tFNA2I&tBW>n<*>m|8xqyrOj4)M-91L6QO~`n5PhDSD=BQbJN_siEkP;wYr(-_&%^4x>~EMIS;W6#d<{ z!UuKjWK;APWikC;eduaQKIA1atpYwnwtbQ!>ohfzh}FwkgJc7h;5b!_N2pP*83M}G9%MX

|qz%NtO-JFF&M&OkW8Qu%8B-6$b1(K*Rw_{8vOQr5Q+M z`-1-rwAE`7?gb&<*1g^2%Z8{UT_&cty?WJW~(OWq_OrAV@_uU2=YnAIa1L%4b5@X ztivB^ymc~Z{5!P59iZKiLvA3}{0`7=D5fS<25YE}$l+b8RowtaD%Re+fEn=vNs&Xt zvuX=EWa~mw)lG&#Rpb&f^nJ9#(iKrZ@y)|xkuP1*By~esrjL9c{4t5RL5}VyKq@t= zg332YXeavf$ijyqBn%a`GIhS7^(RP)Wz|S_$_OgessejANXVud$-V-sOb$h-W-MjM z{b+`fHP$s!TmW+;923?zWUCUbLUKQq|G?ih5S0iJW>ewIx|ectjp}u60L^lmzM)_1 z-lx$EKJ>uY-9TEiEOH1fUsU3%g0Wd-UPD(CD6)tm0uypt>(`?_f?WCoG7ziEY-!+e zYDJJ*vjfyQ#RLDMHAgzt-uH`h&~bALY*ZR5%cym2{09F@Bh{|mPi{bu|ZLCjFRln zpe2GY5qq9Yj&DmQwfiada`JBn`X&2VY;HNXpjAc#zJa#7lAXlvl^)^+a+DTh?WMHz z1}RW?6D!inJDONuMk~J^oqjzgA;@HxXa}j!ioh`5ad{^_1~yuS&yc>JFzm_@TO>7U z%Nhfax|y6e-mCl&s~2XyR+FPr_eNVU#t9AwC+7m}o2D*JNYRsaKNYQFw zJP+W5{=CE);;qp~D1Rg9EN}KUg6&fB6-wZ5fX(o`>jBedn@K>Xfkc+?Js`4!wE0?V zttJXE$_!N@o^vtAoTgTIa&11rpF?mcw;myC&Abg%_U^~K^X}7Ze-DB%vbrKHm&@9) zwZgS~5xnJl7^J}!q@=-`rK!t}65`9DS>KRd4`elbsB|`rCY{}AE1iv6=d;~;H>c>7 ztFu&xtuDjdsEB!Sp?C0&VnGjsKS*DLeywLlD3p z0rUbWqTb>mv>r#RWHWM&%3<*KfdL+M_2P56%Qsb&+?~|>SW53s>`UKI>NR|UEy8!@ z`0ZlsedLIer?gEZ|BopWHlsHqNrD$987Z!<=nWq=2l2@e~wV* ztcQqD0ST+l5n{}@B<}^(ejM$b&Q(|&Lldce2JKwA(d79^l52M*rB_&LtqdXA+M=t_ zmg)8rYD09sZ#r15Tm*hDc&P9oX={_q1tH{ekt0e=NPocMHEXQo0BWzaJ0NA>2x&P; z^FWwQ0YS^6^ZDW4@W0kR7ItTzLNgg;p>?>#R@sZU3{ZYcq)t7Z98&12ZD$dBA?VQm>tvdx@eT^s&Uw(%68HsIq8c+J)X6 zsHVT{>wtvh5rFmSHF!6%S6aUXY8>r+D92O7Iiz_MY^?CpkVmZsI8B->M*jF?Z(2JqD-C{8+Pq|3#Mt-Dn5V5Za2KsX# zpLhZjBx`0e-QqBrQCT!lRg7q7TGtg4U-opQqEfv$?1GKKzRea6auw*y0fJ(%vAd17_*bq&%zGm0YbZCahMR^grj`P z!y!kJ1i$hU&ee%=AphADS;`aH)W+bP5{yeoR8F!C@ZCm~< zd{-E-1y1{xdxWknk4?`RmOI9AT(yclayWazO8N}w%-C3x8 z3W19b0J*lypO4yYXGmFov`b{$CPLsaus9B>wy1}ZUp3UV(O6rwWlD2fq@}AdnlkpT zmgWXE_V|X5Xr~%{XS_bHAng!3$w`NFD1V4zw~YaKaI@v2(E}zQ_;@fhR(M7Y#77P7 zrLgKyPRNoSJZQ=Aj`&GFd=@PVt5tPxaCtU{e730}UKYL7Ptv?T?^@_3#vt zCCW*?1Qkjk(+b4kl z^U2sdJy`@6${Xaw@R7+-P{vSp!4Q*BE-BgQmy!`(P05jRXW4hiWCOxyd-uUDg3gLET&8)v+~(ap5vh;dv*9#e%jhk#)BB^ZPFaG8pzQTQ!0k^ z@V4NE9eHR6Sug1$7V=MuK1=5auN-OxE;||y2)WmF(B}Vy@nnlZ9yl^FW+~tn{MbVzn1n@JVChlo6-Wv zfy?j-^VQ7%FU)@iZy+j&=974)vBh`3GP31U94Cq?U7fKhI7v)d6^TrVHFrkV&6qy9 zrMazZ{p3|`T~jKiragP8Tlh4mkH-4gDx5!X>d@y8d{d;(AN(DZ9Z+j0ElzUAVzSVj zr;Mc;g>@XC7?1u;T00V}X&`k!$Viz_dg@@6^2wD1Q)j*0)ZKcaD8}+dQ@=p3C~|k9 z-HvRK2Oy=}E!EI^K-9%aIt!Ba4%&igZ=&_jPcWZS%nnAAop&(6)KA2i_WH!2RzS1z z(2_(;2v%aXi_w}giV(1oIv_%ACJ=E8VYI!8^#g=*i9rY14u(+@AP--_^blhFY)&A# zy#Cp+f@Jt1i-Hxj{ZfPR?Csx{8$m5i+v_)mx1u7CAyM=l4nYT@1Y`dPs zLz{}Q$`R6>10lfC8BRU^z=LW!6tef>aw+6uBB68DRPM5q z1TqZ;&j*7sUY6p>7~W$+!dna-NwQc5IPR2shjZZ6Kn7*oA*$_|R8BD`_rn_e0WiBJ z`;yL_l$hCikpSgwk`;0A$jD^sfoREY+VKztiX&!_T&oZ+_)dA40wkw43{PIq==94p zWRj#oAYdQX>@uv6wxG+sFW-%at=Y*>z+J8NEiLVlVN0>|@sGc#wTqSMaEdZ3aTJpW zQW^$)p>qVaToY+P`WLLpy?LIo#;ERWg}HhYz6_>IFc0 z6|lvpAA;E$y>2U8FG3G5S#-1~m`s-1NsL)ClYk-ksVg+4^?Fm-n2}12Q zteX$IVU~pswe^|lNQZlJt?VJd&>~z)Iwqv@F)(UY8?W*a7~Gxm8yAy}W^30=r1EQG z8Jmz zadSK{hU^PT82+#{;G0A&5$92CVclV6yTmxOlM8>nQxz;-w8^z2qtA1%uPVzojPFt~ z&B7nk^Q#n9!BEu>7jZ26MV2d6g>ScIVS4AA8wvV`wDiwv{zKAYg8Z5aLaDe|vDKgoB#=~ES4xs3hPAGj~;{@??lfjk<&tLA4_ zP`R?|wW>F&epB_^s^3+;S@jk>WITrKHWO8RvE`vrW#`!ippCgSC~GkF4BgYgr+^MV=vTk0IX^kPq{7gahZ5)v;+ zmtI{l^J~8&T{^in(%Rk_ZMmv7+F0MV0qwT-$+2il{d$1)vB>JHVt9mH6`@u`^HnV! zGp09|PF}UDu>qf@lmA=kHNoH^u5(qmw(@@xE|027FU2itqF5+ZvnOKuKhg z_SV++HU*<6vUYMutUVr$#M@(HRW#nw+$dV>o7+T7J2<-9I-6Iu;ni+>|CnwD>OdS3 z1|3;l9}|3cFhyS-Oj+ID8l4i0#$&6aE$vg5#@biGs#aJ)*9`KiVePbvsiJdrdn}%+ zqNzQ$Rz%k~$3=Tbv`w@|Ve7gm)sfZ?(FH4JOi$?3U)Qgr*DpKc@KcN^JEL)GwSb?H z9UEv^ouW(H+C<;xi&(V2QA!i*=xx6GczZKG;<4t2u6R^(L0g2nNHZjm=2;@r(%y;g z=G&r4HmI+4DCYC7EuB_=+2!RGqOPu?tGOlK+*Y@)zOJPia!74dbacfd30CvOUss3k z1tNxao$3&>BFEnvYX);eJ(M@;zQ*QN%@~wy(R&M$$X zy4r|=@}Wb;mL#{f$D$GZC5%cOe&@9y^pXK;qU$>3zK5w|-V1h#sr!lufl{I>f#2zE z5To|-7C7IaXA@etOtl274i$QHj*2B}8RQqXGsMSM?bl#jo{n+(*I-MtU)g z%EW}J4Ay9rGL>7`BnTPg;iezca1G1TX;{9GHLy5K;GOE!!m{XPB`TI!!63aHO~n>> zbnoNFSVMOcgY%Q=oGK-=l9Tvk4RejKT6&3)3JXJkSrj`3CHYKH_4ZD$!BD}l z8T?SAh;jKoZklDp3??fb zzKYD(4A&aQT9(C(h^=#sY{W{#)!-!IBmTR(15y~|)3zm6G00<2qieXJw(_DGG5=*C1i=hY8eE8vIED#UKwe zeQv}w!<~k41%s@a7>6(R>ovnxU6T_S=F_A{bc@@tr*V6yru8X&nKdlO7io&?b&b(7 zNJnrgwz#c(A2-?*4ljq&7RW7r$^Z*r6E%>7rlo|1q(Za?6NdloT8<-RaJp523epb>X^z| z<9!iXAeg}f*RbvMZM}^R=^DBi#J5B&h(UZ6CrkQp9MUrWH6e>ZR`Yckm#1SKzFPgW zGDpy_!l=M{2HBYO8izA3PsjLmG_GLAoyLeJ26@x4L<55d6I2YoU{DPV{@kFN7<|{D z8W=okP)!VeY*2L!PSD}7#rhrH+?Zry)Wjg4Q7jQ*aA|^y!J7>#!eE0zH8Hr(pc)w5 zW>8HG?lh_B#nUu>YWC@rXrD%ZoxG+o#uW@seGCW%jLF%_%jD3Dr) z=Ngwq7_8PPB4c=Kf{a75KY`+q@Hq`B{(|m(J3+-EIgvoI|9Ospk+VK-oE>5R^Vevg zGGk*vQ;$nnxiv%HqEM5^B(HdY42Mps123aOC zt}q%{9G()tOjf0lbrXZhvTjNv>n5ix9wxhv;=+fMrJC3lcUt?n5jRS2W^kLK>v8Sl z#&-?!&hBBc;{yn5C#?*#TP#t}qhVjTM8z;sv$dWz#;)ZI@`*7EXQi`~@4(;4vtpk! zUTzBPm|=3YIDubD$bzh4kYkyCgJ#A&!?>J57T^-MGnJ`#Xy_@!R?A?Ep4I8~@p1bF z!&vu;20ztxK$zeD>|-1+j@TS{5u9#Tk2+qQY7DBD!H7ZCY4N6FgA=CI9?_kw_Nz^) z9d1hPBCT(!(K~GV{y-}kXGkI}h<%r4VdLTQSVv+H$jhQH{YwHORFJR0OR$_5g!k2I z(Q9=5mBhv&8emPcjeWHY&eJG7WOVFnbSO!)Qhi-fP7|^;g6p7e&vz2)Ha|mhn(z{T zfh#@3uQmF&y8gYckLa51W@ocMSL-2bvaeO)ZZIt!?#YGEH@wTnBOL$AExFgm2Eo6H zOU3ISTqYv{+rs0(sHQFSkIx*vws{Up?U47psVLd9MIa59^wJID@M4>9e^dKxk)4V_ zi#(PgK05F0Aa|w1<5!z$@kPMnc6iu5*l3M+-giv^9zPfpr8+M7HJ~i?le=_!X98~} z#f!svmBc6F6_QeLpNJao3JtsG{gK8y@2eIAAA%n$Sb&y8kC(Xq7u)o6qDISyRg!GaPb$2t4U^-WSrBJ|u^quhEp{d3+MR^ZxRG z08jKw9P(e&c)ZY%QhufJCmeSEM&mzm;NRBxa)+Eh08jR8)%1L9IHBp^bjbO4iO&%H ze;v`3t@MPyvrB001b*aYdrpb4DA|(26 z>yAsgmhvrey+iL*i65SaPk@i)>_tD7YE=pP*DLsc48!cCbv%R_?DWsO;G^-*b?E(T zNpFew9Db%oopZ-JYcD6(4*B((K10hHr|D^bA$zJF^lcjNap2=x zj`N(teryd~84p14X`+snhqt;{^SKB6F#_ zwynKU-Hq1OT|0l?H|Nz=Em*jCNnM?}2UeG#`UbJHg{}@enmeNULQ>tO@{Q!$=9U)0 zSC^61jm7mhT;I@udsJ@07x?j+Vv@J^+<-;~@fIS3TC7gB|F3E!xG8m=Cy6Ju zROh+IF^$B9nxFAZ|E%6`F4l6ma%@9_`U%I=fzM0tYeFZzvpvPZ&*v)UcWk%APDazg zPvf78zvROF)Sq04mI~MEe4ud2pQV1)I-QIJ?6l_^;8<2NX-FT*Zs7y7?sU@9xxk4d z(oK5f9^p7TI%%7}g#UpHNDA)v@mtyC;^e3Eg%d~gPtt$0K)E49)%@v+Ae8#An`51dY04I+6eBV$MPpZm%I%%oRxo?O7JgfQd y(f+4%Eq+vN@=u3{-n=xq573lF-&EiRheAga|0_B@|Ig(A&k*7fBT)fP{{I7tb#Vd! literal 0 HcmV?d00001