| /* |
| * Copyright (c) 2023 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT intel_emmc_host |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/devicetree.h> |
| #include <zephyr/drivers/sdhc.h> |
| #include <zephyr/sd/sd_spec.h> |
| #include <zephyr/cache.h> |
| #include "intel_emmc_host.h" |
| #if DT_ANY_INST_ON_BUS_STATUS_OKAY(pcie) |
| BUILD_ASSERT(IS_ENABLED(CONFIG_PCIE), "DT need CONFIG_PCIE"); |
| #include <zephyr/drivers/pcie/pcie.h> |
| #endif |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(emmc_hc, CONFIG_SDHC_LOG_LEVEL); |
| |
| typedef void (*emmc_isr_cb_t)(const struct device *dev); |
| |
| #ifdef CONFIG_INTEL_EMMC_HOST_ADMA_DESC_SIZE |
| #define ADMA_DESC_SIZE CONFIG_INTEL_EMMC_HOST_ADMA_DESC_SIZE |
| #else |
| #define ADMA_DESC_SIZE 0 |
| #endif |
| |
| struct emmc_config { |
| #if DT_ANY_INST_ON_BUS_STATUS_OKAY(pcie) |
| struct pcie_dev *pcie; |
| #else |
| DEVICE_MMIO_ROM; |
| #endif |
| emmc_isr_cb_t config_func; |
| uint32_t max_bus_freq; |
| uint32_t min_bus_freq; |
| uint32_t power_delay_ms; |
| uint8_t hs200_mode: 1; |
| uint8_t hs400_mode: 1; |
| uint8_t dw_4bit: 1; |
| uint8_t dw_8bit: 1; |
| }; |
| |
| struct emmc_data { |
| DEVICE_MMIO_RAM; |
| uint32_t rca; |
| struct sdhc_io host_io; |
| struct k_sem lock; |
| struct k_event irq_event; |
| uint64_t desc_table[ADMA_DESC_SIZE]; |
| struct sdhc_host_props props; |
| bool card_present; |
| }; |
| |
| static void enable_interrupts(const struct device *dev) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| regs->normal_int_stat_en = EMMC_HOST_NORMAL_INTR_MASK; |
| regs->err_int_stat_en = EMMC_HOST_ERROR_INTR_MASK; |
| regs->normal_int_signal_en = EMMC_HOST_NORMAL_INTR_MASK; |
| regs->err_int_signal_en = EMMC_HOST_ERROR_INTR_MASK; |
| regs->timeout_ctrl = EMMC_HOST_MAX_TIMEOUT; |
| } |
| |
| static void disable_interrupts(const struct device *dev) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| /* Keep enable interrupt status register to update */ |
| regs->normal_int_stat_en = EMMC_HOST_NORMAL_INTR_MASK; |
| regs->err_int_stat_en = EMMC_HOST_ERROR_INTR_MASK; |
| |
| /* Disable only interrupt generation */ |
| regs->normal_int_signal_en &= 0; |
| regs->err_int_signal_en &= 0; |
| regs->timeout_ctrl = EMMC_HOST_MAX_TIMEOUT; |
| } |
| |
| static void clear_interrupts(const struct device *dev) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| regs->normal_int_stat = EMMC_HOST_NORMAL_INTR_MASK_CLR; |
| regs->err_int_stat = EMMC_HOST_ERROR_INTR_MASK; |
| } |
| |
| static int emmc_set_voltage(const struct device *dev, enum sd_voltage signal_voltage) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| bool power_state = regs->power_ctrl & EMMC_HOST_POWER_CTRL_SD_BUS_POWER ? true : false; |
| int ret = 0; |
| |
| if (power_state) { |
| /* Turn OFF Bus Power before config clock */ |
| regs->power_ctrl &= ~EMMC_HOST_POWER_CTRL_SD_BUS_POWER; |
| } |
| |
| switch (signal_voltage) { |
| case SD_VOL_3_3_V: |
| if (regs->capabilities & EMMC_HOST_VOL_3_3_V_SUPPORT) { |
| regs->host_ctrl2 &= |
| ~(EMMC_HOST_CTRL2_1P8V_SIG_EN << EMMC_HOST_CTRL2_1P8V_SIG_LOC); |
| |
| /* 3.3v voltage select */ |
| regs->power_ctrl = EMMC_HOST_VOL_3_3_V_SELECT; |
| LOG_DBG("3.3V Selected for MMC Card"); |
| } else { |
| LOG_ERR("3.3V not supported by MMC Host"); |
| ret = -ENOTSUP; |
| } |
| break; |
| |
| case SD_VOL_3_0_V: |
| if (regs->capabilities & EMMC_HOST_VOL_3_0_V_SUPPORT) { |
| regs->host_ctrl2 &= |
| ~(EMMC_HOST_CTRL2_1P8V_SIG_EN << EMMC_HOST_CTRL2_1P8V_SIG_LOC); |
| |
| /* 3.0v voltage select */ |
| regs->power_ctrl = EMMC_HOST_VOL_3_0_V_SELECT; |
| LOG_DBG("3.0V Selected for MMC Card"); |
| } else { |
| LOG_ERR("3.0V not supported by MMC Host"); |
| ret = -ENOTSUP; |
| } |
| break; |
| |
| case SD_VOL_1_8_V: |
| if (regs->capabilities & EMMC_HOST_VOL_1_8_V_SUPPORT) { |
| regs->host_ctrl2 |= EMMC_HOST_CTRL2_1P8V_SIG_EN |
| << EMMC_HOST_CTRL2_1P8V_SIG_LOC; |
| |
| /* 1.8v voltage select */ |
| regs->power_ctrl = EMMC_HOST_VOL_1_8_V_SELECT; |
| LOG_DBG("1.8V Selected for MMC Card"); |
| } else { |
| LOG_ERR("1.8V not supported by MMC Host"); |
| ret = -ENOTSUP; |
| } |
| break; |
| |
| default: |
| ret = -EINVAL; |
| } |
| |
| if (power_state) { |
| /* Turn ON Bus Power */ |
| regs->power_ctrl |= EMMC_HOST_POWER_CTRL_SD_BUS_POWER; |
| } |
| |
| return ret; |
| } |
| |
| static int emmc_set_power(const struct device *dev, enum sdhc_power state) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| if (state == SDHC_POWER_ON) { |
| /* Turn ON Bus Power */ |
| regs->power_ctrl |= EMMC_HOST_POWER_CTRL_SD_BUS_POWER; |
| } else { |
| /* Turn OFF Bus Power */ |
| regs->power_ctrl &= ~EMMC_HOST_POWER_CTRL_SD_BUS_POWER; |
| } |
| |
| k_msleep(10u); |
| |
| return 0; |
| } |
| |
| static bool emmc_disable_clock(const struct device *dev) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| if (regs->present_state & EMMC_HOST_PSTATE_CMD_INHIBIT) { |
| LOG_ERR("present_state:%x", regs->present_state); |
| return false; |
| } |
| if (regs->present_state & EMMC_HOST_PSTATE_DAT_INHIBIT) { |
| LOG_ERR("present_state:%x", regs->present_state); |
| return false; |
| } |
| |
| regs->clock_ctrl &= ~EMMC_HOST_INTERNAL_CLOCK_EN; |
| regs->clock_ctrl &= ~EMMC_HOST_SD_CLOCK_EN; |
| |
| while ((regs->clock_ctrl & EMMC_HOST_SD_CLOCK_EN) != 0) { |
| ; |
| } |
| |
| return true; |
| } |
| |
| static bool emmc_enable_clock(const struct device *dev) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| regs->clock_ctrl |= EMMC_HOST_INTERNAL_CLOCK_EN; |
| /* Wait for the stable Internal Clock */ |
| while ((regs->clock_ctrl & EMMC_HOST_INTERNAL_CLOCK_STABLE) == 0) { |
| ; |
| } |
| |
| /* Enable SD Clock */ |
| regs->clock_ctrl |= EMMC_HOST_SD_CLOCK_EN; |
| while ((regs->clock_ctrl & EMMC_HOST_SD_CLOCK_EN) == 0) { |
| ; |
| } |
| |
| return true; |
| } |
| |
| static bool emmc_clock_set(const struct device *dev, enum sdhc_clock_speed speed) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| uint8_t base_freq; |
| uint32_t clock_divider; |
| float freq; |
| bool ret; |
| |
| switch (speed) { |
| case SDMMC_CLOCK_400KHZ: |
| freq = EMMC_HOST_CLK_FREQ_400K; |
| break; |
| |
| case SD_CLOCK_25MHZ: |
| case MMC_CLOCK_26MHZ: |
| freq = EMMC_HOST_CLK_FREQ_25M; |
| break; |
| |
| case SD_CLOCK_50MHZ: |
| case MMC_CLOCK_52MHZ: |
| freq = EMMC_HOST_CLK_FREQ_50M; |
| break; |
| |
| case SD_CLOCK_100MHZ: |
| freq = EMMC_HOST_CLK_FREQ_100M; |
| break; |
| |
| case MMC_CLOCK_HS200: |
| freq = EMMC_HOST_CLK_FREQ_200M; |
| break; |
| |
| case SD_CLOCK_208MHZ: |
| default: |
| return false; |
| } |
| |
| ret = emmc_disable_clock(dev); |
| if (!ret) { |
| return false; |
| } |
| |
| base_freq = regs->capabilities >> 8; |
| clock_divider = (int)(base_freq / (freq * 2)); |
| |
| LOG_DBG("Clock divider for MMC Clk: %d Hz is %d", speed, clock_divider); |
| |
| SET_BITS(regs->clock_ctrl, EMMC_HOST_CLK_SDCLCK_FREQ_SEL_LOC, |
| EMMC_HOST_CLK_SDCLCK_FREQ_SEL_MASK, clock_divider); |
| SET_BITS(regs->clock_ctrl, EMMC_HOST_CLK_SDCLCK_FREQ_SEL_UPPER_LOC, |
| EMMC_HOST_CLK_SDCLCK_FREQ_SEL_UPPER_MASK, clock_divider >> 8); |
| |
| emmc_enable_clock(dev); |
| |
| return true; |
| } |
| |
| static int set_timing(const struct device *dev, enum sdhc_timing_mode timing) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| int ret = 0; |
| uint8_t mode; |
| |
| LOG_DBG("UHS Mode: %d", timing); |
| |
| switch (timing) { |
| case SDHC_TIMING_LEGACY: |
| case SDHC_TIMING_HS: |
| case SDHC_TIMING_SDR12: |
| mode = EMMC_HOST_UHSMODE_SDR12; |
| break; |
| |
| case SDHC_TIMING_SDR25: |
| mode = EMMC_HOST_UHSMODE_SDR25; |
| break; |
| |
| case SDHC_TIMING_SDR50: |
| mode = EMMC_HOST_UHSMODE_SDR50; |
| break; |
| |
| case SDHC_TIMING_SDR104: |
| mode = EMMC_HOST_UHSMODE_SDR104; |
| break; |
| |
| case SDHC_TIMING_DDR50: |
| case SDHC_TIMING_DDR52: |
| mode = EMMC_HOST_UHSMODE_DDR50; |
| break; |
| |
| case SDHC_TIMING_HS400: |
| case SDHC_TIMING_HS200: |
| mode = EMMC_HOST_UHSMODE_HS400; |
| break; |
| |
| default: |
| ret = -ENOTSUP; |
| } |
| |
| if (!ret) { |
| if (!emmc_disable_clock(dev)) { |
| LOG_ERR("Disable clk failed"); |
| return -EIO; |
| } |
| regs->host_ctrl2 |= EMMC_HOST_CTRL2_1P8V_SIG_EN << EMMC_HOST_CTRL2_1P8V_SIG_LOC; |
| SET_BITS(regs->host_ctrl2, EMMC_HOST_CTRL2_UHS_MODE_SEL_LOC, |
| EMMC_HOST_CTRL2_UHS_MODE_SEL_MASK, mode); |
| |
| emmc_enable_clock(dev); |
| } |
| |
| return ret; |
| } |
| |
| static int wait_for_cmd_complete(struct emmc_data *emmc, uint32_t time_out) |
| { |
| int ret; |
| k_timeout_t wait_time; |
| uint32_t events; |
| |
| if (time_out == SDHC_TIMEOUT_FOREVER) { |
| wait_time = K_FOREVER; |
| } else { |
| wait_time = K_MSEC(time_out); |
| } |
| |
| events = k_event_wait(&emmc->irq_event, |
| EMMC_HOST_CMD_COMPLETE | ERR_INTR_STATUS_EVENT(EMMC_HOST_ERR_STATUS), |
| false, wait_time); |
| |
| if (events & EMMC_HOST_CMD_COMPLETE) { |
| ret = 0; |
| } else if (events & ERR_INTR_STATUS_EVENT(EMMC_HOST_ERR_STATUS)) { |
| LOG_ERR("wait for cmd complete error: %x", events); |
| ret = -EIO; |
| } else { |
| LOG_ERR("wait for cmd complete timeout"); |
| ret = -EAGAIN; |
| } |
| |
| return ret; |
| } |
| |
| static int poll_cmd_complete(const struct device *dev, uint32_t time_out) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| int ret = -EAGAIN; |
| int32_t retry = time_out; |
| |
| while (retry > 0) { |
| if (regs->normal_int_stat & EMMC_HOST_CMD_COMPLETE) { |
| regs->normal_int_stat = EMMC_HOST_CMD_COMPLETE; |
| ret = 0; |
| break; |
| } |
| |
| k_busy_wait(1000u); |
| retry--; |
| } |
| |
| if (regs->err_int_stat) { |
| LOG_ERR("err_int_stat:%x", regs->err_int_stat); |
| regs->err_int_stat &= regs->err_int_stat; |
| ret = -EIO; |
| } |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_ADMA)) { |
| if (regs->adma_err_stat) { |
| LOG_ERR("adma error: %x", regs->adma_err_stat); |
| ret = -EIO; |
| } |
| } |
| return ret; |
| } |
| |
| void emmc_host_sw_reset(const struct device *dev, enum emmc_sw_reset reset) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| if (reset == EMMC_HOST_SW_RESET_DATA_LINE) { |
| regs->sw_reset = EMMC_HOST_SW_RESET_REG_DATA; |
| } else if (reset == EMMC_HOST_SW_RESET_CMD_LINE) { |
| regs->sw_reset = EMMC_HOST_SW_RESET_REG_CMD; |
| } else if (reset == EMMC_HOST_SW_RESET_ALL) { |
| regs->sw_reset = EMMC_HOST_SW_RESET_REG_ALL; |
| } |
| |
| while (regs->sw_reset != 0) { |
| ; |
| } |
| |
| k_sleep(K_MSEC(100u)); |
| } |
| |
| static int emmc_dma_init(const struct device *dev, struct sdhc_data *data, bool read) |
| { |
| struct emmc_data *emmc = dev->data; |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| if (IS_ENABLED(CONFIG_DCACHE) && !read) { |
| sys_cache_data_flush_range(data->data, (data->blocks * data->block_size)); |
| } |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_ADMA)) { |
| uint8_t *buff = data->data; |
| |
| /* Setup DMA trasnfer using ADMA2 */ |
| memset(emmc->desc_table, 0, sizeof(emmc->desc_table)); |
| |
| #if defined(CONFIG_INTEL_EMMC_HOST_ADMA_DESC_SIZE) |
| __ASSERT_NO_MSG(data->blocks < CONFIG_INTEL_EMMC_HOST_ADMA_DESC_SIZE); |
| #endif |
| for (int i = 0; i < data->blocks; i++) { |
| emmc->desc_table[i] = ((uint64_t)buff) << EMMC_HOST_ADMA_BUFF_ADD_LOC; |
| emmc->desc_table[i] |= data->block_size << EMMC_HOST_ADMA_BUFF_LEN_LOC; |
| |
| if (i == (data->blocks - 1u)) { |
| emmc->desc_table[i] |= EMMC_HOST_ADMA_BUFF_LINK_LAST; |
| emmc->desc_table[i] |= EMMC_HOST_ADMA_INTR_EN; |
| emmc->desc_table[i] |= EMMC_HOST_ADMA_BUFF_LAST; |
| } else { |
| emmc->desc_table[i] |= EMMC_HOST_ADMA_BUFF_LINK_NEXT; |
| } |
| emmc->desc_table[i] |= EMMC_HOST_ADMA_BUFF_VALID; |
| buff += data->block_size; |
| LOG_DBG("desc_table:%llx", emmc->desc_table[i]); |
| } |
| |
| regs->adma_sys_addr1 = (uint32_t)((uintptr_t)emmc->desc_table & ADDRESS_32BIT_MASK); |
| regs->adma_sys_addr2 = |
| (uint32_t)(((uintptr_t)emmc->desc_table >> 32) & ADDRESS_32BIT_MASK); |
| |
| LOG_DBG("adma: %llx %x %p", emmc->desc_table[0], regs->adma_sys_addr1, |
| emmc->desc_table); |
| } else { |
| /* Setup DMA trasnfer using SDMA */ |
| regs->sdma_sysaddr = (uint32_t)((uintptr_t)data->data); |
| LOG_DBG("sdma_sysaddr: %x", regs->sdma_sysaddr); |
| } |
| return 0; |
| } |
| |
| static int emmc_init_xfr(const struct device *dev, struct sdhc_data *data, bool read) |
| { |
| struct emmc_data *emmc = dev->data; |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| uint16_t multi_block = 0u; |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_DMA)) { |
| emmc_dma_init(dev, data, read); |
| } |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_ADMA)) { |
| SET_BITS(regs->host_ctrl1, EMMC_HOST_CTRL1_DMA_SEL_LOC, |
| EMMC_HOST_CTRL1_DMA_SEL_MASK, 2u); |
| } else { |
| SET_BITS(regs->host_ctrl1, EMMC_HOST_CTRL1_DMA_SEL_LOC, |
| EMMC_HOST_CTRL1_DMA_SEL_MASK, 0u); |
| } |
| |
| /* Set Block Size Register */ |
| SET_BITS(regs->block_size, EMMC_HOST_DMA_BUF_SIZE_LOC, EMMC_HOST_DMA_BUF_SIZE_MASK, |
| EMMC_HOST_SDMA_BOUNDARY); |
| SET_BITS(regs->block_size, EMMC_HOST_BLOCK_SIZE_LOC, EMMC_HOST_BLOCK_SIZE_MASK, |
| data->block_size); |
| if (data->blocks > 1) { |
| multi_block = 1u; |
| } |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_AUTO_STOP)) { |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_ADMA) && |
| emmc->host_io.timing == SDHC_TIMING_SDR104) { |
| /* Auto cmd23 only applicable for ADMA */ |
| SET_BITS(regs->transfer_mode, EMMC_HOST_XFER_AUTO_CMD_EN_LOC, |
| EMMC_HOST_XFER_AUTO_CMD_EN_MASK, multi_block ? 2 : 0); |
| } else { |
| SET_BITS(regs->transfer_mode, EMMC_HOST_XFER_AUTO_CMD_EN_LOC, |
| EMMC_HOST_XFER_AUTO_CMD_EN_MASK, multi_block ? 1 : 0); |
| } |
| } else { |
| SET_BITS(regs->transfer_mode, EMMC_HOST_XFER_AUTO_CMD_EN_LOC, |
| EMMC_HOST_XFER_AUTO_CMD_EN_MASK, 0); |
| } |
| |
| if (!IS_ENABLED(CONFIG_INTEL_EMMC_HOST_AUTO_STOP)) { |
| /* Set block count regitser to 0 for infinite transfer mode */ |
| regs->block_count = 0; |
| SET_BITS(regs->transfer_mode, EMMC_HOST_XFER_BLOCK_CNT_EN_LOC, |
| EMMC_HOST_XFER_BLOCK_CNT_EN_MASK, 0); |
| } else { |
| regs->block_count = (uint16_t)data->blocks; |
| /* Enable block count in transfer register */ |
| SET_BITS(regs->transfer_mode, EMMC_HOST_XFER_BLOCK_CNT_EN_LOC, |
| EMMC_HOST_XFER_BLOCK_CNT_EN_MASK, multi_block ? 1 : 0); |
| } |
| |
| SET_BITS(regs->transfer_mode, EMMC_HOST_XFER_MULTI_BLOCK_SEL_LOC, |
| EMMC_HOST_XFER_MULTI_BLOCK_SEL_MASK, multi_block); |
| |
| /* Set data transfer direction, Read = 1, Write = 0 */ |
| SET_BITS(regs->transfer_mode, EMMC_HOST_XFER_DATA_DIR_LOC, EMMC_HOST_XFER_DATA_DIR_MASK, |
| read ? 1u : 0u); |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_DMA)) { |
| /* Enable DMA or not */ |
| SET_BITS(regs->transfer_mode, EMMC_HOST_XFER_DMA_EN_LOC, EMMC_HOST_XFER_DMA_EN_MASK, |
| 1u); |
| } else { |
| SET_BITS(regs->transfer_mode, EMMC_HOST_XFER_DMA_EN_LOC, EMMC_HOST_XFER_DMA_EN_MASK, |
| 0u); |
| } |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_BLOCK_GAP)) { |
| /* Set an interrupt at the block gap */ |
| SET_BITS(regs->block_gap_ctrl, EMMC_HOST_BLOCK_GAP_LOC, EMMC_HOST_BLOCK_GAP_MASK, |
| 1u); |
| } else { |
| SET_BITS(regs->block_gap_ctrl, EMMC_HOST_BLOCK_GAP_LOC, EMMC_HOST_BLOCK_GAP_MASK, |
| 0u); |
| } |
| |
| /* Set data timeout time */ |
| regs->timeout_ctrl = data->timeout_ms; |
| |
| return 0; |
| } |
| |
| static int wait_xfr_intr_complete(const struct device *dev, uint32_t time_out) |
| { |
| struct emmc_data *emmc = dev->data; |
| uint32_t events; |
| int ret; |
| k_timeout_t wait_time; |
| |
| LOG_DBG(""); |
| |
| if (time_out == SDHC_TIMEOUT_FOREVER) { |
| wait_time = K_FOREVER; |
| } else { |
| wait_time = K_MSEC(time_out); |
| } |
| |
| events = k_event_wait(&emmc->irq_event, |
| EMMC_HOST_XFER_COMPLETE | |
| ERR_INTR_STATUS_EVENT(EMMC_HOST_DMA_TXFR_ERR), |
| false, wait_time); |
| |
| if (events & EMMC_HOST_XFER_COMPLETE) { |
| ret = 0; |
| } else if (events & ERR_INTR_STATUS_EVENT(0xFFFF)) { |
| LOG_ERR("wait for xfer complete error: %x", events); |
| ret = -EIO; |
| } else { |
| LOG_ERR("wait for xfer complete timeout"); |
| ret = -EAGAIN; |
| } |
| |
| return ret; |
| } |
| |
| static int wait_xfr_poll_complete(const struct device *dev, uint32_t time_out) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| int ret = -EAGAIN; |
| int32_t retry = time_out; |
| |
| LOG_DBG(""); |
| |
| while (retry > 0) { |
| if (regs->normal_int_stat & EMMC_HOST_XFER_COMPLETE) { |
| regs->normal_int_stat |= EMMC_HOST_XFER_COMPLETE; |
| ret = 0; |
| break; |
| } |
| |
| k_busy_wait(EMMC_HOST_MSEC_DELAY); |
| retry--; |
| } |
| |
| return ret; |
| } |
| |
| static int wait_xfr_complete(const struct device *dev, uint32_t time_out) |
| { |
| int ret; |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_INTR)) { |
| ret = wait_xfr_intr_complete(dev, time_out); |
| } else { |
| ret = wait_xfr_poll_complete(dev, time_out); |
| } |
| return ret; |
| } |
| |
| static enum emmc_response_type emmc_decode_resp_type(enum sd_rsp_type type) |
| { |
| enum emmc_response_type resp_type; |
| |
| switch (type & 0xF) { |
| case SD_RSP_TYPE_NONE: |
| resp_type = EMMC_HOST_RESP_NONE; |
| break; |
| case SD_RSP_TYPE_R1: |
| case SD_RSP_TYPE_R3: |
| case SD_RSP_TYPE_R4: |
| case SD_RSP_TYPE_R5: |
| resp_type = EMMC_HOST_RESP_LEN_48; |
| break; |
| case SD_RSP_TYPE_R1b: |
| resp_type = EMMC_HOST_RESP_LEN_48B; |
| break; |
| case SD_RSP_TYPE_R2: |
| resp_type = EMMC_HOST_RESP_LEN_136; |
| break; |
| |
| case SD_RSP_TYPE_R5b: |
| case SD_RSP_TYPE_R6: |
| case SD_RSP_TYPE_R7: |
| default: |
| resp_type = EMMC_HOST_INVAL_HOST_RESP_LEN; |
| } |
| |
| return resp_type; |
| } |
| |
| static void update_cmd_response(const struct device *dev, struct sdhc_command *sdhc_cmd) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| uint32_t resp0, resp1, resp2, resp3; |
| |
| if (sdhc_cmd->response_type == SD_RSP_TYPE_NONE) { |
| return; |
| } |
| |
| resp0 = regs->resp_01; |
| |
| if (sdhc_cmd->response_type == SD_RSP_TYPE_R2) { |
| resp1 = regs->resp_2 | (regs->resp_3 << 16u); |
| resp2 = regs->resp_4 | (regs->resp_5 << 16u); |
| resp3 = regs->resp_6 | (regs->resp_7 << 16u); |
| |
| LOG_DBG("cmd resp: %x %x %x %x", resp0, resp1, resp2, resp3); |
| |
| sdhc_cmd->response[0u] = resp3; |
| sdhc_cmd->response[1U] = resp2; |
| sdhc_cmd->response[2U] = resp1; |
| sdhc_cmd->response[3U] = resp0; |
| } else { |
| LOG_DBG("cmd resp: %x", resp0); |
| sdhc_cmd->response[0u] = resp0; |
| } |
| } |
| |
| static int emmc_host_send_cmd(const struct device *dev, const struct emmc_cmd_config *config) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| struct emmc_data *emmc = dev->data; |
| struct sdhc_command *sdhc_cmd = config->sdhc_cmd; |
| enum emmc_response_type resp_type = emmc_decode_resp_type(sdhc_cmd->response_type); |
| uint16_t cmd_reg; |
| int ret; |
| |
| LOG_DBG(""); |
| |
| /* Check if CMD line is available */ |
| if (regs->present_state & EMMC_HOST_PSTATE_CMD_INHIBIT) { |
| LOG_ERR("CMD line is not available"); |
| return -EBUSY; |
| } |
| |
| if (config->data_present && (regs->present_state & EMMC_HOST_PSTATE_DAT_INHIBIT)) { |
| LOG_ERR("Data line is not available"); |
| return -EBUSY; |
| } |
| |
| if (resp_type == EMMC_HOST_INVAL_HOST_RESP_LEN) { |
| LOG_ERR("Invalid eMMC resp type:%d", resp_type); |
| return -EINVAL; |
| } |
| |
| k_event_clear(&emmc->irq_event, EMMC_HOST_CMD_COMPLETE); |
| |
| regs->argument = sdhc_cmd->arg; |
| |
| cmd_reg = config->cmd_idx << EMMC_HOST_CMD_INDEX_LOC | |
| config->cmd_type << EMMC_HOST_CMD_TYPE_LOC | |
| config->data_present << EMMC_HOST_CMD_DATA_PRESENT_LOC | |
| config->idx_check_en << EMMC_HOST_CMD_IDX_CHECK_EN_LOC | |
| config->crc_check_en << EMMC_HOST_CMD_CRC_CHECK_EN_LOC | |
| resp_type << EMMC_HOST_CMD_RESP_TYPE_LOC; |
| regs->cmd = cmd_reg; |
| |
| LOG_DBG("CMD REG:%x %x", cmd_reg, regs->cmd); |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_INTR)) { |
| ret = wait_for_cmd_complete(emmc, sdhc_cmd->timeout_ms); |
| } else { |
| ret = poll_cmd_complete(dev, sdhc_cmd->timeout_ms); |
| } |
| if (ret) { |
| LOG_ERR("Error on send cmd: %d, status:%d", config->cmd_idx, ret); |
| return ret; |
| } |
| |
| update_cmd_response(dev, sdhc_cmd); |
| |
| return 0; |
| } |
| |
| static int emmc_stop_transfer(const struct device *dev) |
| { |
| struct emmc_data *emmc = dev->data; |
| struct sdhc_command hdc_cmd = {0}; |
| struct emmc_cmd_config cmd; |
| |
| hdc_cmd.arg = emmc->rca << EMMC_HOST_RCA_SHIFT; |
| hdc_cmd.response_type = SD_RSP_TYPE_R1; |
| hdc_cmd.timeout_ms = 1000; |
| |
| cmd.sdhc_cmd = &hdc_cmd; |
| cmd.cmd_idx = SD_STOP_TRANSMISSION; |
| cmd.cmd_type = EMMC_HOST_CMD_NORMAL; |
| cmd.data_present = false; |
| cmd.idx_check_en = false; |
| cmd.crc_check_en = false; |
| |
| return emmc_host_send_cmd(dev, &cmd); |
| } |
| |
| static int emmc_reset(const struct device *dev) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| LOG_DBG(""); |
| |
| if (!(regs->present_state & EMMC_HOST_PSTATE_CARD_INSERTED)) { |
| LOG_ERR("No EMMC card found"); |
| return -ENODEV; |
| } |
| |
| /* Reset device to idle state */ |
| emmc_host_sw_reset(dev, EMMC_HOST_SW_RESET_ALL); |
| |
| clear_interrupts(dev); |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_INTR)) { |
| enable_interrupts(dev); |
| } else { |
| disable_interrupts(dev); |
| } |
| |
| return 0; |
| } |
| |
| static int read_data_port(const struct device *dev, struct sdhc_data *sdhc) |
| { |
| struct emmc_data *emmc = dev->data; |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| uint32_t block_size = sdhc->block_size; |
| uint32_t i, block_cnt = sdhc->blocks; |
| uint32_t *data = (uint32_t *)sdhc->data; |
| k_timeout_t wait_time; |
| |
| if (sdhc->timeout_ms == SDHC_TIMEOUT_FOREVER) { |
| wait_time = K_FOREVER; |
| } else { |
| wait_time = K_MSEC(sdhc->timeout_ms); |
| } |
| |
| LOG_DBG(""); |
| |
| while (block_cnt--) { |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_INTR)) { |
| uint32_t events; |
| |
| events = k_event_wait(&emmc->irq_event, EMMC_HOST_BUF_RD_READY, false, |
| wait_time); |
| k_event_clear(&emmc->irq_event, EMMC_HOST_BUF_RD_READY); |
| if (!(events & EMMC_HOST_BUF_RD_READY)) { |
| LOG_ERR("time out on EMMC_HOST_BUF_RD_READY:%d", |
| (sdhc->blocks - block_cnt)); |
| return -EIO; |
| } |
| } else { |
| while ((regs->present_state & EMMC_HOST_PSTATE_BUF_READ_EN) == 0) { |
| ; |
| } |
| } |
| |
| if (regs->present_state & EMMC_HOST_PSTATE_DAT_INHIBIT) { |
| for (i = block_size >> 2u; i != 0u; i--) { |
| *data = regs->data_port; |
| data++; |
| } |
| } |
| } |
| |
| return wait_xfr_complete(dev, sdhc->timeout_ms); |
| } |
| |
| static int write_data_port(const struct device *dev, struct sdhc_data *sdhc) |
| { |
| struct emmc_data *emmc = dev->data; |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| uint32_t block_size = sdhc->block_size; |
| uint32_t i, block_cnt = sdhc->blocks; |
| uint32_t *data = (uint32_t *)sdhc->data; |
| k_timeout_t wait_time; |
| |
| if (sdhc->timeout_ms == SDHC_TIMEOUT_FOREVER) { |
| wait_time = K_FOREVER; |
| } else { |
| wait_time = K_MSEC(sdhc->timeout_ms); |
| } |
| |
| LOG_DBG(""); |
| |
| while ((regs->present_state & EMMC_HOST_PSTATE_BUF_WRITE_EN) == 0) { |
| ; |
| } |
| |
| while (1) { |
| uint32_t events; |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_INTR)) { |
| k_event_clear(&emmc->irq_event, EMMC_HOST_BUF_WR_READY); |
| } |
| |
| if (regs->present_state & EMMC_HOST_PSTATE_DAT_INHIBIT) { |
| for (i = block_size >> 2u; i != 0u; i--) { |
| regs->data_port = *data; |
| data++; |
| } |
| } |
| |
| LOG_DBG("EMMC_HOST_BUF_WR_READY"); |
| |
| if (!(--block_cnt)) { |
| break; |
| } |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_INTR)) { |
| events = k_event_wait(&emmc->irq_event, EMMC_HOST_BUF_WR_READY, false, |
| wait_time); |
| k_event_clear(&emmc->irq_event, EMMC_HOST_BUF_WR_READY); |
| |
| if (!(events & EMMC_HOST_BUF_WR_READY)) { |
| LOG_ERR("time out on EMMC_HOST_BUF_WR_READY"); |
| return -EIO; |
| } |
| } else { |
| while ((regs->present_state & EMMC_HOST_PSTATE_BUF_WRITE_EN) == 0) { |
| ; |
| } |
| } |
| } |
| |
| return wait_xfr_complete(dev, sdhc->timeout_ms); |
| } |
| |
| static int emmc_send_cmd_no_data(const struct device *dev, uint32_t cmd_idx, |
| struct sdhc_command *cmd) |
| { |
| struct emmc_cmd_config emmc_cmd; |
| |
| emmc_cmd.sdhc_cmd = cmd; |
| emmc_cmd.cmd_idx = cmd_idx; |
| emmc_cmd.cmd_type = EMMC_HOST_CMD_NORMAL; |
| emmc_cmd.data_present = false; |
| emmc_cmd.idx_check_en = false; |
| emmc_cmd.crc_check_en = false; |
| |
| return emmc_host_send_cmd(dev, &emmc_cmd); |
| } |
| |
| static int emmc_send_cmd_data(const struct device *dev, uint32_t cmd_idx, |
| struct sdhc_command *cmd, struct sdhc_data *data, bool read) |
| { |
| struct emmc_cmd_config emmc_cmd; |
| int ret; |
| |
| emmc_cmd.sdhc_cmd = cmd; |
| emmc_cmd.cmd_idx = cmd_idx; |
| emmc_cmd.cmd_type = EMMC_HOST_CMD_NORMAL; |
| emmc_cmd.data_present = true; |
| emmc_cmd.idx_check_en = true; |
| emmc_cmd.crc_check_en = true; |
| |
| ret = emmc_init_xfr(dev, data, read); |
| if (ret) { |
| LOG_ERR("Error on init xfr"); |
| return ret; |
| } |
| |
| ret = emmc_host_send_cmd(dev, &emmc_cmd); |
| if (ret) { |
| return ret; |
| } |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_DMA)) { |
| ret = wait_xfr_complete(dev, data->timeout_ms); |
| } else { |
| if (read) { |
| ret = read_data_port(dev, data); |
| } else { |
| ret = write_data_port(dev, data); |
| } |
| } |
| |
| return ret; |
| } |
| |
| static int emmc_xfr(const struct device *dev, struct sdhc_command *cmd, struct sdhc_data *data, |
| bool read) |
| { |
| struct emmc_data *emmc = dev->data; |
| int ret; |
| struct emmc_cmd_config emmc_cmd; |
| |
| ret = emmc_init_xfr(dev, data, read); |
| if (ret) { |
| LOG_ERR("error emmc init xfr"); |
| return ret; |
| } |
| emmc_cmd.sdhc_cmd = cmd; |
| emmc_cmd.cmd_type = EMMC_HOST_CMD_NORMAL; |
| emmc_cmd.data_present = true; |
| emmc_cmd.idx_check_en = true; |
| emmc_cmd.crc_check_en = true; |
| |
| k_event_clear(&emmc->irq_event, EMMC_HOST_XFER_COMPLETE); |
| k_event_clear(&emmc->irq_event, read ? EMMC_HOST_BUF_RD_READY : EMMC_HOST_BUF_WR_READY); |
| |
| if (data->blocks > 1) { |
| emmc_cmd.cmd_idx = read ? SD_READ_MULTIPLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK; |
| ret = emmc_host_send_cmd(dev, &emmc_cmd); |
| } else { |
| emmc_cmd.cmd_idx = read ? SD_READ_SINGLE_BLOCK : SD_WRITE_SINGLE_BLOCK; |
| ret = emmc_host_send_cmd(dev, &emmc_cmd); |
| } |
| |
| if (ret) { |
| return ret; |
| } |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_DMA)) { |
| ret = wait_xfr_complete(dev, data->timeout_ms); |
| } else { |
| if (read) { |
| ret = read_data_port(dev, data); |
| } else { |
| ret = write_data_port(dev, data); |
| } |
| } |
| |
| if (!IS_ENABLED(CONFIG_INTEL_EMMC_HOST_AUTO_STOP)) { |
| emmc_stop_transfer(dev); |
| } |
| return ret; |
| } |
| |
| static int emmc_request(const struct device *dev, struct sdhc_command *cmd, struct sdhc_data *data) |
| { |
| int ret; |
| |
| LOG_DBG(""); |
| |
| if (data) { |
| switch (cmd->opcode) { |
| case SD_WRITE_SINGLE_BLOCK: |
| case SD_WRITE_MULTIPLE_BLOCK: |
| LOG_DBG("SD_WRITE_SINGLE_BLOCK"); |
| ret = emmc_xfr(dev, cmd, data, false); |
| break; |
| |
| case SD_READ_SINGLE_BLOCK: |
| case SD_READ_MULTIPLE_BLOCK: |
| LOG_DBG("SD_READ_SINGLE_BLOCK"); |
| ret = emmc_xfr(dev, cmd, data, true); |
| break; |
| |
| case MMC_SEND_EXT_CSD: |
| LOG_DBG("EMMC_HOST_SEND_EXT_CSD"); |
| ret = emmc_send_cmd_data(dev, MMC_SEND_EXT_CSD, cmd, data, true); |
| break; |
| |
| default: |
| ret = emmc_send_cmd_data(dev, cmd->opcode, cmd, data, true); |
| } |
| } else { |
| ret = emmc_send_cmd_no_data(dev, cmd->opcode, cmd); |
| } |
| |
| return ret; |
| } |
| |
| static int emmc_set_io(const struct device *dev, struct sdhc_io *ios) |
| { |
| struct emmc_data *emmc = dev->data; |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| struct sdhc_io *host_io = &emmc->host_io; |
| int ret; |
| |
| LOG_DBG("emmc I/O: DW %d, Clk %d Hz, card power state %s, voltage %s", ios->bus_width, |
| ios->clock, ios->power_mode == SDHC_POWER_ON ? "ON" : "OFF", |
| ios->signal_voltage == SD_VOL_1_8_V ? "1.8V" : "3.3V"); |
| |
| if (ios->clock && (ios->clock > emmc->props.f_max || ios->clock < emmc->props.f_min)) { |
| LOG_ERR("Invalid argument for clock freq: %d Support max:%d and Min:%d", ios->clock, |
| emmc->props.f_max, emmc->props.f_min); |
| return -EINVAL; |
| } |
| |
| /* Set HC clock */ |
| if (host_io->clock != ios->clock) { |
| LOG_DBG("Clock: %d", host_io->clock); |
| if (ios->clock != 0) { |
| /* Enable clock */ |
| LOG_DBG("CLOCK: %d", ios->clock); |
| if (!emmc_clock_set(dev, ios->clock)) { |
| return -ENOTSUP; |
| } |
| } else { |
| emmc_disable_clock(dev); |
| } |
| host_io->clock = ios->clock; |
| } |
| |
| /* Set data width */ |
| if (host_io->bus_width != ios->bus_width) { |
| LOG_DBG("bus_width: %d", host_io->bus_width); |
| |
| if (ios->bus_width == SDHC_BUS_WIDTH4BIT) { |
| SET_BITS(regs->host_ctrl1, EMMC_HOST_CTRL1_EXT_DAT_WIDTH_LOC, |
| EMMC_HOST_CTRL1_EXT_DAT_WIDTH_MASK, |
| ios->bus_width == SDHC_BUS_WIDTH8BIT ? 1 : 0); |
| } else { |
| SET_BITS(regs->host_ctrl1, EMMC_HOST_CTRL1_DAT_WIDTH_LOC, |
| EMMC_HOST_CTRL1_DAT_WIDTH_MASK, |
| ios->bus_width == SDHC_BUS_WIDTH4BIT ? 1 : 0); |
| } |
| host_io->bus_width = ios->bus_width; |
| } |
| |
| /* Set HC signal voltage */ |
| if (ios->signal_voltage != host_io->signal_voltage) { |
| LOG_DBG("signal_voltage: %d", ios->signal_voltage); |
| ret = emmc_set_voltage(dev, ios->signal_voltage); |
| if (ret) { |
| LOG_ERR("Set signal volatge failed:%d", ret); |
| return ret; |
| } |
| host_io->signal_voltage = ios->signal_voltage; |
| } |
| |
| /* Set card power */ |
| if (host_io->power_mode != ios->power_mode) { |
| LOG_DBG("power_mode: %d", ios->power_mode); |
| |
| ret = emmc_set_power(dev, ios->power_mode); |
| if (ret) { |
| LOG_ERR("Set Bus power failed:%d", ret); |
| return ret; |
| } |
| host_io->power_mode = ios->power_mode; |
| } |
| |
| /* Set I/O timing */ |
| if (host_io->timing != ios->timing) { |
| LOG_DBG("timing: %d", ios->timing); |
| |
| ret = set_timing(dev, ios->timing); |
| if (ret) { |
| LOG_ERR("Set timing failed:%d", ret); |
| return ret; |
| } |
| host_io->timing = ios->timing; |
| } |
| |
| return 0; |
| } |
| |
| static int emmc_get_card_present(const struct device *dev) |
| { |
| struct emmc_data *emmc = dev->data; |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| LOG_DBG(""); |
| |
| emmc->card_present = (bool)((regs->present_state >> 16u) & 1u); |
| |
| if (!emmc->card_present) { |
| LOG_ERR("No MMC device detected"); |
| } |
| |
| return ((int)emmc->card_present); |
| } |
| |
| static int emmc_execute_tuning(const struct device *dev) |
| { |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_TUNING)) { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| LOG_DBG("Tuning starting..."); |
| |
| regs->host_ctrl2 |= EMMC_HOST_START_TUNING; |
| while (!(regs->host_ctrl2 & EMMC_HOST_START_TUNING)) { |
| ; |
| } |
| |
| if (regs->host_ctrl2 & EMMC_HOST_TUNING_SUCCESS) { |
| LOG_DBG("Tuning Completed success"); |
| } else { |
| LOG_ERR("Tuning failed"); |
| return -EIO; |
| } |
| } |
| return 0; |
| } |
| |
| static int emmc_card_busy(const struct device *dev) |
| { |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| LOG_DBG(""); |
| |
| if (regs->present_state & 7u) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int emmc_get_host_props(const struct device *dev, struct sdhc_host_props *props) |
| { |
| struct emmc_data *emmc = dev->data; |
| const struct emmc_config *config = dev->config; |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| uint64_t cap = regs->capabilities; |
| |
| LOG_DBG(""); |
| |
| memset(props, 0, sizeof(struct sdhc_host_props)); |
| props->f_max = config->max_bus_freq; |
| props->f_min = config->min_bus_freq; |
| props->power_delay = config->power_delay_ms; |
| |
| props->host_caps.vol_180_support = (bool)(cap & BIT(26u)); |
| props->host_caps.vol_300_support = (bool)(cap & BIT(25u)); |
| props->host_caps.vol_330_support = (bool)(bool)(cap & BIT(24u)); |
| props->host_caps.suspend_res_support = false; |
| props->host_caps.sdma_support = (bool)(cap & BIT(22u)); |
| props->host_caps.high_spd_support = (bool)(cap & BIT(21u)); |
| props->host_caps.adma_2_support = (bool)(cap & BIT(19u)); |
| |
| props->host_caps.max_blk_len = (cap >> 16u) & 0x3u; |
| props->host_caps.ddr50_support = (bool)(cap & BIT(34u)); |
| props->host_caps.sdr104_support = (bool)(cap & BIT(33u)); |
| props->host_caps.sdr50_support = (bool)(cap & BIT(32u)); |
| props->host_caps.bus_8_bit_support = true; |
| props->host_caps.bus_4_bit_support = true; |
| props->host_caps.hs200_support = (bool)config->hs200_mode; |
| props->host_caps.hs400_support = (bool)config->hs400_mode; |
| |
| emmc->props = *props; |
| |
| return 0; |
| } |
| |
| static void emmc_isr(const struct device *dev) |
| { |
| struct emmc_data *emmc = dev->data; |
| volatile struct emmc_reg *regs = (struct emmc_reg *)DEVICE_MMIO_GET(dev); |
| |
| if (regs->normal_int_stat & EMMC_HOST_CMD_COMPLETE) { |
| regs->normal_int_stat |= EMMC_HOST_CMD_COMPLETE; |
| k_event_post(&emmc->irq_event, EMMC_HOST_CMD_COMPLETE); |
| } |
| |
| if (regs->normal_int_stat & EMMC_HOST_XFER_COMPLETE) { |
| regs->normal_int_stat |= EMMC_HOST_XFER_COMPLETE; |
| k_event_post(&emmc->irq_event, EMMC_HOST_XFER_COMPLETE); |
| } |
| |
| if (regs->normal_int_stat & EMMC_HOST_DMA_INTR) { |
| regs->normal_int_stat |= EMMC_HOST_DMA_INTR; |
| k_event_post(&emmc->irq_event, EMMC_HOST_DMA_INTR); |
| } |
| |
| if (regs->normal_int_stat & EMMC_HOST_BUF_WR_READY) { |
| regs->normal_int_stat |= EMMC_HOST_BUF_WR_READY; |
| k_event_post(&emmc->irq_event, EMMC_HOST_BUF_WR_READY); |
| } |
| |
| if (regs->normal_int_stat & EMMC_HOST_BUF_RD_READY) { |
| regs->normal_int_stat |= EMMC_HOST_BUF_RD_READY; |
| k_event_post(&emmc->irq_event, EMMC_HOST_BUF_RD_READY); |
| } |
| |
| if (regs->err_int_stat) { |
| LOG_ERR("err int:%x", regs->err_int_stat); |
| k_event_post(&emmc->irq_event, ERR_INTR_STATUS_EVENT(regs->err_int_stat)); |
| if (regs->err_int_stat & EMMC_HOST_DMA_TXFR_ERR) { |
| regs->err_int_stat |= EMMC_HOST_DMA_TXFR_ERR; |
| } else { |
| regs->err_int_stat |= regs->err_int_stat; |
| } |
| } |
| |
| if (regs->normal_int_stat) { |
| k_event_post(&emmc->irq_event, regs->normal_int_stat); |
| regs->normal_int_stat |= regs->normal_int_stat; |
| } |
| |
| if (regs->adma_err_stat) { |
| LOG_ERR("adma err:%x", regs->adma_err_stat); |
| } |
| } |
| |
| static int emmc_init(const struct device *dev) |
| { |
| struct emmc_data *emmc = dev->data; |
| const struct emmc_config *config = dev->config; |
| |
| k_sem_init(&emmc->lock, 1, 1); |
| k_event_init(&emmc->irq_event); |
| |
| #if DT_ANY_INST_ON_BUS_STATUS_OKAY(pcie) |
| if (config->pcie) { |
| struct pcie_bar mbar; |
| |
| if (config->pcie->bdf == PCIE_BDF_NONE) { |
| LOG_ERR("Cannot probe eMMC PCI device: %x", config->pcie->id); |
| return -ENODEV; |
| } |
| |
| if (!pcie_probe_mbar(config->pcie->bdf, 0, &mbar)) { |
| LOG_ERR("eMMC MBAR not found"); |
| return -EINVAL; |
| } |
| |
| pcie_get_mbar(config->pcie->bdf, 0, &mbar); |
| pcie_set_cmd(config->pcie->bdf, PCIE_CONF_CMDSTAT_MEM, true); |
| device_map(DEVICE_MMIO_RAM_PTR(dev), mbar.phys_addr, mbar.size, K_MEM_CACHE_NONE); |
| pcie_set_cmd(config->pcie->bdf, PCIE_CONF_CMDSTAT_MASTER, true); |
| } else |
| #endif |
| { |
| DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); |
| } |
| |
| LOG_DBG("MMC Device MMIO: %p", (void *)(struct emmc_reg *)DEVICE_MMIO_GET(dev)); |
| |
| if (IS_ENABLED(CONFIG_INTEL_EMMC_HOST_INTR)) { |
| config->config_func(dev); |
| } |
| return emmc_reset(dev); |
| } |
| |
| static const struct sdhc_driver_api emmc_api = { |
| .reset = emmc_reset, |
| .request = emmc_request, |
| .set_io = emmc_set_io, |
| .get_card_present = emmc_get_card_present, |
| .execute_tuning = emmc_execute_tuning, |
| .card_busy = emmc_card_busy, |
| .get_host_props = emmc_get_host_props, |
| }; |
| |
| #define EMMC_HOST_IRQ_FLAGS_SENSE0(n) 0 |
| #define EMMC_HOST_IRQ_FLAGS_SENSE1(n) DT_INST_IRQ(n, sense) |
| #define EMMC_HOST_IRQ_FLAGS(n)\ |
| _CONCAT(EMMC_HOST_IRQ_FLAGS_SENSE, DT_INST_IRQ_HAS_CELL(n, sense))(n) |
| |
| /* Not PCI(e) */ |
| #define EMMC_HOST_IRQ_CONFIG_PCIE0(n) \ |
| static void emmc_config_##n(const struct device *port) \ |
| { \ |
| ARG_UNUSED(port); \ |
| IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), emmc_isr, \ |
| DEVICE_DT_INST_GET(n), EMMC_HOST_IRQ_FLAGS(n)); \ |
| irq_enable(DT_INST_IRQN(n)); \ |
| } |
| |
| /* PCI(e) with auto IRQ detection */ |
| #define EMMC_HOST_IRQ_CONFIG_PCIE1(n) \ |
| static void emmc_config_##n(const struct device *port) \ |
| { \ |
| BUILD_ASSERT(DT_INST_IRQN(n) == PCIE_IRQ_DETECT, \ |
| "Only runtime IRQ configuration is supported"); \ |
| BUILD_ASSERT(IS_ENABLED(CONFIG_DYNAMIC_INTERRUPTS), \ |
| "eMMC PCI device needs CONFIG_DYNAMIC_INTERRUPTS"); \ |
| const struct emmc_config *const dev_cfg = port->config; \ |
| unsigned int irq = pcie_alloc_irq(dev_cfg->pcie->bdf); \ |
| \ |
| if (irq == PCIE_CONF_INTR_IRQ_NONE) { \ |
| return; \ |
| } \ |
| pcie_connect_dynamic_irq(dev_cfg->pcie->bdf, irq, DT_INST_IRQ(n, priority), \ |
| (void (*)(const void *))emmc_isr, DEVICE_DT_INST_GET(n), \ |
| EMMC_HOST_IRQ_FLAGS(n)); \ |
| pcie_irq_enable(dev_cfg->pcie->bdf, irq); \ |
| } |
| |
| #define EMMC_HOST_IRQ_CONFIG(n) _CONCAT(EMMC_HOST_IRQ_CONFIG_PCIE, DT_INST_ON_BUS(n, pcie))(n) |
| |
| #define INIT_PCIE0(n) |
| #define INIT_PCIE1(n) DEVICE_PCIE_INST_INIT(n, pcie), |
| #define INIT_PCIE(n) _CONCAT(INIT_PCIE, DT_INST_ON_BUS(n, pcie))(n) |
| |
| #define REG_INIT_PCIE0(n) DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), |
| #define REG_INIT_PCIE1(n) |
| #define REG_INIT(n) _CONCAT(REG_INIT_PCIE, DT_INST_ON_BUS(n, pcie))(n) |
| |
| #define DEFINE_PCIE0(n) |
| #define DEFINE_PCIE1(n) DEVICE_PCIE_INST_DECLARE(n) |
| #define EMMC_HOST_PCIE_DEFINE(n) _CONCAT(DEFINE_PCIE, DT_INST_ON_BUS(n, pcie))(n) |
| |
| #define EMMC_HOST_DEV_CFG(n) \ |
| EMMC_HOST_PCIE_DEFINE(n); \ |
| EMMC_HOST_IRQ_CONFIG(n); \ |
| static const struct emmc_config emmc_config_data_##n = { \ |
| REG_INIT(n) INIT_PCIE(n).config_func = emmc_config_##n, \ |
| .hs200_mode = DT_INST_PROP_OR(n, mmc_hs200_1_8v, 0), \ |
| .hs400_mode = DT_INST_PROP_OR(n, mmc_hs400_1_8v, 0), \ |
| .dw_4bit = DT_INST_ENUM_HAS_VALUE(n, bus_width, 4), \ |
| .dw_8bit = DT_INST_ENUM_HAS_VALUE(n, bus_width, 8), \ |
| .max_bus_freq = DT_INST_PROP_OR(n, max_bus_freq, 40000), \ |
| .min_bus_freq = DT_INST_PROP_OR(n, min_bus_freq, 40000), \ |
| .power_delay_ms = DT_INST_PROP_OR(n, power_delay_ms, 500), \ |
| }; \ |
| \ |
| static struct emmc_data emmc_priv_data_##n; \ |
| \ |
| DEVICE_DT_INST_DEFINE(n, emmc_init, NULL, &emmc_priv_data_##n, &emmc_config_data_##n, \ |
| POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &emmc_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(EMMC_HOST_DEV_CFG) |