mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
feat(hal):graudate the sdmmc/sdio_slave hal driver into a new component
This commit is contained in:
@@ -1,568 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* NOTICE
|
||||
* The hal is not public api, don't use in application code.
|
||||
* See readme.md in hal/include/hal/readme.md
|
||||
******************************************************************************/
|
||||
|
||||
// The LL layer for ESP32 SDIO slave register operations
|
||||
// It's strange but `tx_*` regs for host->slave transfers while `rx_*` regs for slave->host transfers
|
||||
// To reduce ambiguity, we call (host->slave, tx) transfers receiving and (slave->host, rx) transfers receiving
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <stdbool.h>
|
||||
#include "hal/sdio_slave_types.h"
|
||||
#include "hal/misc.h"
|
||||
#include "soc/sdio_slc_struct.h"
|
||||
#include "soc/sdio_slc_reg.h"
|
||||
#include "soc/sdio_slc_host_struct.h"
|
||||
#include "soc/sdio_slc_host_reg.h"
|
||||
#include "soc/sdio_hinf_struct.h"
|
||||
#include "soc/dport_reg.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// Get address of the only SLC registers for ESP32
|
||||
#define sdio_slave_ll_get_slc(ID) (&SLC)
|
||||
/// Get address of the only HOST registers for ESP32
|
||||
#define sdio_slave_ll_get_host(ID) (&HOST)
|
||||
/// Get address of the only HINF registers for ESP32
|
||||
#define sdio_slave_ll_get_hinf(ID) (&HINF)
|
||||
/// Get the mask of the interrupt status.
|
||||
#define sdio_slave_ll_intr_status_mask (0xff | SLC_SLC0_RX_DONE_INT_ST | SLC_SLC0_RX_EOF_INT_ST | SLC_SLC0_TX_DONE_INT_ST)
|
||||
|
||||
/*
|
||||
* SLC2 DMA Desc struct, aka sdio_slave_ll_desc_t
|
||||
*
|
||||
* --------------------------------------------------------------
|
||||
* | own | EoF | sub_sof | 5'b0 | length [11:0] | size [11:0] |
|
||||
* --------------------------------------------------------------
|
||||
* | buf_ptr [31:0] |
|
||||
* --------------------------------------------------------------
|
||||
* | next_desc_ptr [31:0] |
|
||||
* --------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* this bitfield is start from the LSB!!! */
|
||||
typedef struct sdio_slave_ll_desc_s {
|
||||
volatile uint32_t size : 12,
|
||||
length: 12,
|
||||
offset: 5, /* starting from bit24, h/w reserved 5bit, s/w use it as offset in buffer */
|
||||
sosf : 1, /* start of sub-frame */
|
||||
eof : 1, /* end of frame */
|
||||
owner : 1; /* hw or sw */
|
||||
volatile const uint8_t *buf; /* point to buffer data */
|
||||
union {
|
||||
volatile uint32_t empty;
|
||||
STAILQ_ENTRY(sdio_slave_ll_desc_s) qe; /* pointing to the next desc */
|
||||
};
|
||||
} sdio_slave_ll_desc_t;
|
||||
|
||||
/// Mask of general purpose interrupts sending from the host.
|
||||
typedef enum {
|
||||
SDIO_SLAVE_LL_SLVINT_0 = BIT(0), ///< General purpose interrupt bit 0.
|
||||
SDIO_SLAVE_LL_SLVINT_1 = BIT(1),
|
||||
SDIO_SLAVE_LL_SLVINT_2 = BIT(2),
|
||||
SDIO_SLAVE_LL_SLVINT_3 = BIT(3),
|
||||
SDIO_SLAVE_LL_SLVINT_4 = BIT(4),
|
||||
SDIO_SLAVE_LL_SLVINT_5 = BIT(5),
|
||||
SDIO_SLAVE_LL_SLVINT_6 = BIT(6),
|
||||
SDIO_SLAVE_LL_SLVINT_7 = BIT(7),
|
||||
} sdio_slave_ll_slvint_t;
|
||||
|
||||
/**
|
||||
* @brief Enable the bus clock for the SDIO slave module
|
||||
*
|
||||
* @param enable true to enable, false to disable
|
||||
*/
|
||||
static inline void _sdio_slave_ll_enable_bus_clock(bool enable)
|
||||
{
|
||||
uint32_t reg_val = DPORT_READ_PERI_REG(DPORT_WIFI_CLK_EN_REG);
|
||||
reg_val &= ~DPORT_WIFI_CLK_SDIOSLAVE_EN;
|
||||
reg_val |= enable << 4;
|
||||
DPORT_WRITE_PERI_REG(DPORT_WIFI_CLK_EN_REG, reg_val);
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define sdio_slave_ll_enable_bus_clock(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_sdio_slave_ll_enable_bus_clock(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Reset the SDIO slave module
|
||||
*/
|
||||
static inline void _sdio_slave_ll_reset_register(void)
|
||||
{
|
||||
DPORT_WRITE_PERI_REG(DPORT_CORE_RST_EN_REG, DPORT_SDIO_RST);
|
||||
DPORT_WRITE_PERI_REG(DPORT_CORE_RST_EN_REG, 0);
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define sdio_slave_ll_reset_register(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_sdio_slave_ll_reset_register(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* Initialize the hardware.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_init(slc_dev_t *slc)
|
||||
{
|
||||
slc->slc0_int_ena.val = 0;
|
||||
|
||||
slc->conf0.slc0_rx_auto_wrback = 1;
|
||||
slc->conf0.slc0_token_auto_clr = 0;
|
||||
slc->conf0.slc0_rx_loop_test = 0;
|
||||
slc->conf0.slc0_tx_loop_test = 0;
|
||||
|
||||
slc->conf1.slc0_rx_stitch_en = 0;
|
||||
slc->conf1.slc0_tx_stitch_en = 0;
|
||||
slc->conf1.slc0_len_auto_clr = 0;
|
||||
|
||||
slc->rx_dscr_conf.slc0_token_no_replace = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timing for the communication
|
||||
*
|
||||
* @param host Address of the host registers
|
||||
* @param timing Timing configuration to set
|
||||
*/
|
||||
static inline void sdio_slave_ll_set_timing(host_dev_t *host, sdio_slave_timing_t timing)
|
||||
{
|
||||
switch (timing) {
|
||||
case SDIO_SLAVE_TIMING_PSEND_PSAMPLE:
|
||||
host->conf.frc_sdio20 = 0x1f;
|
||||
host->conf.frc_sdio11 = 0;
|
||||
host->conf.frc_pos_samp = 0x1f;
|
||||
host->conf.frc_neg_samp = 0;
|
||||
break;
|
||||
case SDIO_SLAVE_TIMING_PSEND_NSAMPLE:
|
||||
host->conf.frc_sdio20 = 0x1f;
|
||||
host->conf.frc_sdio11 = 0;
|
||||
host->conf.frc_pos_samp = 0;
|
||||
host->conf.frc_neg_samp = 0x1f;
|
||||
break;
|
||||
case SDIO_SLAVE_TIMING_NSEND_PSAMPLE:
|
||||
host->conf.frc_sdio20 = 0;
|
||||
host->conf.frc_sdio11 = 0x1f;
|
||||
host->conf.frc_pos_samp = 0x1f;
|
||||
host->conf.frc_neg_samp = 0;
|
||||
break;
|
||||
case SDIO_SLAVE_TIMING_NSEND_NSAMPLE:
|
||||
host->conf.frc_sdio20 = 0;
|
||||
host->conf.frc_sdio11 = 0x1f;
|
||||
host->conf.frc_pos_samp = 0;
|
||||
host->conf.frc_neg_samp = 0x1f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CCCR, SDIO and Physical Layer version
|
||||
*/
|
||||
static inline void sdio_slave_ll_init_version(hinf_dev_t *hinf)
|
||||
{
|
||||
hinf->cfg_data1.sdio_ver = 0x232;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the HS supported bit to be read by the host.
|
||||
*
|
||||
* @param hinf Address of the hinf registers
|
||||
* @param hs true if supported, otherwise false.
|
||||
*/
|
||||
static inline void sdio_slave_ll_enable_hs(hinf_dev_t *hinf, bool hs)
|
||||
{
|
||||
if (hs) {
|
||||
hinf->cfg_data1.highspeed_enable = 1;
|
||||
} else {
|
||||
hinf->cfg_data1.highspeed_enable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IO Ready bit to be read by the host.
|
||||
*
|
||||
* @param hinf Address of the hinf registers
|
||||
* @param ready true if ready, otherwise false.
|
||||
*/
|
||||
static inline void sdio_slave_ll_set_ioready(hinf_dev_t *hinf, bool ready)
|
||||
{
|
||||
hinf->cfg_data1.sdio_ioready1 = (ready ? 1 : 0); //set IO ready to 1 to stop host from using
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Send
|
||||
*--------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Reset the sending DMA.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_send_reset(slc_dev_t *slc)
|
||||
{
|
||||
//reset to flush previous packets
|
||||
slc->conf0.slc0_rx_rst = 1;
|
||||
slc->conf0.slc0_rx_rst = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the sending DMA with the given descriptor.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @param desc Descriptor to send
|
||||
*/
|
||||
static inline void sdio_slave_ll_send_start(slc_dev_t *slc, const sdio_slave_ll_desc_t *desc)
|
||||
{
|
||||
slc->slc0_rx_link.addr = (uint32_t)desc;
|
||||
slc->slc0_rx_link.start = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the PKT_LEN register to be written by the host to a certain value.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @param len Length to write
|
||||
*/
|
||||
static inline void sdio_slave_ll_send_write_len(slc_dev_t *slc, uint32_t len)
|
||||
{
|
||||
slc->slc0_len_conf.val = FIELD_TO_VALUE2(SLC_SLC0_LEN_WDATA, len) | FIELD_TO_VALUE2(SLC_SLC0_LEN_WR, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value of PKT_LEN register. The register may keep the same until read
|
||||
* by the host.
|
||||
*
|
||||
* @param host Address of the host registers
|
||||
* @return The value of PKT_LEN register.
|
||||
*/
|
||||
static inline uint32_t sdio_slave_ll_send_read_len(host_dev_t *host)
|
||||
{
|
||||
return host->pkt_len.reg_slc0_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the rx_done interrupt. (sending)
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @param ena true if enable, otherwise false.
|
||||
*/
|
||||
static inline void sdio_slave_ll_send_part_done_intr_ena(slc_dev_t *slc, bool ena)
|
||||
{
|
||||
slc->slc0_int_ena.rx_done = (ena ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the rx_done interrupt. (sending)
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_send_part_done_clear(slc_dev_t *slc)
|
||||
{
|
||||
slc->slc0_int_clr.rx_done = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the hardware is ready for the SW to use rx_done to invoke
|
||||
* the ISR.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @return true if ready, otherwise false.
|
||||
*/
|
||||
static inline bool sdio_slave_ll_send_invoker_ready(slc_dev_t *slc)
|
||||
{
|
||||
return slc->slc0_int_raw.rx_done;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the sending DMA.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_send_stop(slc_dev_t *slc)
|
||||
{
|
||||
slc->slc0_rx_link.stop = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the sending interrupt (rx_eof).
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @param ena true to enable, false to disable
|
||||
*/
|
||||
static inline void sdio_slave_ll_send_intr_ena(slc_dev_t *slc, bool ena)
|
||||
{
|
||||
slc->slc0_int_ena.rx_eof = (ena ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the sending interrupt (rx_eof).
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_send_intr_clr(slc_dev_t *slc)
|
||||
{
|
||||
slc->slc0_int_clr.rx_eof = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the sending is done.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @return true if done, otherwise false
|
||||
*/
|
||||
static inline bool sdio_slave_ll_send_done(slc_dev_t *slc)
|
||||
{
|
||||
return slc->slc0_int_st.rx_eof != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the host interrupt indicating the slave having packet to be read.
|
||||
*
|
||||
* @param host Address of the host registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_send_hostint_clr(host_dev_t *host)
|
||||
{
|
||||
host->slc0_int_clr.rx_new_packet = 1;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Receive
|
||||
*--------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Enable the receiving interrupt.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @param ena
|
||||
*/
|
||||
static inline void sdio_slave_ll_recv_intr_ena(slc_dev_t *slc, bool ena)
|
||||
{
|
||||
slc->slc0_int_ena.tx_done = (ena ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start receiving DMA with the given descriptor.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @param desc Descriptor of the receiving buffer.
|
||||
*/
|
||||
static inline void sdio_slave_ll_recv_start(slc_dev_t *slc, sdio_slave_ll_desc_t *desc)
|
||||
{
|
||||
slc->slc0_tx_link.addr = (uint32_t)desc;
|
||||
slc->slc0_tx_link.start = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the receiving buffer counter by 1.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_recv_size_inc(slc_dev_t *slc)
|
||||
{
|
||||
// fields wdata and inc_more should be written by the same instruction.
|
||||
slc->slc0_token1.val = FIELD_TO_VALUE2(SLC_SLC0_TOKEN1_WDATA, 1) | FIELD_TO_VALUE2(SLC_SLC0_TOKEN1_INC_MORE, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the receiving buffer.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_recv_size_reset(slc_dev_t *slc)
|
||||
{
|
||||
slc->slc0_token1.val = FIELD_TO_VALUE2(SLC_SLC0_TOKEN1_WDATA, 0) | FIELD_TO_VALUE2(SLC_SLC0_TOKEN1_WR, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether there is a receiving finished event.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @return
|
||||
*/
|
||||
static inline bool sdio_slave_ll_recv_done(slc_dev_t *slc)
|
||||
{
|
||||
return slc->slc0_int_raw.tx_done != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the receiving finished interrupt.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_recv_done_clear(slc_dev_t *slc)
|
||||
{
|
||||
slc->slc0_int_clr.tx_done = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the DMA. Call after you modified the next pointer of the tail descriptor to the appended
|
||||
* descriptor.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_recv_restart(slc_dev_t *slc)
|
||||
{
|
||||
slc->slc0_tx_link.restart = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the receiving DMA.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_recv_reset(slc_dev_t *slc)
|
||||
{
|
||||
slc->conf0.slc0_tx_rst = 1;
|
||||
slc->conf0.slc0_tx_rst = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the receiving DMA.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
*/
|
||||
static inline void sdio_slave_ll_recv_stop(slc_dev_t *slc)
|
||||
{
|
||||
slc->slc0_tx_link.stop = 1;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Host
|
||||
*--------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Get the address of the shared general purpose register. Internal.
|
||||
*
|
||||
* @param host Address of the host registers
|
||||
* @param pos Position of the register, 0-63 except 24-27.
|
||||
* @return address of the register.
|
||||
*/
|
||||
static inline intptr_t sdio_slave_ll_host_get_w_reg(host_dev_t *host, int pos)
|
||||
{
|
||||
return (intptr_t) & (host->conf_w0) + pos + (pos > 23 ? 4 : 0) + (pos > 31 ? 12 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the shared general purpose register.
|
||||
*
|
||||
* @param host Address of the host registers
|
||||
* @param pos Position of the register, 0-63, except 24-27.
|
||||
* @return value of the register.
|
||||
*/
|
||||
static inline uint8_t sdio_slave_ll_host_get_reg(host_dev_t *host, int pos)
|
||||
{
|
||||
return *(uint8_t *)sdio_slave_ll_host_get_w_reg(host, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the shared general purpose register.
|
||||
*
|
||||
* @param host Address of the host registers
|
||||
* @param pos Position of the register, 0-63, except 24-27.
|
||||
* @param reg Value to set.
|
||||
*/
|
||||
static inline void sdio_slave_ll_host_set_reg(host_dev_t *host, int pos, uint8_t reg)
|
||||
{
|
||||
uint32_t *addr = (uint32_t *)(sdio_slave_ll_host_get_w_reg(host, pos) & (~3));
|
||||
uint32_t shift = (pos % 4) * 8;
|
||||
*addr &= ~(0xff << shift);
|
||||
*addr |= ((uint32_t)reg << shift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the interrupt enable bits for the host.
|
||||
*
|
||||
* @param host Address of the host registers
|
||||
* @return Enabled interrupts
|
||||
*/
|
||||
static inline sdio_slave_hostint_t sdio_slave_ll_host_get_intena(host_dev_t *host)
|
||||
{
|
||||
return (sdio_slave_hostint_t)host->slc0_func1_int_ena.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interrupt enable bits for the host.
|
||||
*
|
||||
* @param host Address of the host registers
|
||||
* @param mask Mask of interrupts to enable
|
||||
*/
|
||||
static inline void sdio_slave_ll_host_set_intena(host_dev_t *host, const sdio_slave_hostint_t *mask)
|
||||
{
|
||||
host->slc0_func1_int_ena.val = (*mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the interrupt bits for the host.
|
||||
* @param host Address of the host registers
|
||||
* @param mask Mask of interrupts to clear.
|
||||
*/
|
||||
static inline void sdio_slave_ll_host_intr_clear(host_dev_t *host, const sdio_slave_hostint_t *mask)
|
||||
{
|
||||
host->slc0_int_clr.val = (*mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send general purpose interrupts to the host.
|
||||
* @param slc Address of the SLC registers
|
||||
* @param mask Mask of interrupts to seend to host
|
||||
*/
|
||||
static inline void sdio_slave_ll_host_send_int(slc_dev_t *slc, const sdio_slave_hostint_t *mask)
|
||||
{
|
||||
//use registers in SLC to trigger, rather than write HOST registers directly
|
||||
//other interrupts than tohost interrupts are not supported yet
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(slc->intvec_tohost, slc0_intvec, *mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable some of the slave interrupts (send from host)
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @param mask Mask of interrupts to enable, all those set to 0 will be disabled.
|
||||
*/
|
||||
static inline void sdio_slave_ll_slvint_set_ena(slc_dev_t *slc, const sdio_slave_ll_slvint_t *mask)
|
||||
{
|
||||
//other interrupts are not enabled
|
||||
slc->slc0_int_ena.val = (slc->slc0_int_ena.val & (~0xff)) | ((*mask) & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the slave interrupts (send from host) and clear them.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @param out_slv_int Output of the slave interrupts fetched and cleared.
|
||||
*/
|
||||
static inline void sdio_slave_ll_slvint_fetch_clear(slc_dev_t *slc, sdio_slave_ll_slvint_t *out_slv_int)
|
||||
{
|
||||
sdio_slave_ll_slvint_t slv_int = (sdio_slave_ll_slvint_t)(slc->slc0_int_st.val & 0xff);
|
||||
*out_slv_int = slv_int;
|
||||
slc->slc0_int_clr.val = slv_int;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address of the interrupt status register.
|
||||
*
|
||||
* @param slc Address of the SLC registers
|
||||
* @return Address of the interrupt status register
|
||||
*/
|
||||
static inline volatile void* sdio_slave_ll_get_intr_status_reg(slc_dev_t *slc)
|
||||
{
|
||||
return &slc->slc0_int_st.val;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,739 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* NOTICE
|
||||
* The ll is not public api, don't use in application code.
|
||||
* See readme.md in hal/include/hal/readme.md
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "esp_bit_defs.h"
|
||||
#include "hal/assert.h"
|
||||
#include "hal/misc.h"
|
||||
#include "hal/sd_types.h"
|
||||
#include "soc/clk_tree_defs.h"
|
||||
#include "soc/sdmmc_struct.h"
|
||||
#include "soc/sdmmc_reg.h"
|
||||
#include "soc/dport_reg.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SDMMC_LL_GET_HW(id) (((id) == 0) ? (&SDMMC) : NULL)
|
||||
|
||||
#define SDMMC_LL_EVENT_IO_SLOT1 (1<<17)
|
||||
#define SDMMC_LL_EVENT_IO_SLOT0 (1<<16)
|
||||
#define SDMMC_LL_EVENT_EBE (1<<15)
|
||||
#define SDMMC_LL_EVENT_ACD (1<<14)
|
||||
#define SDMMC_LL_EVENT_SBE (1<<13)
|
||||
#define SDMMC_LL_EVENT_BCI (1<<13)
|
||||
#define SDMMC_LL_EVENT_HLE (1<<12)
|
||||
#define SDMMC_LL_EVENT_FRUN (1<<11)
|
||||
#define SDMMC_LL_EVENT_HTO (1<<10)
|
||||
#define SDMMC_LL_EVENT_DTO (1<<9)
|
||||
#define SDMMC_LL_EVENT_RTO (1<<8)
|
||||
#define SDMMC_LL_EVENT_DCRC (1<<7)
|
||||
#define SDMMC_LL_EVENT_RCRC (1<<6)
|
||||
#define SDMMC_LL_EVENT_RXDR (1<<5)
|
||||
#define SDMMC_LL_EVENT_TXDR (1<<4)
|
||||
#define SDMMC_LL_EVENT_DATA_OVER (1<<3)
|
||||
#define SDMMC_LL_EVENT_CMD_DONE (1<<2)
|
||||
#define SDMMC_LL_EVENT_RESP_ERR (1<<1)
|
||||
#define SDMMC_LL_EVENT_CD (1<<0)
|
||||
|
||||
/* Default disabled interrupts (on init):
|
||||
* SDMMC_LL_EVENT_RXDR,
|
||||
* SDMMC_LL_EVENT_TXDR,
|
||||
* SDMMC_LL_EVENT_BCI,
|
||||
* SDMMC_LL_EVENT_ACD,
|
||||
* SDMMC_LL_EVENT_IO_SLOT1,
|
||||
* SDMMC_LL_EVENT_IO_SLOT0
|
||||
*/
|
||||
// Default enabled interrupts (sdio is enabled only when use):
|
||||
#define SDMMC_LL_EVENT_DEFAULT \
|
||||
(SDMMC_LL_EVENT_CD | SDMMC_LL_EVENT_RESP_ERR | SDMMC_LL_EVENT_CMD_DONE | SDMMC_LL_EVENT_DATA_OVER | \
|
||||
SDMMC_LL_EVENT_RCRC | SDMMC_LL_EVENT_DCRC | SDMMC_LL_EVENT_RTO | SDMMC_LL_EVENT_DTO | SDMMC_LL_EVENT_HTO | \
|
||||
SDMMC_LL_EVENT_HLE | \
|
||||
SDMMC_LL_EVENT_SBE | \
|
||||
SDMMC_LL_EVENT_EBE)
|
||||
|
||||
#define SDMMC_LL_SD_EVENT_MASK \
|
||||
(SDMMC_LL_EVENT_CD | SDMMC_LL_EVENT_RESP_ERR | SDMMC_LL_EVENT_CMD_DONE | SDMMC_LL_EVENT_DATA_OVER | \
|
||||
SDMMC_LL_EVENT_TXDR | SDMMC_LL_EVENT_RXDR |\
|
||||
SDMMC_LL_EVENT_RCRC | SDMMC_LL_EVENT_DCRC | SDMMC_LL_EVENT_RTO | SDMMC_LL_EVENT_DTO | SDMMC_LL_EVENT_HTO | \
|
||||
SDMMC_LL_EVENT_FRUN | SDMMC_LL_EVENT_HLE |\
|
||||
SDMMC_LL_EVENT_SBE | SDMMC_LL_EVENT_ACD |\
|
||||
SDMMC_LL_EVENT_EBE)
|
||||
|
||||
// DMA interrupts (idsts register)
|
||||
#define SDMMC_LL_EVENT_DMA_TI SDMMC_IDMAC_INTMASK_TI
|
||||
#define SDMMC_LL_EVENT_DMA_RI SDMMC_IDMAC_INTMASK_RI
|
||||
#define SDMMC_LL_EVENT_DMA_NI SDMMC_IDMAC_INTMASK_NI
|
||||
#define SDMMC_LL_EVENT_DMA_MASK 0x1f //NI and AI will be indicated by TI/RI and FBE/DU respectively
|
||||
|
||||
/**
|
||||
* SDMMC capabilities
|
||||
*/
|
||||
#define SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(SLOT_ID) 0
|
||||
#define SDMMC_LL_IOMUX_FUNC 3
|
||||
#define SDMMC_LL_HOST_CTLR_NUMS 1U
|
||||
|
||||
typedef enum {
|
||||
SDMMC_LL_DELAY_PHASE_0,
|
||||
SDMMC_LL_DELAY_PHASE_1,
|
||||
SDMMC_LL_DELAY_PHASE_2,
|
||||
SDMMC_LL_DELAY_PHASE_3,
|
||||
} sdmmc_ll_delay_phase_t;
|
||||
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
Clock & Reset
|
||||
---------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Enable the bus clock for SDMMC module
|
||||
*
|
||||
* @param group_id Group ID
|
||||
* @param en enable / disable
|
||||
*/
|
||||
static inline void sdmmc_ll_enable_bus_clock(int group_id, bool en)
|
||||
{
|
||||
(void)group_id;
|
||||
if (en) {
|
||||
DPORT_SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_SDIO_HOST_EN);
|
||||
} else {
|
||||
DPORT_CLEAR_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_SDIO_HOST_EN);
|
||||
}
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define sdmmc_ll_enable_bus_clock(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
sdmmc_ll_enable_bus_clock(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Reset the SDMMC module
|
||||
*
|
||||
* @param group_id Group ID
|
||||
*/
|
||||
static inline void sdmmc_ll_reset_register(int group_id)
|
||||
{
|
||||
(void)group_id;
|
||||
DPORT_SET_PERI_REG_MASK(DPORT_CORE_RST_EN_REG, DPORT_SDIO_HOST_RST);
|
||||
DPORT_CLEAR_PERI_REG_MASK(DPORT_CORE_RST_EN_REG, DPORT_SDIO_HOST_RST);
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define sdmmc_ll_reset_register(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
sdmmc_ll_reset_register(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Select SDMMC clock source
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param clk_src clock source, see valid sources in type `soc_periph_sdmmc_clk_src_t`
|
||||
*/
|
||||
static inline void sdmmc_ll_select_clk_source(sdmmc_dev_t *hw, soc_periph_sdmmc_clk_src_t clk_src)
|
||||
{
|
||||
//leave for compatibility
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set SDMMC clock div
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param div divider value
|
||||
*/
|
||||
static inline void sdmmc_ll_set_clock_div(sdmmc_dev_t *hw, uint32_t div)
|
||||
{
|
||||
/**
|
||||
* Set frequency to 160MHz / div
|
||||
*
|
||||
* n: counter resets at div_factor_n.
|
||||
* l: negedge when counter equals div_factor_l.
|
||||
* h: posedge when counter equals div_factor_h.
|
||||
*
|
||||
* We set the duty cycle to 1/2
|
||||
*/
|
||||
HAL_ASSERT(div > 1 && div <= 16);
|
||||
int h = div - 1;
|
||||
int l = div / 2 - 1;
|
||||
|
||||
hw->clock.div_factor_h = h;
|
||||
hw->clock.div_factor_l = l;
|
||||
hw->clock.div_factor_n = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinit clock
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*/
|
||||
static inline void sdmmc_ll_deinit_clk(sdmmc_dev_t *hw)
|
||||
{
|
||||
hw->clock.val = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get SDMMC clock div
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*
|
||||
* @return Divider value
|
||||
*/
|
||||
static inline uint32_t sdmmc_ll_get_clock_div(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->clock.div_factor_h + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialise the din, dout, self delay phase
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*/
|
||||
static inline void sdmmc_ll_init_phase_delay(sdmmc_dev_t *hw)
|
||||
{
|
||||
// 180 degree phase on input and output clocks
|
||||
hw->clock.phase_dout = 4;
|
||||
hw->clock.phase_din = 4;
|
||||
hw->clock.phase_core = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable card clock
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param slot slot
|
||||
* @param en enable / disable
|
||||
*/
|
||||
static inline void sdmmc_ll_enable_card_clock(sdmmc_dev_t *hw, uint32_t slot, bool en)
|
||||
{
|
||||
uint32_t reg_val = HAL_FORCE_READ_U32_REG_FIELD(hw->clkena, cclk_enable);
|
||||
if (en) {
|
||||
reg_val |= BIT(slot);
|
||||
} else {
|
||||
reg_val &= ~BIT(slot);
|
||||
}
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkena, cclk_enable, reg_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set card clock div
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param slot slot
|
||||
* @param card_div divider value
|
||||
*/
|
||||
static inline void sdmmc_ll_set_card_clock_div(sdmmc_dev_t *hw, uint32_t slot, uint32_t card_div)
|
||||
{
|
||||
if (slot == 0) {
|
||||
hw->clksrc.card0 = 0;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkdiv, div0, card_div);
|
||||
} else if (slot == 1) {
|
||||
hw->clksrc.card1 = 1;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkdiv, div1, card_div);
|
||||
} else {
|
||||
HAL_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get card clock div
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param slot slot
|
||||
*
|
||||
* @return Divider value
|
||||
*/
|
||||
static inline uint32_t sdmmc_ll_get_card_clock_div(sdmmc_dev_t *hw, uint32_t slot)
|
||||
{
|
||||
uint32_t card_div = 0;
|
||||
|
||||
if (slot == 0) {
|
||||
card_div = HAL_FORCE_READ_U32_REG_FIELD(hw->clkdiv, div0);
|
||||
} else if (slot == 1) {
|
||||
card_div = HAL_FORCE_READ_U32_REG_FIELD(hw->clkdiv, div1);
|
||||
} else {
|
||||
HAL_ASSERT(false);
|
||||
}
|
||||
|
||||
return card_div;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable clock when the card is in IDLE state
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param slot slot
|
||||
* @param en enable / disable
|
||||
*/
|
||||
static inline void sdmmc_ll_enable_card_clock_low_power(sdmmc_dev_t *hw, uint32_t slot, bool en)
|
||||
{
|
||||
uint32_t reg_val = HAL_FORCE_READ_U32_REG_FIELD(hw->clkena, cclk_low_power);
|
||||
if (en) {
|
||||
reg_val |= BIT(slot);
|
||||
} else {
|
||||
reg_val &= ~BIT(slot);
|
||||
}
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkena, cclk_low_power, reg_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset controller
|
||||
*
|
||||
* @note Self clear after two AHB clock cycles, needs wait done
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*/
|
||||
static inline void sdmmc_ll_reset_controller(sdmmc_dev_t *hw)
|
||||
{
|
||||
hw->ctrl.controller_reset = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get if controller reset is done
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*
|
||||
* @return true: done; false: not done
|
||||
*/
|
||||
static inline bool sdmmc_ll_is_controller_reset_done(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->ctrl.controller_reset == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset DMA
|
||||
*
|
||||
* @note Self clear after two AHB clock cycles, needs wait done
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*/
|
||||
static inline void sdmmc_ll_reset_dma(sdmmc_dev_t *hw)
|
||||
{
|
||||
hw->ctrl.dma_reset = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get if dma reset is done
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*
|
||||
* @return true: done; false: not done
|
||||
*/
|
||||
static inline bool sdmmc_ll_is_dma_reset_done(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->ctrl.dma_reset == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset fifo
|
||||
*
|
||||
* @note Self clear after reset done, needs wait done
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*/
|
||||
static inline void sdmmc_ll_reset_fifo(sdmmc_dev_t *hw)
|
||||
{
|
||||
hw->ctrl.fifo_reset = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get if fifo reset is done
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*
|
||||
* @return true: done; false: not done
|
||||
*/
|
||||
static inline bool sdmmc_ll_is_fifo_reset_done(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->ctrl.fifo_reset == 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
MISC
|
||||
---------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Set card data read timeout cycles
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param timeout_cycles timeout cycles
|
||||
*/
|
||||
static inline void sdmmc_ll_set_data_timeout(sdmmc_dev_t *hw, uint32_t timeout_cycles)
|
||||
{
|
||||
if (timeout_cycles > 0xffffff) {
|
||||
timeout_cycles = 0xffffff;
|
||||
}
|
||||
|
||||
hw->tmout.data = timeout_cycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set response timeout cycles (in card output clocks)
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param timeout_cycles timeout cycles
|
||||
*/
|
||||
static inline void sdmmc_ll_set_response_timeout(sdmmc_dev_t *hw, uint32_t timeout_cycles)
|
||||
{
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tmout, response, timeout_cycles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if card is detected
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param slot slot
|
||||
*
|
||||
* @return True for detected
|
||||
*/
|
||||
static inline bool sdmmc_ll_is_card_detected(sdmmc_dev_t *hw, uint32_t slot)
|
||||
{
|
||||
return ((hw->cdetect.cards & BIT(slot)) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if card is write protected
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param slot slot
|
||||
*
|
||||
* @return True for write protected
|
||||
*/
|
||||
static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t slot)
|
||||
{
|
||||
bool is_protected = hw->wrtprt.cards & BIT(slot);
|
||||
return is_protected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switch between 3.3V and 1.8V mode
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param slot slot
|
||||
* @param en enable / disable 1.8V (3.3V on disable)
|
||||
*/
|
||||
static inline void sdmmc_ll_enable_1v8_mode(sdmmc_dev_t *hw, uint32_t slot, bool en)
|
||||
{
|
||||
//for compatibility
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable DDR mode
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param slot slot
|
||||
* @param en enable / disable
|
||||
*/
|
||||
static inline void sdmmc_ll_enable_ddr_mode(sdmmc_dev_t *hw, uint32_t slot, bool en)
|
||||
{
|
||||
uint32_t ddr_reg_val = HAL_FORCE_READ_U32_REG_FIELD(hw->uhs, ddr);
|
||||
if (en) {
|
||||
ddr_reg_val|= BIT(slot);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->uhs, ddr, ddr_reg_val);
|
||||
hw->emmc_ddr_reg |= BIT(slot);
|
||||
} else {
|
||||
ddr_reg_val&= ~BIT(slot);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->uhs, ddr, ddr_reg_val);
|
||||
hw->emmc_ddr_reg &= ~BIT(slot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set data transfer length
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param len length
|
||||
*/
|
||||
static inline void sdmmc_ll_set_data_transfer_len(sdmmc_dev_t *hw, uint32_t len)
|
||||
{
|
||||
hw->bytcnt = len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set block size
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param block_size block size
|
||||
*/
|
||||
static inline void sdmmc_ll_set_block_size(sdmmc_dev_t *hw, uint32_t block_size)
|
||||
{
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->blksiz, block_size, block_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set descriptor addr
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param block_size block size
|
||||
*/
|
||||
static inline void sdmmc_ll_set_desc_addr(sdmmc_dev_t *hw, uint32_t desc_addr)
|
||||
{
|
||||
hw->dbaddr = (sdmmc_desc_t *)desc_addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Poll demand
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*/
|
||||
static inline void sdmmc_ll_poll_demand(sdmmc_dev_t *hw)
|
||||
{
|
||||
hw->pldmnd = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set command
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*/
|
||||
static inline void sdmmc_ll_set_command(sdmmc_dev_t *hw, sdmmc_hw_cmd_t cmd)
|
||||
{
|
||||
memcpy((void *)&hw->cmd, &cmd, sizeof(sdmmc_hw_cmd_t));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get if command is taken by CIU
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*
|
||||
* @return 1: is taken; 0: not taken, should not write to any command regs
|
||||
*/
|
||||
static inline bool sdmmc_ll_is_command_taken(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->cmd.start_command == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set command argument
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param arg value indicates command argument to be passed to card
|
||||
*/
|
||||
static inline void sdmmc_ll_set_command_arg(sdmmc_dev_t *hw, uint32_t arg)
|
||||
{
|
||||
hw->cmdarg = arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get version ID
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*
|
||||
* @return version ID
|
||||
*/
|
||||
static inline uint32_t sdmmc_ll_get_version_id(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->verid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get hardware configuration info
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*
|
||||
* @return hardware configurations
|
||||
*/
|
||||
static inline uint32_t sdmmc_ll_get_hw_config_info(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->hcon.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set card width
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param slot slot ID
|
||||
* @param width card width
|
||||
*/
|
||||
static inline void sdmmc_ll_set_card_width(sdmmc_dev_t *hw, uint32_t slot, sd_bus_width_t width)
|
||||
{
|
||||
uint16_t mask = 1 << slot;
|
||||
uint32_t reg_val = HAL_FORCE_READ_U32_REG_FIELD(hw->ctype, card_width);
|
||||
uint32_t reg_val_8 = HAL_FORCE_READ_U32_REG_FIELD(hw->ctype, card_width_8);
|
||||
|
||||
switch (width) {
|
||||
case SD_BUS_WIDTH_1_BIT:
|
||||
reg_val_8 &= ~mask;
|
||||
reg_val &= ~mask;
|
||||
break;
|
||||
case SD_BUS_WIDTH_4_BIT:
|
||||
reg_val_8 &= ~mask;
|
||||
reg_val |= mask;
|
||||
break;
|
||||
case SD_BUS_WIDTH_8_BIT:
|
||||
reg_val_8 |= mask;
|
||||
break;
|
||||
default:
|
||||
HAL_ASSERT(false);
|
||||
}
|
||||
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->ctype, card_width, reg_val);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->ctype, card_width_8, reg_val_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Is card data busy
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*
|
||||
* @return 1: busy; 0: idle
|
||||
*/
|
||||
static inline bool sdmmc_ll_is_card_data_busy(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->status.data_busy == 1;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
DMA
|
||||
---------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Init DMA
|
||||
* - enable dma
|
||||
* - clear bus mode reg and reset all dmac internal regs
|
||||
* - enable internal dmac interrupt
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*/
|
||||
static inline void sdmmc_ll_init_dma(sdmmc_dev_t *hw)
|
||||
{
|
||||
hw->ctrl.dma_enable = 1;
|
||||
hw->bmod.val = 0;
|
||||
hw->bmod.sw_reset = 1;
|
||||
hw->idinten.ni = 1;
|
||||
hw->idinten.ri = 1;
|
||||
hw->idinten.ti = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable DMA
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param en enable / disable
|
||||
*/
|
||||
static inline void sdmmc_ll_enable_dma(sdmmc_dev_t *hw, bool en)
|
||||
{
|
||||
hw->ctrl.dma_enable = en;
|
||||
hw->ctrl.use_internal_dma = en;
|
||||
hw->bmod.enable = en;
|
||||
hw->bmod.fb = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop DMA
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*/
|
||||
static inline void sdmmc_ll_stop_dma(sdmmc_dev_t *hw)
|
||||
{
|
||||
hw->ctrl.use_internal_dma = 0;
|
||||
hw->ctrl.dma_reset = 1; //here might be an issue as we don't wait the `dma_reset` to be self-cleared, check in next steps
|
||||
hw->bmod.fb = 0;
|
||||
hw->bmod.enable = 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
INTR
|
||||
---------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Get masked interrupt-status register value
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
*/
|
||||
static inline uint32_t sdmmc_ll_get_intr_status(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->mintsts.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable interrupt
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param mask interrupt mask
|
||||
* @param en enable / disable
|
||||
*/
|
||||
static inline void sdmmc_ll_enable_interrupt(sdmmc_dev_t *hw, uint32_t mask, bool en)
|
||||
{
|
||||
if (en) {
|
||||
hw->intmask.val |= mask;
|
||||
} else {
|
||||
hw->intmask.val &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get RAW interrupt-status register value
|
||||
*/
|
||||
static inline uint32_t sdmmc_ll_get_interrupt_raw(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->rintsts.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear interrupt
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param mask interrupt mask
|
||||
*/
|
||||
static inline void sdmmc_ll_clear_interrupt(sdmmc_dev_t *hw, uint32_t mask)
|
||||
{
|
||||
hw->rintsts.val = mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable / disable interrupts globally
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param en enable / disable
|
||||
*/
|
||||
static inline void sdmmc_ll_enable_global_interrupt(sdmmc_dev_t *hw, bool en)
|
||||
{
|
||||
hw->ctrl.int_enable = (uint32_t)en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable / disable busy clear interrupt
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param en enable / disable
|
||||
*/
|
||||
static inline void sdmmc_ll_enable_busy_clear_interrupt(sdmmc_dev_t *hw, bool en)
|
||||
{
|
||||
hw->cardthrctl.busy_clr_int_en = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get internal dmac status register val
|
||||
*/
|
||||
static inline uint32_t sdmmc_ll_get_idsts_interrupt_raw(sdmmc_dev_t *hw)
|
||||
{
|
||||
return hw->idsts.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear internal dmac status register events
|
||||
*
|
||||
* @param hw hardware instance address
|
||||
* @param mask interrupt mask
|
||||
*/
|
||||
static inline void sdmmc_ll_clear_idsts_interrupt(sdmmc_dev_t *hw, uint32_t mask)
|
||||
{
|
||||
hw->idsts.val = mask;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user