imx8 sdhci support. Onhold for now until we have a working ARM-based SDHCI driver.
Change-Id: I4c002660652ebc33cc9cb98fd26e8167273d92d6
diff --git a/system/dev/block/imx-sdhci/imx-sdhci.c b/system/dev/block/imx-sdhci/imx-sdhci.c
new file mode 100644
index 0000000..c73f5ab
--- /dev/null
+++ b/system/dev/block/imx-sdhci/imx-sdhci.c
@@ -0,0 +1,773 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Standard Includes
+#include <assert.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <threads.h>
+
+// DDK Includes
+#include <ddk/binding.h>
+#include <ddk/device.h>
+#include <ddk/debug.h>
+#include <ddk/io-buffer.h>
+#include <ddk/phys-iter.h>
+#include <ddk/protocol/platform-defs.h>
+#include <ddk/protocol/platform-device.h>
+#include <ddk/protocol/platform-bus.h>
+#include <ddk/protocol/sdmmc.h>
+#include <ddk/protocol/sdhci.h>
+#include <hw/sdmmc.h>
+#include <zircon/types.h>
+
+
+// Zircon Includes
+#include <fdio/watcher.h>
+#include <zircon/threads.h>
+#include <zircon/assert.h>
+#include <sync/completion.h>
+#include <pretty/hexdump.h>
+
+#include "imx-sdhci.h"
+
+#define SDHCI_ERROR(fmt, ...) zxlogf(ERROR, "[%s %d]" fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define SDHCI_INFO(fmt, ...) zxlogf(ERROR, "[%s %d]" fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define SDHCI_TRACE(fmt, ...) zxlogf(ERROR, "[%s %d]" fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define SDHCI_FUNC_ENTRY_LOG zxlogf(ERROR, "[%s %d]\n", __func__, __LINE__)
+
+#define SD_FREQ_SETUP_HZ 400000
+
+#define MAX_TUNING_COUNT 40
+
+#define PAGE_MASK (PAGE_SIZE - 1ull)
+
+#define HI32(val) (((val) >> 32) & 0xffffffff)
+#define LO32(val) ((val) & 0xffffffff)
+#define SDMMC_COMMAND(c) ((c) << 24)
+
+typedef struct sdhci_adma64_desc {
+ union {
+ struct {
+ uint8_t valid : 1;
+ uint8_t end : 1;
+ uint8_t intr : 1;
+ uint8_t rsvd0 : 1;
+ uint8_t act1 : 1;
+ uint8_t act2 : 1;
+ uint8_t rsvd1 : 2;
+ uint8_t rsvd2;
+ } __PACKED;
+ uint16_t attr;
+ } __PACKED;
+ uint16_t length;
+ uint64_t address;
+} __PACKED sdhci_adma64_desc_t;
+
+static_assert(sizeof(sdhci_adma64_desc_t) == 12, "unexpected ADMA2 descriptor size");
+
+// 64k max per descriptor
+#define ADMA2_DESC_MAX_LENGTH 0x10000 // 64k
+// for 2M max transfer size for fully discontiguous
+// also see SDMMC_PAGES_COUNT in ddk/protocol/sdmmc.h
+#define DMA_DESC_COUNT 512
+
+typedef struct imx_sdhci_device {
+ platform_device_protocol_t pdev;
+ platform_bus_protocol_t pbus;
+ zx_device_t* zxdev;
+ io_buffer_t mmios;
+ zx_handle_t irq_handle;
+
+ volatile imx_sdhci_regs_t* regs;
+ uint64_t regs_size;
+ zx_handle_t regs_handle;
+ zx_handle_t bti_handle;
+
+ // DMA descriptors
+ io_buffer_t iobuf;
+ sdhci_adma64_desc_t* descs;
+
+ // Held when a command or action is in progress.
+ mtx_t mtx;
+
+ // Current command request
+ sdmmc_req_t* cmd_req;
+ // Current data line request
+ sdmmc_req_t* data_req;
+ // Current block id to transfer (PIO)
+ uint16_t data_blockid;
+ uint16_t reserved;
+ // Set to true if the data stage completed before the command stage
+ bool data_done;
+ // used to signal request complete
+ completion_t req_completion;
+
+ // Controller info
+ sdmmc_host_info_t info;
+
+ // Controller specific quirks
+ uint64_t quirks;
+
+ // Base clock rate
+ uint32_t base_clock;
+
+} imx_sdhci_device_t;
+
+static const uint32_t error_interrupts = (
+ IMX_SDHC_INT_STAT_DMAE |
+ IMX_SDHC_INT_STAT_TNE |
+ IMX_SDHC_INT_STAT_AC12E |
+ IMX_SDHC_INT_STAT_DEBE |
+ IMX_SDHC_INT_STAT_DCE |
+ IMX_SDHC_INT_STAT_DTOE |
+ IMX_SDHC_INT_STAT_CIE |
+ IMX_SDHC_INT_STAT_CEBE |
+ IMX_SDHC_INT_STAT_CCE |
+ IMX_SDHC_INT_STAT_CTOE
+);
+
+static const uint32_t normal_interrupts = (
+ IMX_SDHC_INT_STAT_BRR |
+ IMX_SDHC_INT_STAT_BWR |
+ IMX_SDHC_INT_STAT_DINT |
+ IMX_SDHC_INT_STAT_BGE |
+ IMX_SDHC_INT_STAT_TC |
+ IMX_SDHC_INT_STAT_CC
+);
+
+static bool imx_sdmmc_cmd_rsp_busy(uint32_t cmd) {
+ uint32_t resp = cmd & SDMMC_RESP_MASK;
+ return resp == SDMMC_RESP_LEN_48B;
+}
+
+static bool imx_sdmmc_has_data(uint32_t resp_type) {
+ return resp_type & SDMMC_RESP_DATA_PRESENT;
+}
+
+
+static bool imx_sdmmc_supports_adma2_64bit(imx_sdhci_device_t* dev) {
+ return (0); // TODO: iMX8 doesn't support 64bit ADMA2
+}
+
+static zx_status_t imx_sdhci_wait_for_reset(imx_sdhci_device_t* dev,
+ const uint32_t mask, zx_time_t timeout) {
+ zx_time_t deadline = zx_clock_get(ZX_CLOCK_MONOTONIC) + timeout;
+ while (true) {
+ if (((dev->regs->sys_ctrl) & mask) == 0) {
+ break;
+ }
+ if (zx_clock_get(ZX_CLOCK_MONOTONIC) > deadline) {
+ SDHCI_ERROR("time out while waiting for reset\n");
+ return ZX_ERR_TIMED_OUT;
+ }
+ }
+ return ZX_OK;
+}
+
+static void imx_sdhci_complete_request_locked(imx_sdhci_device_t* dev, sdmmc_req_t* req,
+ zx_status_t status) {
+ SDHCI_TRACE("complete cmd 0x%08x status %d\n", req->cmd, status);
+
+ // Disable interrupts when no pending transfer
+ dev->regs->int_signal_en = 0;
+
+ dev->cmd_req = NULL;
+ dev->data_req = NULL;
+ dev->data_blockid = 0;
+ dev->data_done = false;
+
+ req->status = status;
+ completion_signal(&dev->req_completion);
+}
+
+static void imx_sdhci_cmd_stage_complete_locked(imx_sdhci_device_t* dev) {
+ SDHCI_TRACE("Got CC interrupt\n");
+
+ if (!dev->cmd_req) {
+ SDHCI_TRACE("Spurious CC interupt\n");
+ return;
+ }
+
+ sdmmc_req_t* req = dev->cmd_req;
+ volatile struct imx_sdhci_regs* regs = dev->regs;
+ uint32_t cmd = SDMMC_COMMAND(req->cmd) | req->resp_type;
+
+ // Read the response data
+ if (cmd & SDMMC_RESP_LEN_136) {
+ // TODO: need any quirks??
+ req->response[0] = (regs->cmd_rsp0 << 8);
+ req->response[1] = (regs->cmd_rsp1 << 8) | ((regs->cmd_rsp0 >> 24) & 0xFF);
+ req->response[2] = (regs->cmd_rsp2 << 8) | ((regs->cmd_rsp1 >> 24) & 0xFF);
+ req->response[3] = (regs->cmd_rsp3 << 8) | ((regs->cmd_rsp2 >> 24) & 0xFF);
+
+ } else if (cmd & (SDMMC_RESP_LEN_48 | SDMMC_RESP_LEN_48B)) {
+ req->response[0] = regs->cmd_rsp0;
+ req->response[1] = regs->cmd_rsp1;
+ }
+
+ // We're done if the command has no data stage or if the data stage completed early
+ if (!dev->data_req || dev->data_done) {
+ imx_sdhci_complete_request_locked(dev, dev->cmd_req, ZX_OK);
+ } else {
+ dev->cmd_req = NULL;
+ }
+}
+
+static void imx_sdhci_data_stage_read_ready_locked(imx_sdhci_device_t* dev) {
+ SDHCI_TRACE("Got BRR Interrupts\n");
+
+ if (!dev->data_req || !imx_sdmmc_has_data(dev->data_req->resp_type)) {
+ SDHCI_TRACE("Spurious BRR Interrupt\n");
+ return;
+ }
+
+ sdmmc_req_t* req = dev->data_req;
+
+ if (dev->data_req->cmd == MMC_SEND_TUNING_BLOCK) {
+ // tuing commnad is done here
+ imx_sdhci_complete_request_locked(dev, dev->data_req, ZX_OK);
+ } else {
+ // Sequentially read each block
+ for (size_t byteid = 0; byteid < req->blocksize; byteid += 4) {
+ const size_t offset = dev->data_blockid * req->blocksize + byteid;
+ uint32_t* wrd = req->virt + offset;
+ *wrd = dev->regs->data_buff_acc_port; //TODO: Can't read this if DMA is enabled!
+ }
+ dev->data_blockid += 1;
+ }
+}
+
+static void imx_sdhci_data_stage_write_ready_locked(imx_sdhci_device_t* dev) {
+ SDHCI_TRACE("Got BWR Interrupt\n");
+
+ if (!dev->data_req || !imx_sdmmc_has_data(dev->data_req->resp_type)) {
+ SDHCI_TRACE("Spurious BWR Interrupt\n");
+ return;
+ }
+
+ sdmmc_req_t* req = dev->data_req;
+
+ // Sequentially write each block
+ for (size_t byteid = 0; byteid < req->blocksize; byteid += 4) {
+ const size_t offset = dev->data_blockid * req->blocksize + byteid;
+ uint32_t* wrd = req->virt + offset;
+ dev->regs->data_buff_acc_port = *wrd; //TODO: Can't write if DMA is enabled
+ }
+ dev->data_blockid += 1;
+}
+
+static void imx_sdhci_transfer_complete_locked(imx_sdhci_device_t* dev) {
+ SDHCI_TRACE("Got TC Interrupt\n");
+ if (!dev->data_req) {
+ SDHCI_TRACE("Spurious TC Interrupt\n");
+ return;
+ }
+
+ if (dev->cmd_req) {
+ dev->data_done = true;
+ } else {
+ imx_sdhci_complete_request_locked(dev, dev->data_req, ZX_OK);
+ }
+}
+
+static void imx_sdhci_error_recovery_locked(imx_sdhci_device_t* dev) {
+ // Reset internal state machines
+ dev->regs->sys_ctrl |= IMX_SDHC_SYS_CTRL_RSTC;
+ imx_sdhci_wait_for_reset(dev, IMX_SDHC_SYS_CTRL_RSTC, ZX_SEC(1));
+ dev->regs->sys_ctrl |= IMX_SDHC_SYS_CTRL_RSTD;
+ imx_sdhci_wait_for_reset(dev, IMX_SDHC_SYS_CTRL_RSTD, ZX_SEC(1));
+
+ // Complete any pending txn with error status
+ if (dev->cmd_req != NULL) {
+ imx_sdhci_complete_request_locked(dev, dev->cmd_req, ZX_ERR_IO);
+ } else if (dev->data_req != NULL) {
+ imx_sdhci_complete_request_locked(dev, dev->data_req, ZX_ERR_IO);
+ }
+}
+
+static uint32_t get_clock_divider(const uint32_t base_clock, const uint32_t target_rate) {
+ uint32_t pre_div = 1;
+ uint32_t div = 1;
+
+ if (target_rate >= base_clock) {
+ // A clock divider of 0 means "don't divide the clock"
+ // If the base clock is already slow enough to use as the SD clock then
+ // we don't need to divide it any further.
+ return 0;
+ }
+
+ SDHCI_TRACE("base %d, pre_div %d, div = %d, target_rate %d\n",
+ base_clock, pre_div, div, target_rate);
+ while (base_clock / pre_div / 16 > target_rate && pre_div < 256) {
+ SDHCI_TRACE("base %d, pre_div %d, div = %d, target_rate %d\n",
+ base_clock, pre_div, div, target_rate);
+ pre_div *= 2;
+ }
+
+ while (base_clock / pre_div / div > target_rate && div < 16) {
+ SDHCI_TRACE("base %d, pre_div %d, div = %d, target_rate %d\n",
+ base_clock, pre_div, div, target_rate);
+ div++;
+ }
+
+ SDHCI_TRACE("base %d, pre_div %d, div = %d, target_rate %d\n",
+ base_clock, pre_div, div, target_rate);
+
+ pre_div >>= 1;
+ div -= 1;
+
+ return (((pre_div & 0xFF) << 16)| (div & 0xF));
+}
+
+
+static int imx_sdhci_irq_thread(void *args) {
+ zx_status_t wait_res;
+ imx_sdhci_device_t* dev = (imx_sdhci_device_t*)args;
+ volatile struct imx_sdhci_regs* regs = dev->regs;
+ zx_handle_t irq_handle = dev->irq_handle;
+
+ while(true) {
+ uint64_t slots;
+ wait_res = zx_interrupt_wait(irq_handle, &slots);
+ if (wait_res != ZX_OK) {
+ SDHCI_ERROR("Interrupt_wait failed with retcode %d\n", wait_res);
+ break;
+ }
+
+ const uint32_t irq = regs->int_status;
+ SDHCI_TRACE("got irq 0x%08x 0x%08x en 0x%08x sig 0x%08x\n", regs->int_status, irq,
+ regs->int_status_en, regs->int_signal_en);
+
+ // Acknowledge the IRQs that we stashed.
+ regs->int_status = irq;
+
+ mtx_lock(&dev->mtx);
+ if (irq & IMX_SDHC_INT_STAT_CC) {
+ imx_sdhci_cmd_stage_complete_locked(dev);
+ }
+ if (irq & IMX_SDHC_INT_STAT_BRR) {
+ imx_sdhci_data_stage_read_ready_locked(dev);
+ }
+ if (irq & IMX_SDHC_INT_STAT_BWR) {
+ imx_sdhci_data_stage_write_ready_locked(dev);
+ }
+ if (irq & IMX_SDHC_INT_STAT_TC) {
+ imx_sdhci_transfer_complete_locked(dev);
+ }
+ if (irq & error_interrupts) {
+ if (irq & IMX_SDHC_INT_STAT_DMAE) {
+ SDHCI_TRACE("ADMA error 0x%x ADMAADDR0 0x%x\n",
+ regs->adma_err_status, regs->adma_sys_addr);
+ }
+ imx_sdhci_error_recovery_locked(dev);
+ }
+ mtx_unlock(&dev->mtx);
+ }
+ return ZX_OK;
+}
+
+static zx_status_t imx_sdhci_build_dma_desc(imx_sdhci_device_t* dev, sdmmc_req_t* req) {
+ SDHCI_FUNC_ENTRY_LOG;
+ return ZX_OK;
+}
+
+static zx_status_t imx_sdhci_start_req_locked(imx_sdhci_device_t* dev, sdmmc_req_t* req) {
+ volatile struct imx_sdhci_regs* regs = dev->regs;
+ const uint32_t arg = req->arg;
+ const uint16_t blkcnt = req->blockcount;
+ const uint16_t blksiz = req->blocksize;
+ uint32_t cmd = SDMMC_COMMAND(req->cmd) | req->resp_type;
+ bool has_data = imx_sdmmc_has_data(req->resp_type);
+
+ if (req->use_dma && !imx_sdmmc_supports_adma2_64bit(dev)) {
+ SDHCI_INFO("Host does not support 64BIT DMA\n");
+ // iMX8 support ADMA2 32bit!! why not use that!
+ return ZX_ERR_NOT_SUPPORTED;
+ }
+
+ if (req->use_dma) {
+ SDHCI_INFO("we don't support dma yet\t");
+ return ZX_ERR_NOT_SUPPORTED;
+ }
+
+ SDHCI_TRACE("start_req cmd=0x%08x (data %d dma %d bsy %d) blkcnt %u blksiz %u\n",
+ cmd, has_data, req->use_dma, imx_sdmmc_cmd_rsp_busy(cmd), blkcnt, blksiz);
+
+ // Every command requires that the Commnad Inhibit bit is unset
+ uint32_t inhibit_mask = IMX_SDHC_PRES_STATE_CIHB;
+
+ // Busy type commands must also wait for the DATA Inhibit to be 0 unless it's an abort
+ // command which can be issued with the data lines active
+ if (((cmd & SDMMC_RESP_LEN_48B) == SDMMC_RESP_LEN_48B) &&
+ ((cmd & SDMMC_CMD_TYPE_ABORT) == 0)) {
+ inhibit_mask |= IMX_SDHC_PRES_STATE_CDIHB;
+ }
+
+ // Wait for the inhibit masks from above to become 0 before issueing the command
+ while(regs->pres_state & inhibit_mask) {
+ zx_nanosleep(zx_deadline_after(ZX_MSEC(1)));
+ }
+
+ // zx_status_t status = ZX_OK;
+ if (has_data) {
+ if (cmd & SDMMC_CMD_MULTI_BLK) {
+ cmd |= SDMMC_CMD_AUTO12;
+ }
+ }
+
+ regs->blk_att = (blksiz | (blkcnt << 16));
+
+ regs->cmd_arg = arg;
+
+ // Clear any pending interrupts before starting the transaction
+ regs->int_status = regs->int_signal_en;
+
+ // Unmask and enable interrupts
+ regs->int_signal_en = error_interrupts | normal_interrupts;
+ regs->int_status_en = error_interrupts | normal_interrupts;
+
+ // Start command
+ regs->cmd_xfr_typ = cmd;
+
+ dev->cmd_req = req;
+
+ if (has_data || imx_sdmmc_cmd_rsp_busy(cmd)) {
+ dev->data_req = req;
+ } else {
+ dev->data_req = NULL;
+ }
+ dev->data_blockid = 0;
+ dev->data_done = false;
+ return ZX_OK;
+// err:
+// return status;
+}
+
+static zx_status_t imx_sdhci_finish_req(imx_sdhci_device_t* dev, sdmmc_req_t* req) {
+ zx_status_t status = ZX_OK;
+
+ if (req->use_dma && req->pmt != ZX_HANDLE_INVALID) {
+ status = zx_pmt_unpin(req->pmt);
+ if (status != ZX_OK) {
+ SDHCI_ERROR("error %d in pmt_unpin\n", status);
+ }
+ req->pmt = ZX_HANDLE_INVALID;
+ }
+ return status;
+}
+
+/* SDMMC PROTOCOL Implementations: host_info */
+static zx_status_t imx_sdhci_host_info(void* ctx, sdmmc_host_info_t* info) {
+ SDHCI_FUNC_ENTRY_LOG;
+ return ZX_OK;
+}
+
+/* SDMMC PROTOCOL Implementations: set_signal_voltage */
+static zx_status_t imx_sdhci_set_signal_voltage(void* ctx, sdmmc_voltage_t voltage) {
+ SDHCI_FUNC_ENTRY_LOG;
+ return ZX_ERR_NOT_SUPPORTED;
+}
+
+/* SDMMC PROTOCOL Implementations: set_bus_width */
+static zx_status_t imx_sdhci_set_bus_width(void* ctx, uint32_t bus_width) {
+ SDHCI_FUNC_ENTRY_LOG;
+ if (bus_width >= SDMMC_BUS_WIDTH_MAX) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+ zx_status_t status = ZX_OK;
+ imx_sdhci_device_t* dev = ctx;
+
+ mtx_lock(&dev->mtx);
+
+ if ((bus_width == SDMMC_BUS_WIDTH_8) && !(dev->info.caps & SDMMC_HOST_CAP_BUS_WIDTH_8)) {
+ SDHCI_TRACE("8-bit bus width not supported\n");
+ status = ZX_ERR_NOT_SUPPORTED;
+ goto unlock;
+ }
+
+ switch (bus_width) {
+ case SDMMC_BUS_WIDTH_1:
+ dev->regs->prot_ctrl &= ~IMX_SDHC_PROT_CTRL_DTW_MASK;
+ dev->regs->prot_ctrl |= IMX_SDHC_PROT_CTRL_DTW_1;
+ break;
+ case SDMMC_BUS_WIDTH_4:
+ dev->regs->prot_ctrl &= ~IMX_SDHC_PROT_CTRL_DTW_MASK;
+ dev->regs->prot_ctrl |= IMX_SDHC_PROT_CTRL_DTW_4;
+ break;
+ case SDMMC_BUS_WIDTH_8:
+ dev->regs->prot_ctrl &= ~IMX_SDHC_PROT_CTRL_DTW_MASK;
+ dev->regs->prot_ctrl |= IMX_SDHC_PROT_CTRL_DTW_8;
+ break;
+ default:
+ break;
+ }
+
+ SDHCI_TRACE("set bus width to %d\n", bus_width);
+
+unlock:
+ mtx_unlock(&dev->mtx);
+ return status;
+}
+
+/* SDMMC PROTOCOL Implementations: set_bus_freq */
+static zx_status_t imx_sdhci_set_bus_freq(void* ctx, uint32_t bus_freq) {
+ SDHCI_FUNC_ENTRY_LOG;
+ zx_status_t status = ZX_OK;
+ imx_sdhci_device_t* dev = ctx;
+
+ mtx_lock(&dev->mtx);
+
+ const uint32_t divider = get_clock_divider(dev->base_clock, bus_freq);
+ const uint8_t pre_div = (divider >> 16) & 0xFF;
+ const uint8_t div = (divider & 0xF);
+ SDHCI_TRACE("divider %d, pre_div %d, div = %d\n",
+ divider, pre_div, div);
+
+ volatile struct imx_sdhci_regs* regs = dev->regs;
+
+ uint32_t iterations = 0;
+ while (regs->pres_state & (IMX_SDHC_PRES_STATE_CIHB | IMX_SDHC_PRES_STATE_CDIHB)) {
+ if (++iterations > 1000) {
+ status = ZX_ERR_TIMED_OUT;
+ goto unlock;
+ }
+ zx_nanosleep(zx_deadline_after(ZX_MSEC(1)));
+ }
+
+ regs->vend_spec &= ~(IMX_SDHC_VEND_SPEC_FRC_SDCLK_ON);
+
+ // turn off clocks
+ regs->sys_ctrl &= ~(IMX_SDHC_SYS_CTRL_CLOCK_PEREN |
+ IMX_SDHC_SYS_CTRL_CLOCK_HCKEN |
+ IMX_SDHC_SYS_CTRL_CLOCK_IPGEN |
+ IMX_SDHC_SYS_CTRL_CLOCK_MASK);
+
+ regs->sys_ctrl |= (IMX_SDHC_SYS_CTRL_CLOCK_PEREN |
+ IMX_SDHC_SYS_CTRL_CLOCK_HCKEN |
+ IMX_SDHC_SYS_CTRL_CLOCK_IPGEN |
+ (pre_div << IMX_SDHC_SYS_CTRL_PREDIV_SHIFT) |
+ (div << IMX_SDHC_SYS_CTRL_DIVIDER_SHIFT));
+
+ regs->vend_spec |= (IMX_SDHC_VEND_SPEC_FRC_SDCLK_ON);
+ zx_nanosleep(zx_deadline_after(ZX_MSEC(2)));
+
+ SDHCI_TRACE("desired freq = %d, actual = %d, (%d, %d. %d)\n",
+ bus_freq, dev->base_clock / (pre_div<<1) / (div+1), dev->base_clock, pre_div, div);
+
+unlock:
+ mtx_unlock(&dev->mtx);
+ return status;
+}
+
+/* SDMMC PROTOCOL Implementations: set_timing */
+static zx_status_t imx_sdhci_set_timing(void* ctx, sdmmc_timing_t timing) {
+ SDHCI_FUNC_ENTRY_LOG;
+ return ZX_OK;
+}
+
+/* SDMMC PROTOCOL Implementations: hw_reset */
+static void imx_sdhci_hw_reset(void* ctx) {
+ SDHCI_FUNC_ENTRY_LOG;
+}
+
+/* SDMMC PROTOCOL Implementations: perform_tuning */
+static zx_status_t imx_sdhci_perform_tuning(void* ctx) {
+ SDHCI_FUNC_ENTRY_LOG;
+ return ZX_OK;
+}
+
+/* SDMMC PROTOCOL Implementations: request */
+static zx_status_t imx_sdhci_request(void* ctx, sdmmc_req_t* req) {
+ SDHCI_FUNC_ENTRY_LOG;
+ zx_status_t status = ZX_OK;
+ imx_sdhci_device_t* dev = ctx;
+
+ mtx_lock(&dev->mtx);
+
+ // one command at a time
+ if ((dev->cmd_req != NULL) || (dev->data_req != NULL)) {
+ status = ZX_ERR_SHOULD_WAIT;
+ goto unlock_out;
+ }
+
+ status = imx_sdhci_start_req_locked(dev, req);
+ if (status != ZX_OK) {
+ goto unlock_out;
+ }
+
+ mtx_unlock(&dev->mtx);
+
+ completion_wait(&dev->req_completion, ZX_TIME_INFINITE);
+
+ imx_sdhci_finish_req(dev, req);
+
+ completion_reset(&dev->req_completion);
+
+ return req->status;
+
+unlock_out:
+ mtx_unlock(&dev->mtx);
+ imx_sdhci_finish_req(dev, req);
+ return status;
+}
+
+
+static sdmmc_protocol_ops_t sdmmc_proto = {
+ .host_info = imx_sdhci_host_info,
+ .set_signal_voltage = imx_sdhci_set_signal_voltage,
+ .set_bus_width = imx_sdhci_set_bus_width,
+ .set_bus_freq = imx_sdhci_set_bus_freq,
+ .set_timing = imx_sdhci_set_timing,
+ .hw_reset = imx_sdhci_hw_reset,
+ .perform_tuning = imx_sdhci_perform_tuning,
+ .request = imx_sdhci_request,
+};
+
+static void imx_sdhci_unbind(void* ctx) {
+ imx_sdhci_device_t* dev = ctx;
+ device_remove(dev->zxdev);
+}
+
+static void imx_sdhci_release(void* ctx) {
+ imx_sdhci_device_t* dev = ctx;
+ if (dev->regs != NULL) {
+ zx_handle_close(dev->regs_handle);
+ }
+ zx_handle_close(dev->bti_handle);
+ free(dev);
+}
+
+static zx_protocol_device_t imx_sdhci_device_proto = {
+ .version = DEVICE_OPS_VERSION,
+ .unbind = imx_sdhci_unbind,
+ .release = imx_sdhci_release,
+
+};
+
+
+static zx_status_t imx_sdhci_bind(void* ctx, zx_device_t* parent) {
+ zx_status_t status;
+
+ imx_sdhci_device_t* dev = calloc(1, sizeof(imx_sdhci_device_t));
+ if (!dev) {
+ return ZX_ERR_NO_MEMORY;
+ }
+
+ status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &dev->pdev);
+ if (status != ZX_OK) {
+ SDHCI_ERROR("ZX_PROTOCOL_PLATFORM_DEV not available %d \n", status);
+ goto fail;
+ }
+
+ status = pdev_map_mmio_buffer(&dev->pdev, 0, ZX_CACHE_POLICY_UNCACHED_DEVICE,
+ &dev->mmios);
+ if (status != ZX_OK) {
+ SDHCI_ERROR("pdev_map_mmio_buffer failed %d\n", status);
+ goto fail;
+ }
+
+ // hook up mmio to dev->regs
+ dev->regs = io_buffer_virt(&dev->mmios);
+
+ status = pdev_get_bti(&dev->pdev, 0, &dev->bti_handle);
+ if (status != ZX_OK) {
+ SDHCI_ERROR("Could not get BTI handle %d\n", status);
+ goto fail;
+ }
+
+ status = pdev_map_interrupt(&dev->pdev, 0, &dev->irq_handle);
+ if (status != ZX_OK) {
+ SDHCI_ERROR("pdev_map_interrupt failed %d\n", status);
+ goto fail;
+ }
+
+ thrd_t irq_thread;
+ if (thrd_create_with_name(&irq_thread, imx_sdhci_irq_thread,
+ dev, "imx_sdhci_irq_thread") != thrd_success) {
+ SDHCI_ERROR("Failed to create irq thread\n");
+ }
+ thrd_detach(irq_thread);
+
+ dev->base_clock = 200000000; // TODO: Better way of doing this obviously
+
+ uint32_t caps0 = dev->regs->host_ctrl_cap;
+ SDHCI_ERROR("caps = 0x%x\n", caps0);
+
+ dev->info.caps |= SDMMC_HOST_CAP_BUS_WIDTH_8;
+ if (caps0 & SDHCI_CORECFG_3P3_VOLT_SUPPORT) {
+ dev->info.caps |= SDMMC_HOST_CAP_VOLTAGE_330;
+ }
+
+
+ // Reset host controller
+ dev->regs->sys_ctrl |= IMX_SDHC_SYS_CTRL_RSTA;
+ if (imx_sdhci_wait_for_reset(dev, IMX_SDHC_SYS_CTRL_RSTA, ZX_SEC(1)) != ZX_OK) {
+ SDHCI_ERROR("Did not recover from reset 0x%x\n", dev->regs->sys_ctrl);
+ goto fail;
+ }
+
+ // RSTA does not reset MMC_BOOT register. so do it manually
+ dev->regs->mmc_boot = 0;
+
+ // Reset MIX_CTRL and CLK_TUNE_CTRL regs to 0 as well
+ dev->regs->mix_ctrl = 0;
+ dev->regs->clk_tune_ctrl_status = 0;
+
+ // disable DLL_CTRL delay lie
+ dev->regs->dll_ctrl = 0;
+
+ // dev->regs->prot_ctrl = 0x00000020
+
+ // no dma for now
+ dev->info.max_transfer_size = 0;
+
+ // enable clocks
+ imx_sdhci_set_bus_freq(dev, SD_FREQ_SETUP_HZ);
+
+ dev->regs->sys_ctrl |= (0xe << 16);
+
+ zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
+
+ // Disable all interrupts
+ dev->regs->int_signal_en = 0;
+ dev->regs->int_status = 0xffffffff;
+
+
+ device_add_args_t args = {
+ .version = DEVICE_ADD_ARGS_VERSION,
+ .name = "imx-sdhci",
+ .ctx = dev,
+ .ops = &imx_sdhci_device_proto,
+ .proto_id = ZX_PROTOCOL_SDMMC,
+ .proto_ops = &sdmmc_proto,
+ };
+
+ status = device_add(parent, &args, &dev->zxdev);
+ if (status != ZX_OK) {
+ SDHCI_ERROR("device_add failed %d\n", status);
+ goto fail;
+ }
+
+ return ZX_OK;
+
+fail:
+ imx_sdhci_release(dev);
+ return status;
+}
+
+static zx_driver_ops_t imx_sdhci_driver_ops = {
+ .version = DRIVER_OPS_VERSION,
+ .bind = imx_sdhci_bind,
+};
+
+ZIRCON_DRIVER_BEGIN(imx_sdhci, imx_sdhci_driver_ops, "zircon", "0.1", 4)
+ BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_DEV),
+ BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_NXP),
+ BI_ABORT_IF(NE, BIND_PLATFORM_DEV_DID, PDEV_DID_IMX_SDHCI),
+ BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_IMX8MEVK),
+ZIRCON_DRIVER_END(imx_sdhci)
diff --git a/system/dev/block/imx-sdhci/imx-sdhci.h b/system/dev/block/imx-sdhci/imx-sdhci.h
new file mode 100644
index 0000000..d6703a7
--- /dev/null
+++ b/system/dev/block/imx-sdhci/imx-sdhci.h
@@ -0,0 +1,203 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+typedef struct imx_sdhci_regs {
+ uint32_t ds_addr; // 0x00
+ uint32_t blk_att; // 0x04
+ uint32_t cmd_arg; // 0x08
+ uint32_t cmd_xfr_typ; // 0x0C
+ uint32_t cmd_rsp0; // 0x10
+ uint32_t cmd_rsp1; // 0x14
+ uint32_t cmd_rsp2; // 0x18
+ uint32_t cmd_rsp3; // 0x1C
+ uint32_t data_buff_acc_port; // 0x20
+ uint32_t pres_state; // 0x24
+ uint32_t prot_ctrl; // 0x28
+ uint32_t sys_ctrl; // 0x2C
+ uint32_t int_status; // 0x30
+ uint32_t int_status_en; // 0x34
+ uint32_t int_signal_en; // 0x38
+ uint32_t autocmd12_err_status; // 0x3C
+ uint32_t host_ctrl_cap; // 0x40
+ uint32_t wtmk_lvl; // 0x44
+ uint32_t mix_ctrl; // 0x48
+ uint8_t rsv1[4]; // xxxxxxxx
+ uint32_t force_event; // 0x50
+ uint32_t adma_err_status; // 0x54
+ uint32_t adma_sys_addr; // 0x58
+ uint8_t rsv2[4]; // xxxxxxxx
+ uint32_t dll_ctrl; // 0x60
+ uint32_t dll_status; // 0x64
+ uint32_t clk_tune_ctrl_status; // 0x68
+ uint8_t rsv3[4]; // xxxxxxxx
+ uint32_t strobe_dll_ctrl; // 0x70
+ uint32_t strobe_dll_status; // 0x74
+ uint8_t rsv4[72]; // xxxxxxxx
+ uint32_t vend_spec; // 0xC0
+ uint32_t mmc_boot; // 0xC4
+ uint32_t vend_spec2; // 0xC8
+ uint32_t tuning_ctrl; // 0xCC
+} __PACKED imx_sdhci_regs_t;
+
+
+#define IMX_SDHC_DS_ADDR(x) (x << 2)
+
+#define IMX_SDHC_BLK_ATT_BLKSIZE(x) ( x & 0x1FFF)
+#define IMX_SDHC_BLK_ATT_BLKCNT(x) ((x & 0xFFFF) << 16)
+
+
+#define IMX_SDHC_CMD_XFER_TYPE_CMDINX(x) ((x & 0x3f) << 24)
+#define IMX_SDHC_CMD_XFER_TYPE_CMDTYP_ABORT (3 << 22)
+#define IMX_SDHC_CMD_XFER_TYPE_CMDTYP_RESUME (2 << 22)
+#define IMX_SDHC_CMD_XFER_TYPE_CMDTYP_SUSPEND (1 << 22)
+#define IMX_SDHC_CMD_XFER_TYPE_CMDTYP_NORM (0 << 22)
+#define IMX_SDHC_CMD_XFER_TYPE_DPSEL (1 << 21)
+#define IMX_SDHC_CMD_XFER_TYPE_CICEN (1 << 20)
+#define IMX_SDHC_CMD_XFER_TYPE_CCCEN (1 << 19)
+#define IMX_SDHC_CMD_XFER_TYPE_RSP_TYPE_48_BSY (3 << 16)
+#define IMX_SDHC_CMD_XFER_TYPE_RSP_TYPE_48 (2 << 16)
+#define IMX_SDHC_CMD_XFER_TYPE_RSP_TYPE_136 (1 << 16)
+#define IMX_SDHC_CMD_XFER_TYPE_RSP_TYPE_NO_RESP (0 << 16)
+
+#define IMX_SDHC_PRES_STATE_DLSL(x) (x << 24)
+#define IMX_SDHC_PRES_STATE_CLSL (1 << 23)
+#define IMX_SDHC_PRES_STATE_WPSPL (1 << 19)
+#define IMX_SDHC_PRES_STATE_CDPL (1 << 18)
+#define IMX_SDHC_PRES_STATE_CINST (1 << 16)
+#define IMX_SDHC_PRES_STATE_TSCD (1 << 15)
+#define IMX_SDHC_PRES_STATE_RTR (1 << 12)
+#define IMX_SDHC_PRES_STATE_BREN (1 << 11)
+#define IMX_SDHC_PRES_STATE_BWEN (1 << 10)
+#define IMX_SDHC_PRES_STATE_RTA (1 << 9)
+#define IMX_SDHC_PRES_STATE_WTA (1 << 8)
+#define IMX_SDHC_PRES_STATE_SDOFF (1 << 7)
+#define IMX_SDHC_PRES_STATE_PEROFF (1 << 6)
+#define IMX_SDHC_PRES_STATE_HCKOFF (1 << 5)
+#define IMX_SDHC_PRES_STATE_IPGOFF (1 << 4)
+#define IMX_SDHC_PRES_STATE_SDSTB (1 << 3)
+#define IMX_SDHC_PRES_STATE_DLA (1 << 2)
+#define IMX_SDHC_PRES_STATE_CDIHB (1 << 1)
+#define IMX_SDHC_PRES_STATE_CIHB (1 << 0)
+
+
+#define IMX_SDHC_PROT_CTRL_DMASEL_MASK (3 << 8)
+#define IMX_SDHC_PROT_CTRL_DMASEL_ADMA2 (2 << 8)
+#define IMX_SDHC_PROT_CTRL_DMASEL_ADMA1 (1 << 8)
+#define IMX_SDHC_PROT_CTRL_DMASEL_NODMA (0 << 8)
+#define IMX_SDHC_PROT_CTRL_CDSS (1 << 7)
+#define IMX_SDHC_PROT_CTRL_CDTL (1 << 6)
+#define IMX_SDHC_PROT_CTRL_INIT (1 << 5)
+#define IMX_SDHC_PROT_CTRL_DTW_MASK (3 << 1)
+#define IMX_SDHC_PROT_CTRL_DTW_8 (2 << 1)
+#define IMX_SDHC_PROT_CTRL_DTW_4 (1 << 1)
+#define IMX_SDHC_PROT_CTRL_DTW_1 (0 << 1)
+
+#define IMX_SDHC_SYS_CTRL_RSTT (1 << 28)
+#define IMX_SDHC_SYS_CTRL_INTA (1 << 27)
+#define IMX_SDHC_SYS_CTRL_RSTD (1 << 26)
+#define IMX_SDHC_SYS_CTRL_RSTC (1 << 25)
+#define IMX_SDHC_SYS_CTRL_RSTA (1 << 24)
+#define IMX_SDHC_SYS_CTRL_DTOCV(x) ((x & 0xF) << 16)
+#define IMX_SDHC_SYS_CTRL_SDCLKFS(x) ((x & 0xFF) << 8)
+#define IMX_SDHC_SYS_CTRL_DVS(x) ((x & 0xF) << 4)
+
+// Undocumented sysctl bits (clock related)
+#define IMX_SDHC_SYS_CTRL_CLOCK_MASK (0x0000fff0)
+#define IMX_SDHC_SYS_CTRL_PREDIV_SHIFT (8)
+#define IMX_SDHC_SYS_CTRL_DIVIDER_SHIFT (4)
+#define IMX_SDHC_SYS_CTRL_CLOCK_PEREN (1 << 2)
+#define IMX_SDHC_SYS_CTRL_CLOCK_HCKEN (1 << 1)
+#define IMX_SDHC_SYS_CTRL_CLOCK_IPGEN (1 << 0)
+
+#define IMX_SDHC_INT_STAT_DMAE (1 << 28)
+#define IMX_SDHC_INT_STAT_TNE (1 << 26)
+#define IMX_SDHC_INT_STAT_AC12E (1 << 24)
+#define IMX_SDHC_INT_STAT_DEBE (1 << 22)
+#define IMX_SDHC_INT_STAT_DCE (1 << 21)
+#define IMX_SDHC_INT_STAT_DTOE (1 << 20)
+#define IMX_SDHC_INT_STAT_CIE (1 << 19)
+#define IMX_SDHC_INT_STAT_CEBE (1 << 18)
+#define IMX_SDHC_INT_STAT_CCE (1 << 17)
+#define IMX_SDHC_INT_STAT_CTOE (1 << 16)
+#define IMX_SDHC_INT_STAT_TP (1 << 14)
+#define IMX_SDHC_INT_STAT_RTE (1 << 12)
+#define IMX_SDHC_INT_STAT_CINT (1 << 8)
+#define IMX_SDHC_INT_STAT_CRM (1 << 7)
+#define IMX_SDHC_INT_STAT_CINS (1 << 6)
+#define IMX_SDHC_INT_STAT_BRR (1 << 5)
+#define IMX_SDHC_INT_STAT_BWR (1 << 4)
+#define IMX_SDHC_INT_STAT_DINT (1 << 3)
+#define IMX_SDHC_INT_STAT_BGE (1 << 2)
+#define IMX_SDHC_INT_STAT_TC (1 << 1)
+#define IMX_SDHC_INT_STAT_CC (1 << 0)
+
+#define IMX_SDHC_INT_EN_DMAEN (1 << 28)
+#define IMX_SDHC_INT_EN_TNE (1 << 26)
+#define IMX_SDHC_INT_EN_AC12E (1 << 24)
+#define IMX_SDHC_INT_EN_DEBE (1 << 22)
+#define IMX_SDHC_INT_EN_DCE (1 << 21)
+#define IMX_SDHC_INT_EN_DTOE (1 << 20)
+#define IMX_SDHC_INT_EN_CIE (1 << 19)
+#define IMX_SDHC_INT_EN_CEBE (1 << 18)
+#define IMX_SDHC_INT_EN_CCE (1 << 17)
+#define IMX_SDHC_INT_EN_CTOE (1 << 16)
+#define IMX_SDHC_INT_EN_TP (1 << 14)
+#define IMX_SDHC_INT_EN_RTE (1 << 12)
+#define IMX_SDHC_INT_EN_CINT (1 << 8)
+#define IMX_SDHC_INT_EN_CRM (1 << 7)
+#define IMX_SDHC_INT_EN_CINS (1 << 6)
+#define IMX_SDHC_INT_EN_BRR (1 << 5)
+#define IMX_SDHC_INT_EN_BWR (1 << 4)
+#define IMX_SDHC_INT_EN_DINT (1 << 3)
+#define IMX_SDHC_INT_EN_BGE (1 << 2)
+#define IMX_SDHC_INT_EN_TC (1 << 1)
+#define IMX_SDHC_INT_EN_CC (1 << 0)
+
+#define IMX_SDHC_INT_SIG_DMAEN (1 << 28)
+#define IMX_SDHC_INT_SIG_TNE (1 << 26)
+#define IMX_SDHC_INT_SIG_AC12E (1 << 24)
+#define IMX_SDHC_INT_SIG_DEBE (1 << 22)
+#define IMX_SDHC_INT_SIG_DCE (1 << 21)
+#define IMX_SDHC_INT_SIG_DTOE (1 << 20)
+#define IMX_SDHC_INT_SIG_CIE (1 << 19)
+#define IMX_SDHC_INT_SIG_CEBE (1 << 18)
+#define IMX_SDHC_INT_SIG_CCE (1 << 17)
+#define IMX_SDHC_INT_SIG_CTOE (1 << 16)
+#define IMX_SDHC_INT_SIG_TP (1 << 14)
+#define IMX_SDHC_INT_SIG_RTE (1 << 12)
+#define IMX_SDHC_INT_SIG_CINT (1 << 8)
+#define IMX_SDHC_INT_SIG_CRM (1 << 7)
+#define IMX_SDHC_INT_SIG_CINS (1 << 6)
+#define IMX_SDHC_INT_SIG_BRR (1 << 5)
+#define IMX_SDHC_INT_SIG_BWR (1 << 4)
+#define IMX_SDHC_INT_SIG_DINT (1 << 3)
+#define IMX_SDHC_INT_SIG_BGE (1 << 2)
+#define IMX_SDHC_INT_SIG_TC (1 << 1)
+#define IMX_SDHC_INT_SIG_CC (1 << 0)
+
+#define IMX_SDHC_HOST_CTRL_CAP_VS18 (1 << 26)
+#define IMX_SDHC_HOST_CTRL_CAP_VS30 (1 << 25)
+#define IMX_SDHC_HOST_CTRL_CAP_VS33 (1 << 24)
+#define IMX_SDHC_HOST_CTRL_CAP_SRS (1 << 23)
+#define IMX_SDHC_HOST_CTRL_CAP_DMAS (1 << 22)
+#define IMX_SDHC_HOST_CTRL_CAP_HSS (1 << 21)
+#define IMX_SDHC_HOST_CTRL_CAP_ADMAS (1 << 20)
+
+#define IMX_SDHC_MIX_CTRL_HS400 (1 << 26)
+#define IMX_SDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
+#define IMX_SDHC_MIX_CTRL_AUTO_TUNE (1 << 24)
+#define IMX_SDHC_MIX_CTRL_SMP_CLK_SEL (1 << 23)
+#define IMX_SDHC_MIX_CTRL_EXE_TUNE (1 << 22)
+#define IMX_SDHC_MIX_CTRL_AC23EN (1 << 7)
+#define IMX_SDHC_MIX_CTRL_NIBBLE_POS (1 << 6)
+#define IMX_SDHC_MIX_CTRL_MSBSEL (1 << 5)
+#define IMX_SDHC_MIX_CTRL_DTDSEL (1 << 4)
+#define IMX_SDHC_MIX_CTRL_DDR_EN (1 << 3)
+#define IMX_SDHC_MIX_CTRL_AC12EN (1 << 2)
+#define IMX_SDHC_MIX_CTRL_BCEN (1 << 1)
+#define IMX_SDHC_MIX_CTRL_DMAEN (1 << 0)
+
+#define IMX_SDHC_VEND_SPEC_FRC_SDCLK_ON (1 << 8)
\ No newline at end of file
diff --git a/system/dev/block/imx-sdhci/rules.mk b/system/dev/block/imx-sdhci/rules.mk
new file mode 100644
index 0000000..ac5c6ab
--- /dev/null
+++ b/system/dev/block/imx-sdhci/rules.mk
@@ -0,0 +1,20 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_TYPE := driver
+
+MODULE_SRCS := \
+ $(LOCAL_DIR)/imx-sdhci.c
+
+MODULE_STATIC_LIBS := system/ulib/ddk system/ulib/sync system/ulib/pretty
+
+MODULE_LIBS := system/ulib/driver system/ulib/zircon system/ulib/c system/ulib/fdio
+
+MODULE_HEADER_DEPS := system/dev/soc/imx8m
+
+include make/module.mk
diff --git a/system/dev/block/sdmmc/mmc.c b/system/dev/block/sdmmc/mmc.c
index 41d189c..9c11df9 100644
--- a/system/dev/block/sdmmc/mmc.c
+++ b/system/dev/block/sdmmc/mmc.c
@@ -27,7 +27,7 @@
// Send the MMC_SWITCH command
zx_status_t st = mmc_switch(dev, index, value);
if (st != ZX_OK) {
- zxlogf(ERROR, "mmc: failed to MMC_SWITCH (0x%x=%d), retcode = %d\n", index, value, st);
+ printf("mmc: failed to MMC_SWITCH (0x%x=%d), retcode = %d\n", index, value, st);
return st;
}
@@ -36,12 +36,12 @@
st = sdmmc_send_status(dev, &resp);
if (st == ZX_OK) {
if (resp & MMC_STATUS_SWITCH_ERR) {
- zxlogf(ERROR, "mmc: mmc status error after MMC_SWITCH (0x%x=%d), status = 0x%08x\n",
+ printf("mmc: mmc status error after MMC_SWITCH (0x%x=%d), status = 0x%08x\n",
index, value, resp);
st = ZX_ERR_INTERNAL;
}
} else {
- zxlogf(ERROR, "mmc: failed to MMC_SEND_STATUS (%x=%d), retcode = %d\n", index, value, st);
+ printf("mmc: failed to MMC_SEND_STATUS (%x=%d), retcode = %d\n", index, value, st);
}
return ZX_OK;
@@ -52,7 +52,7 @@
// Switch the card to the new bus width
zx_status_t st = mmc_do_switch(dev, MMC_EXT_CSD_BUS_WIDTH, mmc_ext_csd_bus_width);
if (st != ZX_OK) {
- zxlogf(ERROR, "mmc: failed to switch bus width to EXT_CSD %d, retcode = %d\n",
+ printf("mmc: failed to switch bus width to EXT_CSD %d, retcode = %d\n",
mmc_ext_csd_bus_width, st);
return ZX_ERR_INTERNAL;
}
@@ -60,7 +60,7 @@
if (bus_width != dev->bus_width) {
// Switch the host to the new bus width
if ((st = sdmmc_set_bus_width(&dev->host, bus_width)) != ZX_OK) {
- zxlogf(ERROR, "mmc: failed to switch the host bus width to %d, retcode = %d\n",
+ printf("mmc: failed to switch the host bus width to %d, retcode = %d\n",
bus_width, st);
return ZX_ERR_INTERNAL;
}
@@ -109,13 +109,13 @@
zx_status_t st = mmc_do_switch(dev, MMC_EXT_CSD_HS_TIMING, ext_csd_timing);
if (st != ZX_OK) {
- zxlogf(ERROR, "mmc: failed to switch device timing to %d\n", new_timing);
+ printf("mmc: failed to switch device timing to %d\n", new_timing);
return st;
}
// Switch the host timing
if ((st = sdmmc_set_timing(&dev->host, new_timing)) != ZX_OK) {
- zxlogf(ERROR, "mmc: failed to switch host timing to %d\n", new_timing);
+ printf("mmc: failed to switch host timing to %d\n", new_timing);
return st;
}
@@ -126,7 +126,7 @@
static zx_status_t mmc_switch_freq(sdmmc_device_t* dev, uint32_t new_freq) {
zx_status_t st;
if ((st = sdmmc_set_bus_freq(&dev->host, new_freq)) != ZX_OK) {
- zxlogf(ERROR, "mmc: failed to set host bus frequency, retcode = %d\n", st);
+ printf("mmc: failed to set host bus frequency, retcode = %d\n", st);
return st;
}
dev->clock_rate = new_freq;
@@ -148,13 +148,14 @@
uint8_t spec_vrsn = (raw_csd[MMC_CSD_SPEC_VERSION] >> 2) & 0xf;
// Only support spec version > 4.0
if (spec_vrsn < MMC_CID_SPEC_VRSN_40) {
+ printf("%s: unsupported version %d\n", __FUNCTION__, spec_vrsn);
return ZX_ERR_NOT_SUPPORTED;
}
- zxlogf(SPEW, "mmc: CSD version %u spec version %u\n",
+ printf("mmc: CSD version %u spec version %u\n",
(raw_csd[MMC_CSD_SPEC_VERSION] >> 6) & 0x3, spec_vrsn);
if (driver_get_log_flags() & DDK_LOG_SPEW) {
- zxlogf(SPEW, "CSD:\n");
+ printf("CSD:\n");
hexdump8_ex(raw_csd, 16, 0);
}
@@ -163,14 +164,14 @@
(raw_csd[MMC_CSD_SIZE_START + 1] << 2) |
((raw_csd[MMC_CSD_SIZE_START + 2] & 0x3) << 10);
if (c_size != 0xfff) {
- zxlogf(ERROR, "mmc: unsupported C_SIZE 0x%04x\n", c_size);
+ printf("mmc: unsupported C_SIZE 0x%04x\n", c_size);
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
static zx_status_t mmc_decode_ext_csd(sdmmc_device_t* dev, const uint8_t* raw_ext_csd) {
- zxlogf(SPEW, "mmc: EXT_CSD version %u CSD version %u\n", raw_ext_csd[192], raw_ext_csd[194]);
+ printf("mmc: EXT_CSD version %u CSD version %u\n", raw_ext_csd[192], raw_ext_csd[194]);
// Get the capacity for the card
uint32_t sectors = (raw_ext_csd[212] << 0) | (raw_ext_csd[213] << 8) |
@@ -178,7 +179,7 @@
dev->block_info.block_count = sectors * MMC_SECTOR_SIZE / MMC_BLOCK_SIZE;
dev->block_info.block_size = (uint32_t)MMC_BLOCK_SIZE;
- zxlogf(TRACE, "mmc: found card with capacity = %" PRIu64 "B\n",
+ printf("mmc: found card with capacity = %" PRIu64 "B\n",
dev->block_info.block_count * dev->block_info.block_size);
return ZX_OK;
@@ -213,23 +214,23 @@
// Query OCR
uint32_t ocr = 0;
if ((st = mmc_send_op_cond(dev, ocr, &ocr)) != ZX_OK) {
- zxlogf(ERROR, "mmc: MMC_SEND_OP_COND failed, retcode = %d\n", st);
+ printf("mmc: MMC_SEND_OP_COND failed, retcode = %d\n", st);
goto err;
}
// Indicate sector mode
if ((st = mmc_send_op_cond(dev, ocr, &ocr)) != ZX_OK) {
- zxlogf(ERROR, "mmc: MMC_SEND_OP_COND failed, retcode = %d\n", st);
+ printf("mmc: MMC_SEND_OP_COND failed, retcode = %d\n", st);
goto err;
}
// Get CID from card
// Only supports 1 card currently so no need to loop
if ((st = mmc_all_send_cid(dev, dev->raw_cid)) != ZX_OK) {
- zxlogf(ERROR, "mmc: MMC_ALL_SEND_CID failed, retcode = %d\n", st);
+ printf("mmc: MMC_ALL_SEND_CID failed, retcode = %d\n", st);
goto err;
}
- zxlogf(SPEW, "mmc: MMC_ALL_SEND_CID cid 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ printf("mmc: MMC_ALL_SEND_CID cid 0x%08x 0x%08x 0x%08x 0x%08x\n",
dev->raw_cid[0],
dev->raw_cid[1],
dev->raw_cid[2],
@@ -239,34 +240,39 @@
// Set relative card address
if ((st = mmc_set_relative_addr(dev, 1)) != ZX_OK) {
- zxlogf(ERROR, "mmc: MMC_SET_RELATIVE_ADDR failed, retcode = %d\n", st);
+ printf("mmc: MMC_SET_RELATIVE_ADDR failed, retcode = %d\n", st);
goto err;
}
dev->rca = 1;
// Read CSD register
if ((st = mmc_send_csd(dev, dev->raw_csd)) != ZX_OK) {
- zxlogf(ERROR, "mmc: MMC_SEND_CSD failed, retcode = %d\n", st);
+ printf("mmc: MMC_SEND_CSD failed, retcode = %d\n", st);
goto err;
}
+ printf("mmc: 0x%x 0x%x 0x%x 0x%x\n",
+ dev->raw_csd[0], dev->raw_csd[1], dev->raw_csd[2], dev->raw_csd[3]);
+
if ((st = mmc_decode_csd(dev, (const uint8_t*)dev->raw_csd)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
// Select the card
if ((st = mmc_select_card(dev)) != ZX_OK) {
- zxlogf(ERROR, "mmc: MMC_SELECT_CARD failed, retcode = %d\n", st);
+ printf("mmc: MMC_SELECT_CARD failed, retcode = %d\n", st);
goto err;
}
// Read extended CSD register
if ((st = mmc_send_ext_csd(dev, dev->raw_ext_csd)) != ZX_OK) {
- zxlogf(ERROR, "mmc: MMC_SEND_EXT_CSD failed, retcode = %d\n", st);
+ printf("mmc: MMC_SEND_EXT_CSD failed, retcode = %d\n", st);
goto err;
}
if ((st = mmc_decode_ext_csd(dev, (const uint8_t*)dev->raw_ext_csd)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
@@ -279,7 +285,7 @@
// Switch to 1.8V signal voltage
sdmmc_voltage_t new_voltage = SDMMC_VOLTAGE_180;
if ((st = sdmmc_set_signal_voltage(&dev->host, new_voltage)) != ZX_OK) {
- zxlogf(ERROR, "mmc: failed to switch to 1.8V signalling, retcode = %d\n", st);
+ printf("mmc: failed to switch to 1.8V signalling, retcode = %d\n", st);
goto err;
}
dev->signal_voltage = new_voltage;
@@ -289,47 +295,56 @@
// Must perform tuning at HS200 first if HS400 is supported
if (mmc_supports_hs200(dev) && dev->bus_width != SDMMC_BUS_WIDTH_1) {
if ((st = mmc_switch_timing(dev, SDMMC_TIMING_HS200)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
if ((st = mmc_switch_freq(dev, FREQ_200MHZ)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
if ((st = sdmmc_perform_tuning(&dev->host)) != ZX_OK) {
- zxlogf(ERROR, "mmc: tuning failed %d\n", st);
+ printf("mmc: tuning failed %d\n", st);
goto err;
}
if (mmc_supports_hs400(dev) && dev->bus_width == SDMMC_BUS_WIDTH_8) {
if ((st = mmc_switch_timing(dev, SDMMC_TIMING_HS)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
if ((st = mmc_switch_freq(dev, FREQ_52MHZ)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
if ((st = mmc_set_bus_width(dev, SDMMC_BUS_WIDTH_8,
MMC_EXT_CSD_BUS_WIDTH_8_DDR)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
if ((st = mmc_switch_timing(dev, SDMMC_TIMING_HS400)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
if ((st = mmc_switch_freq(dev, FREQ_200MHZ)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
}
} else {
if ((st = mmc_switch_timing(dev, SDMMC_TIMING_HS)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
if (mmc_supports_hsddr(dev) && (dev->bus_width != SDMMC_BUS_WIDTH_1)) {
if ((st = mmc_switch_timing(dev, SDMMC_TIMING_HSDDR)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
@@ -337,23 +352,26 @@
MMC_EXT_CSD_BUS_WIDTH_4_DDR :
MMC_EXT_CSD_BUS_WIDTH_8_DDR;
if ((st = mmc_set_bus_width(dev, dev->bus_width, mmc_bus_width)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
}
if ((st = mmc_switch_freq(dev, FREQ_52MHZ)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
}
} else {
// Set the bus frequency to legacy timing
if ((st = mmc_switch_freq(dev, FREQ_25MHZ)) != ZX_OK) {
+ printf("%s %d\n", __FUNCTION__, __LINE__);
goto err;
}
dev->timing = SDMMC_TIMING_LEGACY;
}
- zxlogf(INFO, "mmc: initialized mmc @ %u mhz, bus width %d, timing %d\n",
+ printf("mmc: initialized mmc @ %u mhz, bus width %d, timing %d\n",
dev->clock_rate, dev->bus_width, dev->timing);
err:
diff --git a/system/dev/board/imx8mevk/imx8mevk-sdhci.c b/system/dev/board/imx8mevk/imx8mevk-sdhci.c
new file mode 100644
index 0000000..728de7e
--- /dev/null
+++ b/system/dev/board/imx8mevk/imx8mevk-sdhci.c
@@ -0,0 +1,69 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <ddk/debug.h>
+#include <ddk/device.h>
+#include <ddk/protocol/platform-bus.h>
+#include <ddk/protocol/platform-defs.h>
+
+#include <soc/imx8m/imx8m.h>
+#include <soc/imx8m/imx8m-hw.h>
+#include <soc/imx8m/imx8m-iomux.h>
+#include <soc/imx8m/imx8m-gpio.h>
+#include <limits.h>
+
+#include "imx8mevk.h"
+
+static const pbus_mmio_t sdhci_mmios[] = {
+ {
+ // eMMC Interface
+ .base = IMX8M_AIPS_USDHC1_BASE,
+ .length = IMX8M_AIPS_LENGTH,
+ },
+ {
+ // uSD Interface
+ .base = IMX8M_AIPS_USDHC2_BASE,
+ .length = IMX8M_AIPS_LENGTH,
+ },
+};
+
+static const pbus_irq_t sdhci_irqs[] = {
+ {
+ .irq = IMX8M_A53_INTR_USDHC1,
+ },
+ {
+ .irq = IMX8M_A53_INTR_USDHC2,
+ },
+};
+
+static const pbus_bti_t sdhci_btis[] = {
+ {
+ .iommu_index = 0,
+ .bti_id = BTI_SDHCI,
+ },
+};
+
+static pbus_dev_t sdhci_dev = {
+ .name = "sdhci",
+ .vid = PDEV_VID_NXP,
+ .pid = PDEV_PID_IMX8MEVK,
+ .did = PDEV_DID_IMX_SDHCI,
+ .mmios = sdhci_mmios,
+ .mmio_count = countof(sdhci_mmios),
+ .irqs = sdhci_irqs,
+ .irq_count = countof(sdhci_irqs),
+ .btis = sdhci_btis,
+ .bti_count = countof(sdhci_btis),
+};
+
+
+zx_status_t imx8m_sdhci_init(imx8mevk_bus_t* bus) {
+ zx_status_t status = pbus_device_add(&bus->pbus, &sdhci_dev, 0);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: pbus_device_add failed %d\n", __FUNCTION__, status);
+ return status;
+ }
+
+ return ZX_OK;
+}
\ No newline at end of file
diff --git a/system/dev/board/imx8mevk/imx8mevk.c b/system/dev/board/imx8mevk/imx8mevk.c
index 7a6805a..51f5dec 100644
--- a/system/dev/board/imx8mevk/imx8mevk.c
+++ b/system/dev/board/imx8mevk/imx8mevk.c
@@ -76,6 +76,11 @@
goto fail;
}
+ if ((status = imx8m_sdhci_init(bus)) != ZX_OK) {
+ zxlogf(ERROR, "%s: failed %d\n", __FUNCTION__, status);
+ goto fail;
+ }
+
return ZX_OK;
fail:
diff --git a/system/dev/board/imx8mevk/imx8mevk.h b/system/dev/board/imx8mevk/imx8mevk.h
index 2389eb8..f75141e 100644
--- a/system/dev/board/imx8mevk/imx8mevk.h
+++ b/system/dev/board/imx8mevk/imx8mevk.h
@@ -17,6 +17,7 @@
{
BTI_BOARD,
BTI_USB_XHCI,
+ BTI_SDHCI,
};
typedef struct {
@@ -33,4 +34,4 @@
zx_status_t imx8m_gpio_init(imx8mevk_bus_t* bus);
zx_status_t imx_usb_init(imx8mevk_bus_t* bus);
-
+zx_status_t imx8m_sdhci_init(imx8mevk_bus_t* bus);
diff --git a/system/dev/board/imx8mevk/rules.mk b/system/dev/board/imx8mevk/rules.mk
index 24f8500..98e7bc2 100644
--- a/system/dev/board/imx8mevk/rules.mk
+++ b/system/dev/board/imx8mevk/rules.mk
@@ -12,6 +12,7 @@
$(LOCAL_DIR)/imx8mevk.c \
$(LOCAL_DIR)/imx8mevk-gpio.c \
$(LOCAL_DIR)/imx8mevk-usb.c \
+ $(LOCAL_DIR)/imx8mevk-sdhci.c
MODULE_STATIC_LIBS := \
diff --git a/system/ulib/ddk/include/ddk/protocol/platform-defs.h b/system/ulib/ddk/include/ddk/protocol/platform-defs.h
index 7a578cf..4f43494 100644
--- a/system/ulib/ddk/include/ddk/protocol/platform-defs.h
+++ b/system/ulib/ddk/include/ddk/protocol/platform-defs.h
@@ -80,5 +80,6 @@
#define PDEV_PID_IMX8MEVK 1
#define PDEV_DID_IMX_GPIO 1
+#define PDEV_DID_IMX_SDHCI 2
__END_CDECLS;