# HG changeset patch # User Paul Boddie # Date 1707608396 -3600 # Node ID f96263829f7829e1608a925a2d1b7016c7193dcc # Parent 2138b8459a7b61944c8f240c169f451c2246c7f9 Added initial MMC/SD controller support. diff -r 2138b8459a7b -r f96263829f78 conf/landfall-examples/mips-x1600-info.io --- a/conf/landfall-examples/mips-x1600-info.io Sun Feb 11 00:36:08 2024 +0100 +++ b/conf/landfall-examples/mips-x1600-info.io Sun Feb 11 00:39:56 2024 +0100 @@ -10,6 +10,7 @@ DMA = wrap(hw:match("x1600-dma")); GPIO = wrap(hw:match("x1600-gpio")); I2C = wrap(hw:match("x1600-i2c")); + MSC = wrap(hw:match("x1600-msc")); RTC = wrap(hw:match("x1600-rtc")); SSI = wrap(hw:match("x1600-ssi")); TCU = wrap(hw:match("x1600-tcu")); diff -r 2138b8459a7b -r f96263829f78 pkg/devices/Control --- a/pkg/devices/Control Sun Feb 11 00:36:08 2024 +0100 +++ b/pkg/devices/Control Sun Feb 11 00:39:56 2024 +0100 @@ -34,6 +34,7 @@ provides: libdrivers-keypad-qi_lb60 provides: libdrivers-lcd-headers provides: libdrivers-lcd-jz4740 +provides: libdrivers-msc provides: libdrivers-panel-ci20 provides: libdrivers-panel-headers provides: libdrivers-panel-letux400 diff -r 2138b8459a7b -r f96263829f78 pkg/devices/lib/Makefile --- a/pkg/devices/lib/Makefile Sun Feb 11 00:36:08 2024 +0100 +++ b/pkg/devices/lib/Makefile Sun Feb 11 00:39:56 2024 +0100 @@ -1,7 +1,7 @@ PKGDIR ?= .. L4DIR ?= $(PKGDIR)/../.. -TARGET := aic common cpm dma gpio hdmi i2c keypad lcd panel pwm rtc spi tcu +TARGET := aic common cpm dma gpio hdmi i2c keypad lcd msc panel pwm rtc spi tcu include $(L4DIR)/mk/subdir.mk @@ -13,6 +13,7 @@ i2c: cpm dma gpio keypad: common lcd: common +msc: common panel: lcd pwm: common rtc: common diff -r 2138b8459a7b -r f96263829f78 pkg/devices/lib/msc/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/msc/Makefile Sun Feb 11 00:39:56 2024 +0100 @@ -0,0 +1,8 @@ +PKGDIR ?= ../.. +L4DIR ?= $(PKGDIR)/../.. + +TARGET := include src + +include $(L4DIR)/mk/subdir.mk + +src: include diff -r 2138b8459a7b -r f96263829f78 pkg/devices/lib/msc/include/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/msc/include/Makefile Sun Feb 11 00:39:56 2024 +0100 @@ -0,0 +1,4 @@ +PKGDIR = ../../.. +L4DIR ?= $(PKGDIR)/../.. + +include $(L4DIR)/mk/include.mk diff -r 2138b8459a7b -r f96263829f78 pkg/devices/lib/msc/include/msc-common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/msc/include/msc-common.h Sun Feb 11 00:39:56 2024 +0100 @@ -0,0 +1,160 @@ +/* + * MSC (MMC/SD controller) peripheral support. + * + * Copyright (C) 2024 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include +#include + + + +#ifdef __cplusplus + +#include + + + +// MMC/SD structures. + +struct CID +{ + uint16_t month:4, year:8, reserved:4; + uint32_t serial; + uint8_t revision; + char name[5]; + uint16_t oem; + uint8_t manufacturer; +} __attribute__((packed)); + +struct CSD +{ + uint8_t reserved0:2, format:2, temp_write_prot:1, perm_write_prot:1, copy:1, format_group:1; + uint16_t reserved1:5, write_block_partial:1, write_blocklen:4, write_time_factor:3, + reserved2:2, write_prot_group_enable:1; + uint64_t write_prot_group_size:7, erase_sector_size:7, erase_single_block_enable:1, + device_size_multiplier:3, max_write_current_max:3, max_write_current_min:3, + max_read_current_max:3, max_read_current_min:3, device_size:12, + reserved3:2, dsr_implemented:1, read_block_misalign:1, write_block_misalign:1, + read_block_partial:1, read_blocklen:4, card_command_classes:12; + uint8_t tran_speed, data_read_access_time_2, data_read_access_time_1, + reserved4:6, csd:2; +} __attribute__((packed)); + + + +// MMC/SD controller channel. + +class Msc_channel +{ +protected: + l4_addr_t _msc_start; + Hw::Register_block<32> _regs; + l4_cap_idx_t _irq; + + // Support eight CID/CSD entries of 120 bits and RCA entries of 16 bits. + + struct CID _cid[8]; + struct CSD _csd[8]; + uint16_t _rca[8], _current_rca; + uint8_t _cards; + + // Utility methods. + + uint32_t get_field(uint32_t reg, uint32_t mask, uint8_t shift); + void set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value); + + // Low-level operations. + + void ack_irq(uint32_t flags); + void unmask_irq(uint32_t flags); + void reset(); + void start_clock(); + void stop_clock(); + + // Command properties. + + bool command_will_write(uint8_t index); + bool command_with_data(uint8_t index); + bool command_uses_busy(uint8_t index); + uint8_t get_response_format(uint8_t index); + uint8_t get_app_response_format(uint8_t index); + + // Command initiation. + + bool send_app_command(uint8_t index, uint32_t arg); + bool send_command(uint8_t index, uint32_t arg); + bool send_command(uint8_t index, uint32_t arg, uint8_t response_format, + bool data, bool write, bool busy); + + // Response handling. + + bool have_response(); + void read_response(uint16_t *buffer, uint8_t units); + bool wait_for_irq(uint32_t flags); + bool wait_for_irq(uint32_t flags, unsigned int timeout); + + // Initialisation operations. + + bool check_sd(); + void init_sdio(); + void init_sdmem(); + void init_mmc(); + void identify_cards(); + void query_cards(); + + // Transfer operations. + + uint32_t recv_data(l4re_dma_space_dma_addr_t paddr, uint32_t count); + uint32_t send_data(l4re_dma_space_dma_addr_t paddr, uint32_t count); + + virtual uint32_t transfer(l4re_dma_space_dma_addr_t from_paddr, + l4re_dma_space_dma_addr_t to_paddr, + bool recv, uint32_t count) = 0; + +public: + explicit Msc_channel(l4_addr_t msc_start, l4_addr_t start, l4_cap_idx_t irq); + + virtual ~Msc_channel(); + + void enable(); + + uint32_t get_status(); + + uint32_t read_block(uint8_t card, l4re_dma_space_dma_addr_t paddr); +}; + + + +// MMC/SD controller device control. + +class Msc_chip +{ +protected: + l4_addr_t _msc_start, _start, _end; + + virtual unsigned int num_channels() = 0; + +public: + explicit Msc_chip(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end); +}; + +#endif /* __cplusplus */ diff -r 2138b8459a7b -r f96263829f78 pkg/devices/lib/msc/include/msc-jz4780.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/msc/include/msc-jz4780.h Sun Feb 11 00:39:56 2024 +0100 @@ -0,0 +1,89 @@ +/* + * MSC (MMC/SD controller) peripheral support. + * + * Copyright (C) 2024 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include +#include + + + +#ifdef __cplusplus + +#include +#include + +// MMC/SD controller channel. + +class Msc_jz4780_channel : public Msc_channel +{ +protected: + Dma_jz4780_channel *_dma; + enum Dma_jz4780_request_type _request_type_in, _request_type_out; + +public: + explicit Msc_jz4780_channel(l4_addr_t msc_start, l4_addr_t addr, + l4_cap_idx_t irq, Dma_jz4780_channel *dma, + enum Dma_jz4780_request_type request_type_in, + enum Dma_jz4780_request_type request_type_out); + + uint32_t transfer(l4re_dma_space_dma_addr_t from_paddr, + l4re_dma_space_dma_addr_t to_paddr, + bool recv, uint32_t count); +}; + + + +// MMC/SD controller device control. + +class Msc_jz4780_chip : public Msc_chip +{ +protected: + unsigned int num_channels() + { return 3; } + +public: + explicit Msc_jz4780_chip(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end); + + Msc_channel *get_channel(uint8_t channel, l4_cap_idx_t irq, + Dma_jz4780_channel *dma); +}; + +#endif /* __cplusplus */ + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *jz4780_msc_init(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end); + +void *jz4780_msc_get_channel(void *msc, uint8_t channel, l4_cap_idx_t irq, + void *dma); + +uint32_t jz4780_msc_get_status(void *msc_channel); + +void jz4780_msc_enable(void *msc_channel); + +uint32_t jz4780_msc_read_block(void *msc_channel, uint8_t card, + l4re_dma_space_dma_addr_t paddr); + +EXTERN_C_END diff -r 2138b8459a7b -r f96263829f78 pkg/devices/lib/msc/include/msc-x1600.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/msc/include/msc-x1600.h Sun Feb 11 00:39:56 2024 +0100 @@ -0,0 +1,87 @@ +/* + * MSC (MMC/SD controller) peripheral support. + * + * Copyright (C) 2024 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include +#include + + + +#ifdef __cplusplus + +#include +#include + +// MMC/SD controller channel. + +class Msc_x1600_channel : public Msc_channel +{ +protected: + Dma_x1600_channel *_dma; + enum Dma_x1600_request_type _request_type_in, _request_type_out; + +public: + explicit Msc_x1600_channel(l4_addr_t msc_start, l4_addr_t addr, + l4_cap_idx_t irq, Dma_x1600_channel *dma, + enum Dma_x1600_request_type request_type_in, + enum Dma_x1600_request_type request_type_out); + + uint32_t transfer(l4re_dma_space_dma_addr_t from_paddr, + l4re_dma_space_dma_addr_t to_paddr, + bool recv, uint32_t count); +}; + +// MMC/SD controller device control. + +class Msc_x1600_chip : public Msc_chip +{ +protected: + unsigned int num_channels() + { return 2; } + +public: + explicit Msc_x1600_chip(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end); + + Msc_channel *get_channel(uint8_t channel, l4_cap_idx_t irq, + Dma_x1600_channel *dma); +}; + +#endif /* __cplusplus */ + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *x1600_msc_init(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end); + +void *x1600_msc_get_channel(void *msc, uint8_t channel, l4_cap_idx_t irq, + void *dma); + +uint32_t x1600_msc_get_status(void *msc_channel); + +void x1600_msc_enable(void *msc_channel); + +uint32_t x1600_msc_read_block(void *msc_channel, uint8_t card, + l4re_dma_space_dma_addr_t paddr); + +EXTERN_C_END diff -r 2138b8459a7b -r f96263829f78 pkg/devices/lib/msc/src/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/msc/src/Makefile Sun Feb 11 00:39:56 2024 +0100 @@ -0,0 +1,13 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +TARGET = libmsc.o.a libmsc.o.so +PC_FILENAME := libdrivers-msc + +SRC_CC := common.cc jz4780.cc x1600.cc + +PRIVATE_INCDIR += $(PKGDIR)/lib/msc/include + +REQUIRES_LIBS := l4re_c l4re_c-util libdrivers-common + +include $(L4DIR)/mk/lib.mk diff -r 2138b8459a7b -r f96263829f78 pkg/devices/lib/msc/src/common.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/msc/src/common.cc Sun Feb 11 00:39:56 2024 +0100 @@ -0,0 +1,1232 @@ +/* + * MSC (MMC/SD controller) peripheral support. + * + * Copyright (C) 2024 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include +#include + +#include "msc-common.h" + + + +// Register locations for each channel. + +enum Regs : unsigned +{ + Msc_control = 0x000, // MSC_CTRL + Msc_status = 0x004, // MSC_STAT + Msc_clock_rate = 0x008, // MSC_CLKRT + Msc_command_data_control = 0x00c, // MSC_CMDAT + Msc_response_timeout = 0x010, // MSC_RESTO + Msc_read_timeout = 0x014, // MSC_RDTO + Msc_block_length = 0x018, // MSC_BLKLEN + Msc_block_count = 0x01c, // MSC_NOB + Msc_block_success_count = 0x020, // MSC_SNOB + Msc_interrupt_mask = 0x024, // MSC_IMASK + Msc_interrupt_flag = 0x028, // MSC_IFLG/MSC_IREG + Msc_command_index = 0x02c, // MSC_CMD + Msc_command_argument = 0x030, // MSC_ARG + Msc_response_fifo = 0x034, // MSC_RES + Msc_recv_data_fifo = 0x038, // MSC_RXFIFO + Msc_trans_data_fifo = 0x03c, // MSC_TXFIFO + + // JZ4780/X1600 only. + + Msc_low_power_mode = 0x040, // MSC_LPM + Msc_dma_control = 0x044, // MSC_DMAC + Msc_dma_descriptor_address = 0x048, // MSC_DMANDA + Msc_dma_data_address = 0x04c, // MSC_DMADA + Msc_dma_data_length = 0x050, // MSC_DMALEN + Msc_dma_command = 0x054, // MSC_DMACMD + Msc_control2 = 0x058, // MSC_CTRL2 + Msc_rtfifo_data_counter = 0x05c, // MSC_RTCNT + + // Channel block size/offset. + + Msc_channel_offset = 0x10000, +}; + +// Field definitions. + +enum Control_bits : unsigned +{ + // JZ4780/X1600 only. + + Control_send_ccsd = 0x8000, // SEND_CCSD + Control_send_ccsd_automatically = 0x4000, // SEND_CCSD + + // Common. + + Control_exit_multiple = 0x0080, // EXIT_MULTIPLE + Control_exit_transfer = 0x0040, // EXIT_TRANSFER + Control_start_read_wait = 0x0020, // START_READ_WAIT + Control_stop_read_wait = 0x0010, // STOP_READ_WAIT + Control_reset = 0x0008, // RESET + Control_start_operation = 0x0004, // START_OP + + Control_clock_control_field_mask = 0x3, // CLOCK_CTRL + Control_clock_control_start = 2, + Control_clock_control_stop = 1, + Control_clock_control_field_shift = 0, +}; + +enum Control2_bits : unsigned +{ + // JZ4780/X1600 only. + + Control2_pin_level_polarity_field_mask = 0x1f, // PIP + Control2_pin_level_polarity_field_shift = 24, + + // JZ4780 only. + + Control2_reset_enable = 0x00800000, // RST_EN + + // JZ4780/X1600 only. + + Control2_stop_read_operation_mode = 0x00000010, // STPRM + + // JZ4780 only. + + Control2_signal_voltage_change = 0x00000008, // SVC + + // JZ4780/X1600 only. + + Control2_speed_mode_field_mask = 0x7, // SMS + Control2_speed_mode_default = 0, // = 0 + Control2_speed_mode_high = 1, // = 1 + Control2_speed_mode_sdr12 = 2, // = 2 + Control2_speed_mode_sdr25 = 3, // = 3 + Control2_speed_mode_sdr50 = 4, // = 4 + Control2_speed_mode_field_shift = 0, +}; + +enum Status_bits : unsigned +{ + // JZ4780/X1600 only. + + Status_auto_cmd12_done = 0x80000000, // AUTO_CMD12_DONE + + // JZ4780 only. + + Status_auto_cmd23_done = 0x40000000, // AUTO_CMD23_DONE + Status_signal_voltage_change = 0x20000000, // SVS + + // JZ4780/X1600 only. + + Status_pin_level_field_mask = 0x1f, // PIN_LEVEL + Status_pin_level_field_shift = 24, + + Status_boot_crc_error = 0x00100000, // BCE + Status_boot_data_end = 0x00080000, // BDE + Status_boot_ack_error = 0x00040000, // BAE + Status_boot_ack_received = 0x00020000, // BAR + Status_dma_end = 0x00010000, // DMAEND + + // Common. + + Status_resetting = 0x8000, // IS_RESETTING + Status_sdio_interrupt_active = 0x4000, // SDIO_INT_ACTIVE + Status_programming_done = 0x2000, // PRG_DONE + Status_data_transfer_done = 0x1000, // DATA_TRAN_DONE + Status_end_command_response = 0x0800, // END_CMD_RES + Status_data_fifo_almost_full = 0x0400, // DATA_FIFO_AFULL + Status_read_wait = 0x0200, // IS_READWAIT + Status_clock_enabled = 0x0100, // CLK_EN + Status_data_fifo_full = 0x0080, // DATA_FIFO_FULL + Status_data_fifo_empty = 0x0040, // DATA_FIFO_EMPTY + Status_response_crc_error = 0x0020, // CRC_RES_ERR + Status_read_crc_error = 0x0010, // CRC_READ_ERROR + Status_write_crc_error_no_status = 0x0008, // CRC_WRITE_ERROR (2) + Status_write_crc_error_data = 0x0004, // CRC_WRITE_ERROR (1) + Status_timeout_response = 0x0002, // TIME_OUT_RES + Status_timeout_read = 0x0001, // TIME_OUT_READ +}; + +enum Clock_rate_bits : unsigned +{ + Clock_rate_field_mask = 0x7, // CLK_RATE + Clock_rate_field_shift = 0, +}; + +enum Command_data_control_bits : unsigned +{ + // JZ4780/X1600 only. + + Cdc_ccs_expected = 0x80000000, // CCS_EXPECTED + Cdc_read_ce_ata = 0x40000000, // READ_CEATA + Cdc_disable_boot = 0x08000000, // DIS_BOOT + Cdc_expect_boot_ack = 0x02000000, // EXP_BOOT_ACK + Cdc_alternative_boot_mode = 0x01000000, // BOOT_MODE + + // JZ4780 only. + + Cdc_auto_cmd23 = 0x00040000, // AUTO_CMD23 + + // JZ4780/X1600 only. + + Cdc_sdio_interrupt_2cycle = 0x00020000, // SDIO_PRDT + Cdc_auto_cmd12 = 0x00010000, // AUTO_CMD12 + + Cdc_recv_fifo_level_field_mask = 0x3, // RTRG + Cdc_fifo_level_16 = 0, + Cdc_fifo_level_32 = 1, + Cdc_fifo_level_64 = 2, + Cdc_fifo_level_96 = 3, + Cdc_recv_fifo_level_field_shift = 14, + + Cdc_trans_fifo_level_field_mask = 0x3, // TTRG + Cdc_trans_fifo_level_field_shift = 12, + + // Common. + + Cdc_io_abort = 0x0800, // IO_ABORT + + Cdc_bus_width_field_mask = 0x3, // BUS_WIDTH + Cdc_bus_width_field_1bit = 0, // = 0 + Cdc_bus_width_field_4bit = 2, // = 2 + Cdc_bus_width_field_shift = 9, + + // JZ4740 only. + + Cdc_dma_enable = 0x0100, // DMA_EN + Cdc_dma_disable = 0x0000, + + // Common. + + Cdc_init_sequence = 0x0080, // INIT + + Cdc_expect_busy = 0x0040, // BUSY + Cdc_do_not_expect_busy = 0x0000, + + Cdc_stream_block = 0x0020, // STREAM_BLOCK + Cdc_not_stream_block = 0x0000, + + Cdc_write_operation = 0x0010, // WRITE_READ + Cdc_read_operation = 0x0000, + + Cdc_data_with_command = 0x0008, // DATA_EN + Cdc_no_data_with_command = 0x0000, + + Cdc_response_format_field_mask = 0x7, // RESPONSE_FORMAT + Cdc_response_format_field_shift = 0, +}; + +enum Response_timeout_bits : unsigned +{ + // NOTE: 16-bit value in the JZ4780. + // NOTE: 32-bit value in the X1600. + + Response_timeout_mask = 0x000000ff, // RES_TO +}; + +enum Read_timeout_bits : unsigned +{ + // NOTE: 16-bit value prior to the JZ4780/X1600. + + Read_timeout_mask = 0xffffffff, // READ_TO +}; + +enum Block_length_bits : unsigned +{ + // NOTE: 16-bit value in the JZ4780/X1600. + + Block_length_mask = 0x00000fff, // BLK_LEN +}; + +enum Block_count_bits : unsigned +{ + Block_count_mask = 0x0000ffff, // NOB/SNOB +}; + +// Interrupt mask/flag bits. + +enum Interrupt_bits : unsigned +{ + // X1600 only. + + Int_dma_data_done = 0x80000000, // DMA_DATA_DONE + + // JZ4780 only. + + Int_auto_cmd23_done = 0x40000000, // AUTO_CMD23_DONE + Int_signal_voltage_change = 0x20000000, // SVS + + // JZ4780/X1600 only. + + Int_pin_level_field_mask = 0x1f, // PIN_LEVEL + Int_pin_level_field_shift = 24, + + // X1600 only. + + Int_write_request_all_done = 0x00800000, // WR_ALL_DONE + + // JZ4780/X1600 only. + + Int_boot_crc_error = 0x00100000, // BCE + Int_boot_data_end = 0x00080000, // BDE + Int_boot_ack_error = 0x00040000, // BAE + Int_boot_ack_received = 0x00020000, // BAR + Int_dma_end = 0x00010000, // DMAEND + Int_auto_cmd12_done = 0x00008000, // AUTO_CMD12_DONE + Int_data_fifo_full = 0x00004000, // DATA_FIFO_FULL + Int_data_fifo_empty = 0x00002000, // DATA_FIFO_EMP + Int_crc_response_error = 0x00001000, // CRC_RES_ERR + Int_crc_read_error = 0x00000800, // CRC_READ_ERR + Int_crc_write_error = 0x00000400, // CRC_WRITE_ERR + Int_response_timeout = 0x00000200, // TIME_OUT_RES + Int_read_timeout = 0x00000100, // TIME_OUT_READ + + // Common. + + Int_sdio = 0x80, // SDIO + Int_trans_fifo_write_request = 0x40, // TXFIFO_WR_REQ + Int_recv_fifo_read_request = 0x20, // RXFIFO_RD_REQ + Int_end_command_response = 0x04, // END_CMD_RES + Int_programming_done = 0x02, // PRG_DONE + Int_data_transfer_done = 0x01, // DATA_TRAN_DONE +}; + +enum Command_index_bits : unsigned +{ + Command_index_mask = 0x0000003f, // CMD_INDEX +}; + +enum Command_argument_bits : unsigned +{ + Command_argument_mask = 0xffffffff, // ARG +}; + +enum Response_fifo_bits : unsigned +{ + Response_fifo_mask = 0x0000ffff, // DATA +}; + +enum Recv_data_fifo_bits : unsigned +{ + Recv_data_fifo_mask = 0xffffffff, // DATA +}; + +enum Trans_data_fifo_bits : unsigned +{ + Trans_data_fifo_mask = 0xffffffff, // DATA +}; + +enum Low_power_mode_bits : unsigned +{ + Low_power_mode_enable = 0x00000001, // LPM +}; + +enum Dma_control_bits : unsigned +{ + Dma_mode_specify_transfer_length = 0x80, // MODE_SEL + + Dma_address_offset_field_mask = 0x3, // AOFST + Dma_address_offset_field_shift = 5, + + Dma_align_enable = 0x10, // ALIGNEN + + Dma_burst_type_field_mask = 0x3, // INCR + Dma_burst_type_incr16 = 0, + Dma_burst_type_incr32 = 1, + Dma_burst_type_incr64 = 2, + Dma_burst_type_field_shift = 2, + + Dma_select_common_dma = 0x02, // DMASEL + Dma_select_special_dma = 0x00, + + Dma_enable = 0x01, // DMAEN + Dma_disable = 0x00, +}; + + + +// Command indexes. + +enum Command_index : unsigned +{ + Command_go_idle_state = 0, + Command_send_op_cond = 1, + Command_all_send_cid = 2, + Command_send_relative_addr = 3, // SD + Command_set_relative_addr = 3, // MMC + Command_set_dsr = 4, + Command_io_send_op_cond = 5, // SDIO + Command_select_deselect_card = 7, + Command_send_if_cond = 8, + Command_send_csd = 9, + Command_send_cid = 10, + Command_read_dat_until_stop = 11, + Command_stop_transmission = 12, + Command_send_status = 13, + Command_go_inactive_state = 15, + Command_set_blocklen = 16, + Command_read_single_block = 17, + Command_read_multiple_block = 18, + Command_write_dat_until_stop = 20, + Command_set_block_count = 23, + Command_write_block = 24, + Command_write_multiple_block = 25, + Command_program_cid = 26, + Command_program_csd = 27, + Command_set_write_prot = 28, + Command_clr_write_prot = 29, + Command_send_write_prot = 30, + Command_tag_sector_start = 32, + Command_tag_sector_end = 33, + Command_untag_sector = 34, + Command_tag_erase_group_start = 35, + Command_tag_erase_group_end = 36, + Command_untag_erase_group = 37, + Command_erase = 38, + Command_fast_io = 39, + Command_go_irq_state = 40, + Command_lock_unlock = 42, + Command_io_rw_direct = 52, // SDIO + Command_app_cmd = 55, + Command_gen_cmd = 56, +}; + +// Application-specific command indexes, used by first issuing Command_app_cmd. + +enum App_command_index : unsigned +{ + App_command_set_bus_width = 6, + App_command_sd_status = 13, + App_command_send_num_wr_blocks = 22, + App_command_set_wr_block_erase_count = 23, + App_command_sd_send_op_cond = 41, + App_command_set_clr_card_detect = 42, + App_command_send_scr = 51, + App_command_read_ocr = 58, +}; + +enum Bus_width_bits : unsigned +{ + Bus_width_1bit = 0, + Bus_width_4bit = 2, +}; + +// Command response sizes in 16-bit units. + +enum Response_sizes : unsigned +{ + Response_size_R1 = 3, + Response_size_R2 = 8, // omits the CRC and end bit + Response_size_R3 = 3, + Response_size_R4 = 3, + Response_size_R5 = 3, + Response_size_R6 = 3, + Response_size_R7 = 3, +}; + +// SD_SEND_OP_COND argument flags. + +enum Ocr_argument_flags : unsigned +{ + Ocr_high_capacity_storage = 0x40000000, +}; + +// SD_SEND_OP_COND response flags (R3). + +enum Ocr_response_flags : unsigned +{ + Ocr_card_powered_up = 0x80000000, +}; + +// R1 status flags. + +enum R1_status_flags : unsigned +{ + R1_status_error_mask = 0xffff0000, +}; + + + +// MMC response structures. + +struct R1 +{ + uint8_t end_crc; + uint32_t status; + uint8_t index:6, trans_start:2; +} __attribute__((packed)); + +struct R2 +{ + // uint8_t end_crc; (not retrieved) + + union + { + uint8_t raw[15]; + struct CID cid; + struct CSD csd; + } payload; + + uint8_t reserved_trans_start; +} __attribute__((packed)); + +struct R3 +{ + uint8_t end_reserved; + uint32_t ocr; + uint8_t reserved_trans_start; +} __attribute__((packed)); + +// SDIO response structures. + +struct R4 +{ + uint8_t end_reserved; + uint32_t ocr:24, stuff:3, memory_present:1, number_io_functions:3, ready:1; + uint8_t reserved_trans_start; +} __attribute__((packed)); + +struct R5 +{ + uint8_t end_crc; + uint8_t data; + uint8_t out_of_range:1, invalid_function_number:1, reserved:1, error:1, + io_current_state:2, illegal_command:1, crc_error:1; + uint16_t stuff; + uint8_t index:6, trans_start:2; +} __attribute__((packed)); + +struct R6 +{ + uint8_t end_crc; + uint16_t status; + uint16_t rca; + uint8_t index:6, trans_start:2; +} __attribute__((packed)); + +struct R7 +{ + uint8_t end_crc; + + union + { + uint32_t check:8, voltage:4, reserved:20; + uint32_t raw; + } check_voltage; + + uint8_t index:6, trans_start:2; +} __attribute__((packed)); + + + +// Command frame: +// byte: start (1), direction (1), command (6) +// 4 bytes: argument +// byte: CRC (7), end (1) + +// IO_RW_DIRECT argument: +// Argument MSB to LSB: R/W (1), function number (3), read after write flag (1), +// stuff (1), register address (17), stuff (1), +// write data or stuff (8) +// 0x88000c08: W, function = 0, read after write, register address = 6 (CCCR), +// data = 8 (reset) + +const uint32_t Io_rw_direct_reset = 0x88000c08; + +// (IO_)SEND_OP_COND argument and default voltage range expected in R3, R4: +// Argument MSB to LSB: stuff (8), voltage range (16), reserved (8) +// 0x00ff8000: voltage range 2.7 - 3.6V + +const uint32_t Ocr_default_voltage_range = 0x00ff8000; + +// SEND_IF_COND argument and default voltage range expected in R7: +// Argument MSB to LSB: stuff (20), voltage supplied (4), check (8) +// 0x000001aa: voltage range 2.7 - 3.6V, check = 0b10101010 + +const uint32_t If_cond_default_voltage_range = 0x000001aa; + + + +// Channel abstraction. + +Msc_channel::Msc_channel(l4_addr_t msc_start, l4_addr_t addr, l4_cap_idx_t irq) +: _msc_start(msc_start), _irq(irq) +{ + _regs = new Hw::Mmio_register_block<32>(addr); +} + +Msc_channel::~Msc_channel() +{ +} + +// Utility methods. +// NOTE: Also defined in the CPM abstraction, should be consolidated. + +uint32_t +Msc_channel::get_field(uint32_t reg, uint32_t mask, uint8_t shift) +{ + return (_regs[reg] & (mask << shift)) >> shift; +} + +void +Msc_channel::set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value) +{ + _regs[reg] = (_regs[reg] & (~(mask << shift))) | ((mask & value) << shift); +} + +bool +Msc_channel::command_will_write(uint8_t index) +{ + // NOTE: Probably incomplete coverage. + + switch (index) + { + case Command_write_dat_until_stop: return true; + case Command_write_block: return true; + case Command_write_multiple_block: return true; + case Command_program_cid: return true; + case Command_program_csd: return true; + case Command_lock_unlock: return true; + default: return false; + } +} + +bool +Msc_channel::command_with_data(uint8_t index) +{ + // NOTE: Probably incomplete coverage. + + switch (index) + { + case Command_read_dat_until_stop: return true; + case Command_read_single_block: return true; + case Command_read_multiple_block: return true; + case Command_write_dat_until_stop: return true; + case Command_write_block: return true; + case Command_write_multiple_block: return true; + case Command_program_cid: return true; + case Command_program_csd: return true; + case Command_lock_unlock: return true; + default: return false; + } +} + +bool +Msc_channel::command_uses_busy(uint8_t index) +{ + // NOTE: Probably incomplete coverage. + + switch (index) + { + case Command_select_deselect_card: return true; + case Command_stop_transmission: return true; + default: return false; + } +} + +uint8_t +Msc_channel::get_response_format(uint8_t index) +{ + // NOTE: Probably incomplete coverage. + + switch (index) + { + // Common commands without response. + + case Command_go_idle_state: return 0; + case Command_set_dsr: return 0; + case Command_go_inactive_state: return 0; + + // Common commands with response. + + case Command_send_op_cond: return 3; + case Command_all_send_cid: return 2; + case Command_send_csd: return 2; + case Command_send_cid: return 2; + + // SDIO only. + + case Command_io_send_op_cond: return 4; + case Command_io_rw_direct: return 5; + + // SDMEM only. + + case Command_send_relative_addr: return 6; + case Command_send_if_cond: return 7; + + // All other commands. + + default: return 1; + } +} + +uint8_t +Msc_channel::get_app_response_format(uint8_t index) +{ + // NOTE: Probably incomplete coverage. + + switch (index) + { + // SDMEM only. + + case App_command_sd_send_op_cond: return 3; + + // All other commands. + + default: return 1; + } +} + +// Read a response directly from the FIFO. + +void +Msc_channel::read_response(uint16_t *buffer, uint8_t units) +{ + uint8_t unit = units; + + while (unit > 0) + { + uint32_t data = _regs[Msc_response_fifo]; + + // Ignore the upper byte of the last unit in small transfers since it is the + // lower byte from the previous unit not shifted out of the register. + + unit--; + + if ((unit == 0) && (units == 3)) + buffer[unit] = (data & 0xff) << 8; + else + buffer[unit] = data; + } +} + +void +Msc_channel::ack_irq(uint32_t flags) +{ + // Clear the flags by setting them. + + _regs[Msc_interrupt_flag] = _regs[Msc_interrupt_flag] | flags; +} + +void +Msc_channel::unmask_irq(uint32_t flags) +{ + ack_irq(flags); + + if (_regs[Msc_interrupt_mask] & flags) + _regs[Msc_interrupt_mask] = _regs[Msc_interrupt_mask] & ~flags; +} + +void +Msc_channel::reset() +{ + _regs[Msc_control] = _regs[Msc_control] | Control_reset; + + // NOTE: X1600 and other recent SoCs only. + + _regs[Msc_control] = _regs[Msc_control] & ~Control_reset; + + // Sufficient for other SoCs... + + while (_regs[Msc_status] & Status_resetting); +} + +void +Msc_channel::start_clock() +{ + set_field(Msc_control, Control_clock_control_field_mask, + Control_clock_control_field_shift, Control_clock_control_start); + + while (!(_regs[Msc_status] & Status_clock_enabled)); +} + +void +Msc_channel::stop_clock() +{ + set_field(Msc_control, Control_clock_control_field_mask, + Control_clock_control_field_shift, Control_clock_control_stop); + + while (_regs[Msc_status] & Status_clock_enabled); +} + +uint32_t +Msc_channel::get_status() +{ + return _regs[Msc_status]; +} + +// Send an application-specific command. + +bool +Msc_channel::send_app_command(uint8_t index, uint32_t arg) +{ + if (!send_command(Command_app_cmd, 0, get_app_response_format(index), + false, false, false)) + return false; + + return send_command(index, arg); +} + +// Send a common MMC/SD command. + +bool +Msc_channel::send_command(uint8_t index, uint32_t arg) +{ + return send_command(index, arg, get_response_format(index), + command_with_data(index), command_will_write(index), + command_uses_busy(index)); +} + +// Initiate a command having the given index and using the given argument, +// employing the specified response format and involving a data transfer if +// indicated. + +bool +Msc_channel::send_command(uint8_t index, uint32_t arg, uint8_t response_format, + bool data, bool write, bool busy) +{ + stop_clock(); + + // Enable DMA for data transfers. + // NOTE: Needed for JZ4780 and later SoCs. + + _regs[Msc_dma_control] = (data ? Dma_select_common_dma | Dma_enable : Dma_disable); + + // Set the command index and argument. + + _regs[Msc_command_index] = index; + _regs[Msc_command_argument] = arg; + + // Configure the response format and data bus width. + + set_field(Msc_command_data_control, Cdc_response_format_field_mask, + Cdc_response_format_field_shift, response_format); + + // NOTE: May need to set the SD bus width. + + set_field(Msc_command_data_control, Cdc_bus_width_field_mask, + Cdc_bus_width_field_shift, Cdc_bus_width_field_1bit); + + set_field(Msc_command_data_control, Cdc_recv_fifo_level_field_mask, + Cdc_recv_fifo_level_field_shift, Cdc_fifo_level_16); + + set_field(Msc_command_data_control, Cdc_trans_fifo_level_field_mask, + Cdc_trans_fifo_level_field_shift, Cdc_fifo_level_16); + + // Set and clear control bits appropriate to the command. + // NOTE: Pre-JZ4780 SoCs enable DMA in this register. + + _regs[Msc_command_data_control] = _regs[Msc_command_data_control] | + // (data ? Cdc_dma_enable : Cdc_dma_disable) | + (busy ? Cdc_expect_busy : Cdc_do_not_expect_busy) | + (data ? Cdc_data_with_command : Cdc_no_data_with_command) | + (write ? Cdc_write_operation : Cdc_read_operation); + + _regs[Msc_command_data_control] = _regs[Msc_command_data_control] & + ~( + // (data ? Cdc_dma_disable : Cdc_dma_enable) | + (busy ? Cdc_do_not_expect_busy : Cdc_expect_busy) | + (data ? Cdc_no_data_with_command : Cdc_data_with_command) | + (write ? Cdc_read_operation : Cdc_write_operation) | + Cdc_stream_block | Cdc_init_sequence); + + // Unmask interrupts, start the clock, then initiate the command. + + uint32_t flags = Int_end_command_response | Int_response_timeout; + + unmask_irq(flags); + start_clock(); + + _regs[Msc_control] = _regs[Msc_control] | Control_start_operation; + + // Wait for command completion. + + if (!wait_for_irq(flags)) + return false; + + // Determine whether a timeout occurred. + + bool have_response = !(_regs[Msc_interrupt_flag] & Int_response_timeout); + + // Acknowledge the interrupts and return the status. + + ack_irq(flags); + return have_response; +} + +void +Msc_channel::enable() +{ + // NOTE: X1600 and other recent SoCs only. + + _regs[Msc_low_power_mode] = _regs[Msc_low_power_mode] & ~Low_power_mode_enable; + + stop_clock(); + reset(); + + // Slow the clock for initialisation. + // NOTE: Should use the CPM module to deduce the appropriate divider value. + + set_field(Msc_clock_rate, Clock_rate_field_mask, Clock_rate_field_shift, 7); + + send_command(Command_go_idle_state, 0); + + if (check_sd()) + { + init_sdio(); + init_sdmem(); + } + + init_mmc(); + identify_cards(); + query_cards(); + + // Restore the clock. + // NOTE: Should use the CPM module to deduce the appropriate divider value. + + set_field(Msc_clock_rate, Clock_rate_field_mask, Clock_rate_field_shift, 1); + + _current_rca = 0; +} + +// Check the voltage range of the SD card, potentially establishing that it is +// a high capacity card. Return false if the voltage range is incompatible. + +bool +Msc_channel::check_sd() +{ + uint16_t buffer[Response_size_R7]; + struct R7 *r = (struct R7 *) buffer; + + // Send an interface condition command. + // A card may not respond to this command. + + if (!send_command(Command_send_if_cond, If_cond_default_voltage_range)) + return true; + + read_response(buffer, Response_size_R7); + + // Reject any card not supporting the default voltage range. + + if (r->check_voltage.raw != If_cond_default_voltage_range) + return false; + + return true; +} + +// Check the voltage range of the SDIO card, inactivating it if incompatible. + +void +Msc_channel::init_sdio() +{ + uint16_t buffer[Response_size_R4]; + struct R4 *r = (struct R4 *) buffer; + uint32_t ocr = 0; + + // Reset any SDIO card or IO unit in a combined memory/IO card. + // A non-SDIO card may not respond to this command. + + if (!send_command(Command_io_rw_direct, Io_rw_direct_reset)) + return; + + // Attempt to assert the operating conditions. + + do + { + // Obtain OCR (operating conditions register) values for any IO card. + // Without a response, the card may have inactivated itself due to voltage + // range incompatibility reasons. + + if (!send_command(Command_io_send_op_cond, ocr)) + return; + + read_response(buffer, Response_size_R4); + + // Finish if no IO functions provided. + // NOTE: Should only need to check this the first time. + + if (r->number_io_functions == 0) + return; + + if (r->ocr != Ocr_default_voltage_range) + { + ocr = Ocr_default_voltage_range; + continue; + } + } + while (!r->ready); +} + +void +Msc_channel::init_sdmem() +{ + uint16_t buffer[Response_size_R3]; + struct R3 *r = (struct R3 *) buffer; + + // Incorporate the HCS bit into the OCR for SDMEM. + + uint32_t ocr = Ocr_high_capacity_storage; + + do + { + if (!send_app_command(App_command_sd_send_op_cond, ocr)) + return; + + read_response(buffer, Response_size_R3); + + if (r->ocr != Ocr_default_voltage_range) + { + ocr = Ocr_default_voltage_range | Ocr_high_capacity_storage; + continue; + } + } + while (!(r->ocr & Ocr_card_powered_up)); +} + +void +Msc_channel::init_mmc() +{ + // Obtain OCR (operating conditions register) values for each card using + // send_op_cond command variants without argument, or assert operating + // conditions with argument to avoid handling card responses. Where responses + // are solicited, the host must determine a suitable argument and reissue the + // command. + + uint16_t buffer[Response_size_R3]; + struct R3 *r = (struct R3 *) buffer; + uint32_t ocr = 0; + + do + { + if (!send_command(Command_send_op_cond, ocr)) + return; + + read_response(buffer, Response_size_R3); + + if (r->ocr != Ocr_default_voltage_range) + { + ocr = Ocr_default_voltage_range; + continue; + } + } + while (!(r->ocr & Ocr_card_powered_up)); +} + +void +Msc_channel::identify_cards() +{ + uint16_t buffer[Response_size_R2]; + struct R2 *r = (struct R2 *) buffer; + + _cards = 0; + + while (send_command(Command_all_send_cid, 0)) + { + read_response(buffer, Response_size_R2); + + memcpy(&_cid[_cards], r->payload.raw, sizeof(r->payload.raw)); + + printf("card: %d\n", _cards); + printf("date: %d %d\n", r->payload.cid.month, r->payload.cid.year); + printf("serial: %d\n", r->payload.cid.serial); + printf("revision: %d\n", r->payload.cid.revision); + printf("name: %c%c%c%c%c\n", r->payload.cid.name[4], r->payload.cid.name[3], + r->payload.cid.name[2], r->payload.cid.name[1], + r->payload.cid.name[0]); + printf("oem: %d\n", r->payload.cid.oem); + printf("manufacturer: %d\n", r->payload.cid.manufacturer); + + // Try and obtain a card-issued address. + + if (send_command(Command_send_relative_addr, 0)) + { + uint16_t addr_buffer[Response_size_R6]; + struct R6 *ar = (struct R6 *) addr_buffer; + + read_response(addr_buffer, Response_size_R6); + + memcpy(&_rca[_cards], &ar->rca, sizeof(ar->rca)); + } + + // Try and assign an address. + // Employ 1-based relative addressing. + + else if (send_command(Command_set_relative_addr, _cards + 1)) + _rca[_cards] = _cards + 1; + + // Otherwise, stop identification. + + else + return; + + _cards++; + } +} + +void +Msc_channel::query_cards() +{ + uint16_t buffer[Response_size_R2]; + struct R2 *r = (struct R2 *) buffer; + uint8_t card; + + for (card = 0; card < _cards; card++) + { + // Employ 1-based relative addressing. + + if (!send_command(Command_send_csd, _rca[card] << 16)) + return; + + read_response(buffer, Response_size_R2); + + memcpy(&_csd[card], r->payload.raw, sizeof(r->payload.raw)); + + printf("card: %d\n", card); + printf("csd: %d\n", r->payload.csd.csd); + printf("copy: %s\n", r->payload.csd.copy ? "copied" : "original"); + printf("card command classes: %03x\n", r->payload.csd.card_command_classes); + printf("device (size multiplier): %d %d\n", r->payload.csd.device_size + 1, + 1 << (r->payload.csd.device_size_multiplier + 2)); + printf("device size: %d\n", (1 << r->payload.csd.read_blocklen) * + (r->payload.csd.device_size + 1) * + (1 << (r->payload.csd.device_size_multiplier + 2))); + printf("transfer speed: %d MHz\n", r->payload.csd.tran_speed == 0x32 ? 25 : 50); + printf("format group: %d %d\n", r->payload.csd.format, r->payload.csd.format_group); + printf("write time factor: %d\n", 1 << r->payload.csd.write_time_factor); + printf("write protect (temp perm): %s %s\n", r->payload.csd.temp_write_prot ? "yes" : "no", + r->payload.csd.perm_write_prot ? "yes" : "no"); + printf("write protect group (enable size): %s %d\n", r->payload.csd.write_prot_group_enable ? "yes" : "no", + r->payload.csd.write_prot_group_size + 1); + printf("write block (partial length): %s %d\n", r->payload.csd.write_block_partial ? "yes" : "no", + 1 << r->payload.csd.write_blocklen); + printf("read block (partial length): %s %d\n", r->payload.csd.read_block_partial ? "yes" : "no", + 1 << r->payload.csd.read_blocklen); + printf("erase: sector single: %d %s\n", r->payload.csd.erase_sector_size + 1, + r->payload.csd.erase_single_block_enable ? "yes" : "no"); + printf("misalign: read write: %s %s\n", r->payload.csd.read_block_misalign ? "yes" : "no", + r->payload.csd.write_block_misalign ? "yes" : "no"); + printf("max read current (min max): %d %d\n", r->payload.csd.max_read_current_min, + r->payload.csd.max_read_current_max); + printf("max write current (min max): %d %d\n", r->payload.csd.max_write_current_min, + r->payload.csd.max_write_current_max); + printf("read access time (1 2): %d %d\n", r->payload.csd.data_read_access_time_1, + r->payload.csd.data_read_access_time_2); + printf("DSR: %s\n", r->payload.csd.dsr_implemented ? "yes" : "no"); + } +} + +uint32_t +Msc_channel::recv_data(l4re_dma_space_dma_addr_t paddr, uint32_t count) +{ + return transfer(_msc_start + Msc_recv_data_fifo, paddr, true, count); +} + +uint32_t +Msc_channel::send_data(l4re_dma_space_dma_addr_t paddr, uint32_t count) +{ + return transfer(paddr, _msc_start + Msc_trans_data_fifo, false, count); +} + +uint32_t +Msc_channel::read_block(uint8_t card, l4re_dma_space_dma_addr_t paddr) +{ + uint32_t block_size = 1 << _csd[card].read_blocklen; + uint16_t buffer[Response_size_R1]; + struct R1 *r = (struct R1 *) buffer; + + //printf("read_block: card %d -> %d\n", card, _rca[card]); + + // Select the requested card. + + if (_current_rca != _rca[card]) + { + if (!send_command(Command_select_deselect_card, _rca[card] << 16)) + return 0; + + read_response(buffer, Response_size_R1); + + if (r->status & R1_status_error_mask) + return 0; + + _current_rca = _rca[card]; + } + +#if 0 + // NOTE: SMEM cards should allow bus width setting. + // NOTE: SDIO cards have their bus width set in CCCR via CMD52. + + printf("set bus width -> %s\n", + send_app_command(App_command_set_bus_width, Bus_width_4bit) ? "set" : "not set"); +#endif + + if (!send_command(Command_set_blocklen, block_size)) + return 0; + + read_response(buffer, Response_size_R1); + + if (r->status & R1_status_error_mask) + return 0; + + // Apply block count and size properties to the issued command. + + _regs[Msc_block_count] = 1; + _regs[Msc_block_length] = block_size; + + // NOTE: Support an actual address. + // NOTE: Where CCS = 0, byte addressing is used. Otherwise, block addressing is used. + + if (!send_command(Command_read_single_block, 0)) + return 0; + + read_response(buffer, Response_size_R1); + + if (r->status & R1_status_error_mask) + return 0; + + return recv_data(paddr, block_size); +} + +// Wait indefinitely for an interrupt request, returning true if one was delivered. + +bool +Msc_channel::wait_for_irq(uint32_t flags) +{ + return !l4_error(l4_irq_receive(_irq, L4_IPC_NEVER)) && + (_regs[Msc_interrupt_flag] & flags); +} + +// Wait up to the given timeout (in microseconds) for an interrupt request, +// returning true if one was delivered. + +bool +Msc_channel::wait_for_irq(uint32_t flags, unsigned int timeout) +{ + return !l4_error(l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, + l4util_micros2l4to(timeout)))) && + (_regs[Msc_interrupt_flag] & flags); +} + + + +// Peripheral abstraction. + +Msc_chip::Msc_chip(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end) +: _msc_start(msc_start), _start(start), _end(end) +{ +} diff -r 2138b8459a7b -r f96263829f78 pkg/devices/lib/msc/src/jz4780.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/msc/src/jz4780.cc Sun Feb 11 00:39:56 2024 +0100 @@ -0,0 +1,133 @@ +/* + * MSC (MMC/SD controller) peripheral support. + * + * Copyright (C) 2024 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include + +#include "msc-jz4780.h" + + + +// Register locations for each channel. + +enum Regs : unsigned +{ + // Channel block size/offset. + + Msc_channel_offset = 0x10000, +}; + + + +// Channel abstraction. + +Msc_jz4780_channel::Msc_jz4780_channel(l4_addr_t msc_start, l4_addr_t addr, + l4_cap_idx_t irq, Dma_jz4780_channel *dma, + enum Dma_jz4780_request_type request_type_in, + enum Dma_jz4780_request_type request_type_out) +: Msc_channel(msc_start, addr, irq), + _dma(dma), + _request_type_in(request_type_in), + _request_type_out(request_type_out) +{ +} + +// Request the transfer of the indicated number of bytes between two physical +// addresses in the indicated direction, returning the number of bytes +// transferred. +// NOTE: To be consolidated into a generic method that uses generic request types. + +uint32_t +Msc_jz4780_channel::transfer(l4re_dma_space_dma_addr_t from_paddr, + l4re_dma_space_dma_addr_t to_paddr, + bool recv, uint32_t count) +{ + uint32_t unit_size = 32; + uint32_t unit_count = count / unit_size; + uint32_t to_transfer; + + to_transfer = _dma->transfer(from_paddr, to_paddr, + unit_count, + recv ? false : true, // increment source if sending + recv ? true : false, // increment destination if receiving + 4, 4, unit_size, + recv ? _request_type_in : _request_type_out); + + return to_transfer - _dma->wait() * unit_size; +} + + + +// Peripheral abstraction. + +Msc_jz4780_chip::Msc_jz4780_chip(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end) +: Msc_chip(msc_start, start, end) +{ +} + +Msc_channel *Msc_jz4780_chip::get_channel(uint8_t channel, l4_cap_idx_t irq, + Dma_jz4780_channel *dma) +{ + const enum Dma_jz4780_request_type in_types[] = + {Dma_request_msc0_in, Dma_request_msc1_in, Dma_request_msc2_in}; + + const enum Dma_jz4780_request_type out_types[] = + {Dma_request_msc0_out, Dma_request_msc1_out, Dma_request_msc2_out}; + + if (channel < num_channels()) + return new Msc_jz4780_channel(_msc_start + channel * Msc_channel_offset, + _start + channel * Msc_channel_offset, + irq, dma, + in_types[channel], out_types[channel]); + else + throw -L4_EINVAL; +} + + + +// C language interface functions. + +void *jz4780_msc_init(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end) +{ + return (void *) new Msc_jz4780_chip(msc_start, start, end); +} + +void *jz4780_msc_get_channel(void *msc, uint8_t channel, l4_cap_idx_t irq, + void *dma) +{ + return static_cast(msc)->get_channel(channel, irq, + static_cast(dma)); +} + +uint32_t jz4780_msc_get_status(void *msc_channel) +{ + return static_cast(msc_channel)->get_status(); +} + +void jz4780_msc_enable(void *msc_channel) +{ + static_cast(msc_channel)->enable(); +} + +uint32_t jz4780_msc_read_block(void *msc_channel, uint8_t card, + l4re_dma_space_dma_addr_t paddr) +{ + return static_cast(msc_channel)->read_block(card, paddr); +} diff -r 2138b8459a7b -r f96263829f78 pkg/devices/lib/msc/src/x1600.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/msc/src/x1600.cc Sun Feb 11 00:39:56 2024 +0100 @@ -0,0 +1,133 @@ +/* + * MSC (MMC/SD controller) peripheral support. + * + * Copyright (C) 2024 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include + +#include "msc-x1600.h" + + + +// Register locations for each channel. + +enum Regs : unsigned +{ + // Channel block size/offset. + + Msc_channel_offset = 0x10000, +}; + + + +// Channel abstraction. + +Msc_x1600_channel::Msc_x1600_channel(l4_addr_t msc_start, l4_addr_t addr, + l4_cap_idx_t irq, Dma_x1600_channel *dma, + enum Dma_x1600_request_type request_type_in, + enum Dma_x1600_request_type request_type_out) +: Msc_channel(msc_start, addr, irq), + _dma(dma), + _request_type_in(request_type_in), + _request_type_out(request_type_out) +{ +} + +// Request the transfer of the indicated number of bytes between two physical +// addresses in the indicated direction, returning the number of bytes +// transferred. +// NOTE: To be consolidated into a generic method that uses generic request types. + +uint32_t +Msc_x1600_channel::transfer(l4re_dma_space_dma_addr_t from_paddr, + l4re_dma_space_dma_addr_t to_paddr, + bool recv, uint32_t count) +{ + uint32_t unit_size = 32; + uint32_t unit_count = count / unit_size; + uint32_t to_transfer; + + to_transfer = _dma->transfer(from_paddr, to_paddr, + unit_count, + recv ? false : true, // increment source if sending + recv ? true : false, // increment destination if receiving + 4, 4, unit_size, + recv ? _request_type_in : _request_type_out); + + return to_transfer - _dma->wait() * unit_size; +} + + + +// Peripheral abstraction. + +Msc_x1600_chip::Msc_x1600_chip(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end) +: Msc_chip(msc_start, start, end) +{ +} + +Msc_channel *Msc_x1600_chip::get_channel(uint8_t channel, l4_cap_idx_t irq, + Dma_x1600_channel *dma) +{ + const enum Dma_x1600_request_type in_types[] = + {Dma_request_msc0_in, Dma_request_msc1_in}; + + const enum Dma_x1600_request_type out_types[] = + {Dma_request_msc0_out, Dma_request_msc1_out}; + + if (channel < num_channels()) + return new Msc_x1600_channel(_msc_start + channel * Msc_channel_offset, + _start + channel * Msc_channel_offset, + irq, dma, + in_types[channel], out_types[channel]); + else + throw -L4_EINVAL; +} + + + +// C language interface functions. + +void *x1600_msc_init(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end) +{ + return (void *) new Msc_x1600_chip(msc_start, start, end); +} + +void *x1600_msc_get_channel(void *msc, uint8_t channel, l4_cap_idx_t irq, + void *dma) +{ + return static_cast(msc)->get_channel(channel, irq, + static_cast(dma)); +} + +uint32_t x1600_msc_get_status(void *msc_channel) +{ + return static_cast(msc_channel)->get_status(); +} + +void x1600_msc_enable(void *msc_channel) +{ + static_cast(msc_channel)->enable(); +} + +uint32_t x1600_msc_read_block(void *msc_channel, uint8_t card, + l4re_dma_space_dma_addr_t paddr) +{ + return static_cast(msc_channel)->read_block(card, paddr); +} diff -r 2138b8459a7b -r f96263829f78 pkg/landfall-examples/hw_info/Makefile --- a/pkg/landfall-examples/hw_info/Makefile Sun Feb 11 00:36:08 2024 +0100 +++ b/pkg/landfall-examples/hw_info/Makefile Sun Feb 11 00:39:56 2024 +0100 @@ -7,7 +7,7 @@ REQUIRES_LIBS = \ libio l4re_c-util libdrivers-aic libdrivers-cpm \ libdrivers-dma libdrivers-gpio libdrivers-i2c \ - libdrivers-rtc libdrivers-spi libdrivers-tcu \ - libdevice-util + libdrivers-msc libdrivers-rtc libdrivers-spi \ + libdrivers-tcu libdevice-util include $(L4DIR)/mk/prog.mk diff -r 2138b8459a7b -r f96263829f78 pkg/landfall-examples/hw_info/common.h --- a/pkg/landfall-examples/hw_info/common.h Sun Feb 11 00:36:08 2024 +0100 +++ b/pkg/landfall-examples/hw_info/common.h Sun Feb 11 00:39:56 2024 +0100 @@ -166,6 +166,20 @@ +/* MSC adapter functions. */ + +void *msc_init(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end); + +void *msc_get_channel(void *msc, uint8_t channel, l4_cap_idx_t irq, void *dma); + +uint32_t msc_get_status(void *msc_channel); + +void msc_enable(void *msc_channel); + +uint32_t msc_read_block(void *msc_channel, uint8_t card, l4re_dma_space_dma_addr_t paddr); + + + /* RTC adapter functions. */ void *rtc_init(l4_addr_t start, void *cpm); @@ -277,7 +291,7 @@ enum memory_regions { - AIC, CPM, DMA, GPIO, I2C, RTC, SSI, TCU + AIC, CPM, DMA, GPIO, I2C, MSC, RTC, SSI, TCU }; diff -r 2138b8459a7b -r f96263829f78 pkg/landfall-examples/hw_info/defs.h --- a/pkg/landfall-examples/hw_info/defs.h Sun Feb 11 00:36:08 2024 +0100 +++ b/pkg/landfall-examples/hw_info/defs.h Sun Feb 11 00:39:56 2024 +0100 @@ -81,6 +81,16 @@ +/* MSC definitions. */ + +extern void *msc_channels[]; + +extern const unsigned int num_msc_channels; + +extern l4_cap_idx_t msc_irqs[]; + + + /* SPI definitions. */ extern void *spi_channels[]; diff -r 2138b8459a7b -r f96263829f78 pkg/landfall-examples/hw_info/hw_info.c --- a/pkg/landfall-examples/hw_info/hw_info.c Sun Feb 11 00:36:08 2024 +0100 +++ b/pkg/landfall-examples/hw_info/hw_info.c Sun Feb 11 00:39:56 2024 +0100 @@ -1112,6 +1112,92 @@ +/* MSC configuration. */ + +static l4_uint32_t msc_irq_start = 0, msc_irq_end = 0; + +/* MSC operations. */ + +static void list_msc_channels(void) +{ + unsigned int num; + void *channel; + + printf("Channel Status\n"); + printf("------- --------\n"); + + for (num = 0; num < num_msc_channels; num++) + { + printf("%d ", num); + + channel = msc_channels[num]; + + if (channel == NULL) + printf("inactive\n"); + else + printf("%08x\n", msc_get_status(channel)); + } +} + +static void new_msc_channel(void *msc) +{ + l4_cap_idx_t irqcap; + int num = get_channel_number(num_msc_channels); + void *channel; + + if (num < 0) + return; + + channel = get_channel(num_dma_channels, dma_channels, NULL); + + if (channel == NULL) + return; + + irqcap = l4re_util_cap_alloc(); + + if (init_irq(num, irqcap, msc_irq_start, msc_irq_end)) + return; + + msc_channels[num] = msc_get_channel(msc, num, irqcap, channel); + msc_irqs[num] = irqcap; +} + +static void enable_msc_channel(void) +{ + void *channel = get_channel(num_msc_channels, msc_channels, NULL); + + if (channel == NULL) + return; + + msc_enable(channel); +} + +static void read_block_from_msc(void) +{ + void *channel = get_channel(num_msc_channels, msc_channels, NULL); + struct dma_region *dma_region = NULL; + uint32_t card, transferred; + + if (channel == NULL) + return; + + dma_region = _get_dma_region(); + + if (dma_region == NULL) + return; + + if (!read_number("Card", &card)) + return; + + l4_cache_inv_data(dma_region->vaddr, dma_region->vaddr + dma_region->size); + + transferred = msc_read_block(channel, (uint8_t) card, dma_region->paddr); + + printf("Transferred: %d\n", transferred); +} + + + /* RTC operations. */ static void rtc_reset(void *rtc, void *cpm) @@ -1375,6 +1461,7 @@ printf(" Clock.. Counter......... Mask..............\n"); printf("Channel Status C Pre Cnt Half Full Half Full Int\n"); + printf("------- -------- -- ---- ---- ---- ---- -------- -------- ----\n"); for (num = 0; num < num_tcu_channels; num++) { @@ -1625,6 +1712,27 @@ list_memory_regions(); } +static void handle_msc(void *msc) +{ + char *token; + + if ((token = read_token(NULL)) != NULL) + { + if (!strcmp(token, "l") || !strcmp(token, "list")) + list_msc_channels(); + else if (!strcmp(token, "c") || !strcmp(token, "channel")) + new_msc_channel(msc); + else if (!strcmp(token, "e") || !strcmp(token, "enable")) + enable_msc_channel(); + else if (!strcmp(token, "r") || !strcmp(token, "read")) + read_block_from_msc(); + else + printf("msc channel | enable | read\n"); + } + else + list_msc_channels(); +} + static void handle_rtc(void *rtc, void *cpm) { char *token; @@ -1736,11 +1844,13 @@ l4_addr_t dma_base = 0, dma_base_end = 0; l4_addr_t gpio_base = 0, gpio_base_end = 0; l4_addr_t i2c_base = 0, i2c_base_end = 0; + l4_addr_t msc_base = 0, msc_base_end = 0; + l4_addr_t msc_phys_base = 0, msc_phys_base_end = 0; l4_addr_t rtc_base = 0, rtc_base_end = 0; l4_addr_t ssi_base = 0, ssi_base_end = 0; l4_addr_t ssi_phys_base = 0, ssi_phys_base_end = 0; l4_addr_t tcu_base = 0, tcu_base_end = 0; - void *aic, *cpm, *dma, *gpio[num_gpio_ports], *i2c, *rtc, *spi, *tcu; + void *aic, *cpm, *dma, *gpio[num_gpio_ports], *i2c, *msc, *rtc, *spi, *tcu; int result = 0; unsigned int port; @@ -1811,6 +1921,21 @@ aic = aic_init(aic_phys_base, aic_base, aic_base_end, cpm); + printf("Access MSC...\n"); + + if ((result = get_memory_complete(io_memory_regions[MSC], &msc_base, &msc_base_end, + &msc_phys_base, &msc_phys_base_end)) < 0) + return 1; + + printf("MSC at 0x%lx...0x%lx.\n", msc_base, msc_base_end); + + msc = msc_init(msc_phys_base, msc_base, msc_base_end); + + if (get_irq(io_memory_regions[MSC], &msc_irq_start, &msc_irq_end) < 0) + return 1; + + printf("IRQ range at %d...%d.\n", msc_irq_start, msc_irq_end); + printf("Access RTC...\n"); if ((result = get_memory(io_memory_regions[RTC], &rtc_base, &rtc_base_end)) < 0) @@ -1849,7 +1974,7 @@ /* Start the interactive session. */ - printf("aic, cpm, dma, gpio, i2c, rtc, spi, tcu\n"); + printf("aic, cpm, dma, gpio, i2c, msc, rtc, spi, tcu\n"); while (1) { @@ -1890,6 +2015,11 @@ else if (!strcmp(token, "i") || !strcmp(token, "i2c")) handle_i2c(i2c); + /* MSC commands. */ + + else if (!strcmp(token, "M") || !strcmp(token, "msc")) + handle_msc(msc); + /* Generic memory commands. */ else if (!strcmp(token, "m") || !strcmp(token, "mem") || !strcmp(token, "memory")) diff -r 2138b8459a7b -r f96263829f78 pkg/landfall-examples/hw_info/jz4780.c --- a/pkg/landfall-examples/hw_info/jz4780.c Sun Feb 11 00:36:08 2024 +0100 +++ b/pkg/landfall-examples/hw_info/jz4780.c Sun Feb 11 00:39:56 2024 +0100 @@ -1,7 +1,7 @@ /* * Access various peripherals on a board using the JZ4780. * - * Copyright (C) 2023 Paul Boddie + * Copyright (C) 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -28,6 +28,7 @@ #include #include #include +#include /* The X1600 RTC functionality is a subset of that in the JZ4780. */ @@ -336,6 +337,35 @@ +/* MSC adapter functions. */ + +void *msc_init(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end) +{ + return jz4780_msc_init(msc_start, start, end); +} + +void *msc_get_channel(void *msc, uint8_t channel, l4_cap_idx_t irq, void *dma) +{ + return jz4780_msc_get_channel(msc, channel, irq, dma); +} + +uint32_t msc_get_status(void *msc_channel) +{ + return jz4780_msc_get_status(msc_channel); +} + +void msc_enable(void *msc_channel) +{ + jz4780_msc_enable(msc_channel); +} + +uint32_t msc_read_block(void *msc_channel, uint8_t card, l4re_dma_space_dma_addr_t paddr) +{ + return jz4780_msc_read_block(msc_channel, card, paddr); +} + + + /* RTC adapter functions. */ void *rtc_init(l4_addr_t start, void *cpm) @@ -590,6 +620,7 @@ [DMA] = "jz4780-dma", [GPIO] = "jz4780-gpio", [I2C] = "jz4780-i2c", + [MSC] = "jz4780-msc", [RTC] = "jz4780-rtc", [SSI] = "jz4780-ssi", [TCU] = "jz4780-tcu", @@ -699,6 +730,16 @@ +/* MSC definitions. */ + +void *msc_channels[] = {NULL, NULL, NULL}; + +const unsigned int num_msc_channels = 3; + +l4_cap_idx_t msc_irqs[] = {L4_INVALID_CAP, L4_INVALID_CAP, L4_INVALID_CAP}; + + + /* SPI definitions. */ void *spi_channels[] = {NULL, NULL}; diff -r 2138b8459a7b -r f96263829f78 pkg/landfall-examples/hw_info/x1600.c --- a/pkg/landfall-examples/hw_info/x1600.c Sun Feb 11 00:36:08 2024 +0100 +++ b/pkg/landfall-examples/hw_info/x1600.c Sun Feb 11 00:39:56 2024 +0100 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -325,6 +326,35 @@ +/* MSC adapter functions. */ + +void *msc_init(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end) +{ + return x1600_msc_init(msc_start, start, end); +} + +void *msc_get_channel(void *msc, uint8_t channel, l4_cap_idx_t irq, void *dma) +{ + return x1600_msc_get_channel(msc, channel, irq, dma); +} + +uint32_t msc_get_status(void *msc_channel) +{ + return x1600_msc_get_status(msc_channel); +} + +void msc_enable(void *msc_channel) +{ + return x1600_msc_enable(msc_channel); +} + +uint32_t msc_read_block(void *msc_channel, uint8_t card, l4re_dma_space_dma_addr_t paddr) +{ + return x1600_msc_read_block(msc_channel, card, paddr); +} + + + /* RTC adapter functions. */ void *rtc_init(l4_addr_t start, void *cpm) @@ -576,6 +606,7 @@ [DMA] = "x1600-dma", [GPIO] = "x1600-gpio", [I2C] = "x1600-i2c", + [MSC] = "x1600-msc", [RTC] = "x1600-rtc", [SSI] = "x1600-ssi", [TCU] = "x1600-tcu", @@ -671,6 +702,16 @@ +/* MSC definitions. */ + +void *msc_channels[] = {NULL, NULL}; + +const unsigned int num_msc_channels = 2; + +l4_cap_idx_t msc_irqs[] = {L4_INVALID_CAP, L4_INVALID_CAP}; + + + /* SPI definitions. */ void *spi_channels[] = {NULL};