imx8x: uSDHC driver
authorLukas Humbel <humbell@inf.ethz.ch>
Mon, 13 Jan 2020 11:23:24 +0000 (12:23 +0100)
committerLukas Humbel <humbell@inf.ethz.ch>
Mon, 13 Jan 2020 11:23:24 +0000 (12:23 +0100)
Signed-off-by: Lukas Humbel <lukas.humbel@inf.ethz.ch>

devices/Hakefile
devices/imx8x/sdhc.dev [new file with mode: 0644]
errors/errno.fugu
hake/menu.lst.armv8_imx8x
platforms/Hakefile
usr/drivers/imx8x/sdhc/Hakefile [new file with mode: 0644]
usr/drivers/imx8x/sdhc/sdhc.c [new file with mode: 0644]
usr/kaluga/armv8_imx8x.c

index e720f2f..c38908f 100644 (file)
@@ -77,6 +77,7 @@
            "ti_i2c",
            "ti_twl6030",
            "sdhc",
+           "imx8x/sdhc",
            "apm88xxxx/apm88xxxx_pc16550",
            "armv8/armv8_cache_ctrl",
            "omap/ehci",
diff --git a/devices/imx8x/sdhc.dev b/devices/imx8x/sdhc.dev
new file mode 100644 (file)
index 0000000..f2e05b9
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2019, ETH Zurich. All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+/*
+ * sdhc.dev
+ *
+ * DESCRIPTION: IMX8X SD Host Controller
+ *
+ * See:
+ *   SD Specifications Part A2: SD Host Controller Simplified Specification
+ *   Version 3.00, February 25, 2011. 
+ *   Technical Committee, SD Association
+ *
+ */
+ device sdhc msbfirst (addr base) "IMX8X SD Host Controller" {
+
+     // 14.8.8.1.2
+     register ds_addr rw addr(base, 0x00) "DMA system address" type(uint32);
+     register cmd_arg2 rw also addr(base, 0x00) "Argument 2" type(uint32);
+
+     // 14.8.8.1.3
+     register blk_att rw addr(base, 0x04) "Block count" {
+         blkcnt           16      "Block count";
+         _                 3 mbz;
+         blksize          13      "Transfer block size (bytes)";
+     };
+
+     // 14.8.8.1.4
+     register cmd_arg rw addr(base, 0x08) "Argument 1" type(uint32);
+
+     // 14.8.8.1.5
+     constants auto_en "Auto command enable values" {
+        auto_en_dis = 0b00     "Auto Command Disabled";
+        auto_en_12  = 0b01     "Auto CMD12 Enable";
+        auto_en_23  = 0b10     "Auto CMD23 Enable";
+     };
+     constants cmd_tp "Command type" {
+        cmd_tp_abrt = 0b11     "Abort CMD12, CMD52 for writing I/O Abort";
+        cmd_tp_rsme = 0b10     "Resume CMD52 for writing Function Select";
+        cmd_tp_susp = 0b01     "Suspend CMD52 for writing Bus Suspend";
+        cmd_tp_norm = 0b00     "Normal; other commands";
+     };
+     constants rsp_tp "Response type" {
+        rsp_tp_none = 0b00     "No response";
+        rsp_tp_136  = 0b01     "Response length 136";
+        rsp_tp_48   = 0b10     "Response length 48";
+        rsp_tp_48cb = 0b11     "Response length 48 check busy after response";
+     };
+     register cmd_xfr_typ rw addr(base, 0x0C) "Command Transfer Type" {
+         _              2 mbz;
+         cmdinx         6              "Command index";
+         cmdtyp         2 type(cmd_tp) "Command type";
+         dpsel          1       "Data present select";
+         cicen          1       "Command index check enable";
+         cccen          1       "Command CRC check enable";
+         _              1 mbz;
+         rsptyp         2  type(rsp_tp)   "Response type select";
+         _              16 mbz;
+     };
+
+     // 14.8.8.1.6-9
+     register cmd_rsp0 rw addr(base, 0x10) "Command Response 0" type(uint32);
+     register cmd_rsp1 rw addr(base, 0x14) "Command Response 1" type(uint32);
+     register cmd_rsp2 rw addr(base, 0x18) "Command Response 2" type(uint32);
+     register cmd_rsp3 rw addr(base, 0x1C) "Command Response 3" type(uint32);
+
+     // 14.8.8.1.10
+     register data_buff_acc_port rw addr(base, 0x20) "Data Buffer Access Port" type(uint32);
+    
+
+     // 14.8.8.1.11 
+     register pres_state rw addr(base, 0x24) "Present State" {
+        dlsl            8 "Data Line Signal Level";
+        clsl            1 "CMD Line Signal Level";
+        _               3  mbz;          
+        wpspl           1 "Write Protect Switch Pin Level";
+        cdpl            1 "Card Detect Pin Level";
+        _               1 mbz;
+        cinst           1 "Card inserted";
+        tscd            1 "Tape Select Change Done";
+        _               2 mbz;
+        rtr             1 "Retuning request";
+        bren            1 "Buffer read enable";
+        bwen            1 "Buffer write enable";
+        rta             1 "Read transfer active";
+        wta             1 "Write transfer active";
+        sdoff           1 "SD Clock Gated Off Internally";
+        peroff          1 "IPG_PERCLK Gated Off Internally";
+        hckoff          1 "HCLK Gated Off Internally";
+        ipgoff          1 "IPG_CLK Gated Off Internally";
+        sdstb           1 "SD Clock Stable";
+        dla             1 "Data Line Active";
+        cdihb           1 "Command Inhibit (DATA)";
+        cihb            1 "Command Inhibit (CMD)";
+     };
+
+     // 14.8.8.1.12 
+     register prot_ctrl rw addr(base, 0x28) "Protocol Control" {
+        _ 1 mbz;
+        non_exact_blk_rd 1;
+        _ 3 mbz;
+        wecrm 1;
+        wecins 1;
+        wecint 1;
+        _ 3;
+        rd_done_no_8clk 1;
+        iabg 1;
+        rwctl 1;
+        creq 1;
+        sabgreq 1;
+        _ 6 mbz;
+        dmasel 2;
+        cdss 1;
+        cdtl 1;
+        emode 2;
+        d3cd 1;
+        dtw 2;
+        lctl 1;
+     };
+
+     // 14.8.8.1.13 
+     register sys_ctrl rw addr(base, 0x2C) "System control" {
+        _               3 mbz;
+        rstt            1 "Reset Tuning";
+        inita           1 "initialization activation";
+        rstd            1 "Software reset Data";
+        rstc            1 "Software reset CMD";
+        rsta            1 "Software reset ALL";
+        ipp_rst_n       1 "";
+         _              3 mbz;
+        dtocv           4 "data timeout counter value";
+        sdclkfs         8 "sdclk frequency select";
+        dvs             4 "divisor";
+        _               4 mbz;
+        
+     };
+
+     // 14.8.8.1.14 
+     register int_status rw1c addr(base, 0x30) "Inerrupt Status" {
+         _              3 mbz;
+         dmae           1 "DMA Error";
+         _              1 mbz;
+         tne            1 "Tuning Error";
+         _              1 mbz;
+         ac12e          1 "Auto Cmd 12 Error";
+         _              1 mbz;
+         debe           1 "Data End Bit Error";
+         dce            1 "Data CRC Error";
+         dtoe           1 "Data Timeout Error";
+         cie            1 "Command Index Error";
+         cebe           1 "Command End Bit error";
+         cce            1 "Command CRC Error";
+         ctoe           1 "Command Timeout Error";
+
+         // 16 bit boundary 
+         _              1 mbz;
+         cqi            1 "Command Queueing Interrupt";
+         tp             1 "Tuning pass";
+         rte            1 "Re-Tuning event";
+         _              3 mbz;
+         cint           1 "Card interrupt";
+         crm            1 "Card removal";
+         cins           1 "Card insertion";
+         brr            1 "Buffer read ready";
+         bwr            1 "Buffer write ready";
+         dint           1 "DMA interrupt";
+         bge            1 "Block gap event";
+         tc             1 "Transfer complete";
+         cc             1 "Command complete";
+     };
+
+    regtype ir "Interrupt Enable Register" {
+         _                3 mbz;
+         dmaeen           1 "DMA Error";
+         _                1 mbz;
+         tneen            1 "Tuning Error";
+         _                1 mbz;
+         ac12een          1 "Auto Cmd 12 Error";
+         _                1 mbz;
+         debeen           1 "Data End Bit Error";
+         dceen            1 "Data CRC Error";
+         dtoeen           1 "Data Timeout Error";
+         cieen            1 "Command Index Error";
+         cebeen           1 "Command End Bit error";
+         cceen            1 "Command CRC Error";
+         ctoeen           1 "Command Timeout Error";
+
+         // 16 bit boundary 
+         _                1 mbz;
+         cqien            1 "Command Queueing Interrupt";
+         tpen             1 "Tuning pass";
+         rteen            1 "Re-Tuning event";
+         _                3 mbz;
+         cinten           1 "Card interrupt";
+         crmen            1 "Card removal";
+         cinsen           1 "Card insertion";
+         brren            1 "Buffer read ready";
+         bwren            1 "Buffer write ready";
+         dinten           1 "DMA interrupt";
+         bgeen            1 "Block gap event";
+         tcen             1 "Transfer complete";
+         ccen             1 "Command complete";
+     };
+
+     register int_status_en addr(base, 0x34) "Interrupt Status enable" type(ir);
+     register int_signal_en addr(base, 0x38) "Interrupt Signal enable" type(ir);
+
+     // 14.8.8.1.17
+     register autocmd12_err_status addr(base, 0x3c) "Auto CMD12 Error Status" {
+        _ 8 mbz;
+        smp_clk_sel 1;
+        execute_tuning 1;
+        _ 14 mbz;
+        cnibac12e 1;
+        _ 2 mbz;
+        ac12ie 1;
+        ac12ce 1;
+        ac12ebe 1;
+        ac12toe 1; 
+        ac12ne  1;
+     };
+
+    // 14.8.8.1.18
+    register host_ctrl_cap addr(base, 0x40) "Host Controller Capabilities" {
+        _ 5 mbz;
+        vs_18 1;
+        vs_30 1;
+        vs_33 1;
+        srs   1;
+        dmas  1;
+        hss   1;
+        admas 1;
+        _ 1 mbz;
+        mbl 3;
+        retuning_mode 2;
+        use_tuning_sdr50 1;
+        _ 1 mbz;
+        time_count_retuning 4;
+        _ 5 mbz;
+        ddr50_support 1;
+        sdr104_support 1;
+        sdr50_support 1;
+    };
+
+    // 14.8.8.1.19
+    register wtmk_lvl addr(base, 0x44) "Watermark Level" {
+        _ 8 mbz;
+        wr_wml 8;
+        _ 8 mbz;
+        rd_wml 8;
+    };
+
+    // 14.8.8.1.20
+    register mix_ctrl addr(base, 0x48) "Mix Control" {
+        _ 1 mb1;
+        _ 1 mbz;
+        _ 1 mbz;
+        _ 1;
+        en_hs400_mode 1;
+        hs400_mode 1;
+        fbclk_sel 1;
+        auto_tune_en 1;
+        smp_clk_sel 1;
+        exe_tune 1;
+        _ 14;
+        ac23en 1;
+        nibble_pos 1;
+        msbsel 1;
+        dtdsel 1;
+        ddr_en 1;
+        ac12en 1;
+        bcen 1;
+        dmaen 1;
+    };
+
+     // 14.8.8.1.24
+     register dll rw addr(base, 0x60) "Delay line control" {
+        dll_ctrl_ref_update_int 4; 
+        dll_ctrl_slv_update_int 8;
+        _ 1 mbz;
+        dll_ctrl_slv_dly_target1 3;
+        dll_ctrl_slv_override_val 7;
+        dll_ctrl_slv_override  1;
+        dll_ctrl_gate_update 1;
+        dll_ctrl_slv_dly_target0 4;
+        dll_ctrl_slv_force_upd 1;
+        dll_ctrl_reset 1;
+        dll_ctrl_enable 1; 
+     };
+
+     // 14.8.8.1.26
+     register clk_tune_ctrl_status  rw addr(base, 0x68) "CLK Tuning Control and Status" {
+        pre_err 1;
+        tap_sel_pre 7;
+        tap_sel_out 4;
+        tap_sel_post 4;
+        nxt_err 1;
+        dly_cell_set_pre 7;
+        dly_cell_set_out 4;
+        dly_cell_set_post 4;
+     };
+
+    // 14.8.8.1.29
+    //register vend_spec rw addr(base, 0xC0) "Vendor Specific" {
+    //   cmd_byte_en        1  "cmd byte en";
+    //   _                 15 mbz;
+    //   crc_chk_dis        1 "CRC Check Disable";
+    //   _                  6 mbz;
+    //   frc_sdclk_on       1 "Force CLK output active";
+    //   _                  4 mbz;
+    //   ac12_wr_chkbusy_en 1 "Check busy enable";
+    //   conflict_chk_en    1 "Conflic check enable";
+    //   vselect            1 "Voltage selection";
+    //   _                  1 mbz;
+    //};
+
+    // This is vend_spec according to uboot
+    register vend_spec rw addr(base, 0xC0) "Vendor Specific" {
+       _                  17 mbz;
+       cken 1;
+       peren 1;
+       hcken 1;
+       ipgen 1;
+       _                  11 mbz;
+    };
+
+
+    // 14.8.8.1.30
+    register mmc_boot rw addr(base, 0xC4) "MMC Boot Register" {
+        boot_blk_cnt        16;
+        _                    7 mbz;
+        disable_time_out     1;
+        auto_sabg_en         1;
+        boot_en              1;
+        boot_mode            1;
+        boot_ack             1;
+        dtocv_ack            4;
+    };
+
+    // 14.8.8.1.31
+    register vend_spec2 rw addr(base, 0xC8) "Vendor Specific 2" {
+        fbclk_tap_sel 16;
+        en_32k_clk  1;
+        bus_rst 1;
+        part_dll_debug 1;
+        acmd23_argu2_en  1;
+        hs400_rd_clk_stop_en 1;
+        hs400_wr_clk_stop_en 1;
+        _ 3 mbz;
+        tuning_cmd_en 1;
+        tuning_1bit_en 1;
+        tuning_8bit_en 1;
+        card_int_d3_test 1;
+        _ 3 mbz;
+        
+    };
+    
+     
+
+ };
index d5a186a..f51c48a 100755 (executable)
@@ -1101,6 +1101,14 @@ errors mmchs MMC_ERR_ {
     FAILURE WRITE_READY             "Card not ready for writing.",
 };
 
+errors sdhc SDHC_ERR_ {
+    failure UNKOWN_VENDOR           "Unknown SDHC controller idendtity",
+    failure RESET_TIMEOUT           "Timeout while resetting",
+    failure CMD_TIMEOUT             "Command time out",
+    failure CMD_CONFLICT            "Conflict on command line",
+    failure TEST_FAILED             "Test Failed",
+};
+
 // errors generated by FAT
 errors fat FAT_ERR_ {
     failure BAD_FS              "Filesystem does not look like FAT, or is an unsupported kind of FAT",
index b581dad..ead8fd3 100644 (file)
@@ -25,10 +25,10 @@ modulenounzip /skb_ramfs.cpio.gz nospawn
 
 # Drivers
 module /armv8/sbin/corectrl auto
-#module /armv8/sbin/pci auto
 module /armv8/sbin/serial_lpuart auto 
 module /armv8/sbin/pl390_dist auto 
 module /armv8/sbin/int_route auto
+module /armv8/sbin/imx8x_sdhc auto
 
 
 # General user domains
index cae574e..6901e4b 100644 (file)
@@ -395,7 +395,6 @@ let bin_rcce_lu = [ "/sbin/" ++ f | f <- [
                        "memtest",
                        "int_route",
                        "serial_kernel",
-                       "serial_lpuart",
                        "pl390_dist",
                        "fish",
                        "angler",
@@ -403,6 +402,12 @@ let bin_rcce_lu = [ "/sbin/" ++ f | f <- [
                        "acpi"
                        ] ]
 
+    -- ARMv8 modules for running on the colibri imx8x boards
+    imx8x_modules = [ "/sbin/" ++ f | f <- [
+                        "serial_lpuart",
+                        "imx8x_sdhc"
+                   ]]
+
 
   in
  [
@@ -609,7 +614,7 @@ let bin_rcce_lu = [ "/sbin/" ++ f | f <- [
 
     armv8Image "armv8_rpi3" "armv8_rpi3" "armv8_generic" "a53_rpi3" modules_generic armv8_modules,
 
-    armv8Image "armv8_imx8x" "armv8_imx8x" "armv8_generic" "imx8x" modules_generic armv8_modules,
+    armv8Image "armv8_imx8x" "armv8_imx8x" "armv8_generic" "imx8x" modules_generic (armv8_modules ++ imx8x_modules),
 
     armv8EFIImage "armv8_a57_fvp_base" "armv8_a57_fvp_base" "armv8_generic" "a57_fvp" modules_generic armv8_modules,
 
diff --git a/usr/drivers/imx8x/sdhc/Hakefile b/usr/drivers/imx8x/sdhc/Hakefile
new file mode 100644 (file)
index 0000000..86e2b3f
--- /dev/null
@@ -0,0 +1,26 @@
+--------------------------------------------------------------------------
+-- Copyright (c) 2019, ETH Zurich.
+-- All rights reserved.
+--
+-- This file is distributed under the terms in the attached LICENSE file.
+-- If you do not find this file, copies can be found by writing to:
+-- ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+--
+-- Hakefile for generic (fingers-crossed) sdhc driver
+--
+--------------------------------------------------------------------------
+
+[
+    build drivermodule { target = "imx8x_sdhc_module",
+       cFiles = ["sdhc.c"],
+       architectures = ["armv8"],
+       addLibraries = ["int_route_client"],
+       mackerelDevices = ["imx8x/sdhc"]
+    }, 
+    build driverdomain {
+        target = "imx8x_sdhc",
+        addModules = ["imx8x_sdhc_module"],
+        architectures = ["armv8"]
+    }
+]
+
diff --git a/usr/drivers/imx8x/sdhc/sdhc.c b/usr/drivers/imx8x/sdhc/sdhc.c
new file mode 100644 (file)
index 0000000..717f5c6
--- /dev/null
@@ -0,0 +1,731 @@
+/**
+ * \file
+ * \brief IMX8x uSDHC Digital Host Controller Driver
+ */
+
+/*
+ * Copyright (c) 2007, 2008, 2010, 2011, 2012,2020 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstrasse 6, CH-8092 Zurich,
+ * Attn: Systems Group.
+ */
+
+/*
+ * In U-boot drivers/mmc/fsl_esdhc.c is talking to the
+ * same controller. This driver follows the i.MX 8DualXPlus/8QuadXPlus
+ * Applications Processor Reference Manual register definitions and
+ * descriptions, except for the vendor specific register, which is
+ * used differently in U-Boot, and that seems the correct way.
+ *
+ * Card detection on Toradex boards:
+ * ===========
+ * The Toradex boards dont break out the SDHC card detection pin, instead
+ * a GPIO (Bank4, Pin22) has to be checked for card present. Currently,
+ * this driver simply assumes a card is present.
+ */
+
+#include <barrelfish/barrelfish.h>
+#include <int_route/int_route_client.h>
+#include <pci/pci.h>
+#include <dev/imx8x/sdhc_dev.h>
+#include <driverkit/driverkit.h>
+#include <barrelfish/deferred.h>
+#include <barrelfish/systime.h>
+
+//#define DEBUG_ON
+
+#if defined(DEBUG_ON) || defined(GLOBAL_DEBUG)
+#    define DEBUG(x...) debug_printf(x)
+#else
+#    define DEBUG(x...) ((void)0)
+#endif
+
+#define MMC_CMD_GO_IDLE_STATE       0
+#define MMC_CMD_SEND_OP_COND        1
+#define MMC_CMD_ALL_SEND_CID        2
+#define MMC_CMD_SET_RELATIVE_ADDR   3
+#define MMC_CMD_SET_DSR         4
+#define MMC_CMD_SWITCH          6
+#define MMC_CMD_SELECT_CARD     7
+#define MMC_CMD_SEND_EXT_CSD        8
+#define MMC_CMD_SEND_CSD        9
+#define MMC_CMD_SEND_CID        10
+#define MMC_CMD_STOP_TRANSMISSION   12
+#define MMC_CMD_SEND_STATUS     13
+#define MMC_CMD_SET_BLOCKLEN        16
+#define MMC_CMD_READ_SINGLE_BLOCK   17
+#define MMC_CMD_READ_MULTIPLE_BLOCK 18
+#define MMC_CMD_SEND_TUNING_BLOCK       19
+#define MMC_CMD_SEND_TUNING_BLOCK_HS200 21
+#define MMC_CMD_SET_BLOCK_COUNT         23
+#define MMC_CMD_WRITE_SINGLE_BLOCK  24
+#define MMC_CMD_WRITE_MULTIPLE_BLOCK    25
+#define MMC_CMD_ERASE_GROUP_START   35
+#define MMC_CMD_ERASE_GROUP_END     36
+#define MMC_CMD_ERASE           38
+#define MMC_CMD_APP_CMD         55
+#define MMC_CMD_SPI_READ_OCR        58
+#define MMC_CMD_SPI_CRC_ON_OFF      59
+#define MMC_CMD_RES_MAN         62
+
+#define SD_CMD_SEND_RELATIVE_ADDR   3
+#define SD_CMD_SWITCH_FUNC      6
+#define SD_CMD_SEND_IF_COND     8
+#define SD_CMD_SWITCH_UHS18V        11
+#define SD_CMD_APP_SET_BUS_WIDTH    6
+#define SD_CMD_APP_SD_STATUS        13
+#define SD_CMD_ERASE_WR_BLK_START   32
+#define SD_CMD_ERASE_WR_BLK_END     33
+#define SD_CMD_APP_SEND_OP_COND     41
+#define SD_CMD_APP_SEND_SCR     51
+
+
+#define MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_136 (1 << 1)        /* 136 bit response */
+#define MMC_RSP_CRC (1 << 2)        /* expect valid crc */
+#define MMC_RSP_BUSY    (1 << 3)        /* card may send busy */
+#define MMC_RSP_OPCODE  (1 << 4)        /* response contains opcode */
+
+#define MMC_RSP_NONE    (0)
+#define MMC_RSP_R1  (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R1b (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE| \
+            MMC_RSP_BUSY)
+#define MMC_RSP_R2  (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
+#define MMC_RSP_R3  (MMC_RSP_PRESENT)
+#define MMC_RSP_R4  (MMC_RSP_PRESENT)
+#define MMC_RSP_R5  (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R6  (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R7  (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+
+#define OCR_BUSY        0x80000000
+#define OCR_HCS         0x40000000
+#define OCR_S18R        0x1000000
+
+#define BLOCK_SIZE 512
+
+struct sdhc {
+    sdhc_t dev;
+    uintptr_t vbase;
+    uint32_t caps;
+
+    // Card properties
+    uint8_t cid[16];
+    uint32_t csd[4];
+    uint16_t rca;
+    int high_capacity;
+
+       uint64_t read_bl_len;
+       uint64_t write_bl_len ;
+       uint64_t capacity_user;
+};
+
+struct cmd {
+    uint16_t     cmdidx;
+    unsigned int cmdarg;
+    unsigned int resp_type;
+    unsigned int response[4]; // The response of the command
+    genpaddr_t   dma_base;    // If a data transfer is necessary, use this
+                              // physical base address for read/write.
+};
+
+#define dump(sd) do {\
+        char buf[1024];\
+        sdhc_int_status_pr(buf, 1024, &sd->dev);\
+        DEBUG("%s:%d: %s\n", __FUNCTION__, __LINE__, buf);\
+    } while(0)
+
+#define dump_all(sd) do {\
+        char buf[16*1024];\
+        sdhc_pr(buf, 16*1024, &sd->dev);\
+        printf("%s\n", buf);\
+    } while(0)
+
+
+static void int_handler(void *arg)
+{
+    DEBUG("%s: enter\n", __FUNCTION__);
+    struct sdhc * sd = (struct sdhc *)arg;
+    dump(sd);
+}
+
+__attribute__((used))
+static errval_t software_reset(struct sdhc *sd){
+    sdhc_sys_ctrl_rsta_wrf(&sd->dev, 1); 
+    volatile uint64_t timeout  = 1000000;
+    while(sdhc_sys_ctrl_rsta_rdf(&sd->dev)){
+        timeout--;
+        if(timeout == 0){
+            DEBUG("Reset TIMEOUT!\n");
+            return SDHC_ERR_RESET_TIMEOUT;
+        }
+    }
+
+    // Set sensible defaults
+    sdhc_mmc_boot_rawwr(&sd->dev, 0);
+    sdhc_mix_ctrl_rawwr(&sd->dev, 0);
+    sdhc_clk_tune_ctrl_status_rawwr(&sd->dev, 0);
+    sdhc_dll_rawwr(&sd->dev, 0);
+
+    sdhc_vend_spec_t vs = 0;
+    vs = sdhc_vend_spec_ipgen_insert(vs, 1);
+    vs = sdhc_vend_spec_hcken_insert(vs, 1);
+    sdhc_vend_spec_wr(&sd->dev, vs);
+    
+    DEBUG("Reset complete!\n");
+
+    sd->caps = sdhc_host_ctrl_cap_rawrd(&sd->dev);
+
+    //Setting clock to initial 40mhz
+    sdhc_vend_spec_cken_wrf(&sd->dev, 0);
+
+    sdhc_sys_ctrl_t s = 0;
+    s = sdhc_sys_ctrl_dvs_insert(s, 0xf);
+    s = sdhc_sys_ctrl_sdclkfs_insert(s, 0x10);
+    s = sdhc_sys_ctrl_dtocv_insert(s, 0xc);
+    sdhc_sys_ctrl_wr(&sd->dev, s);
+
+    barrelfish_usleep(10000);
+    vs = sdhc_vend_spec_rd(&sd->dev);
+    vs = sdhc_vend_spec_peren_insert(vs, 1);
+    vs = sdhc_vend_spec_cken_insert(vs, 1);
+    sdhc_vend_spec_wr(&sd->dev, vs);
+
+    sdhc_prot_ctrl_dtw_wrf(&sd->dev, 0); // Bus width: 1-bit mode
+    sdhc_prot_ctrl_emode_wrf(&sd->dev, 0x2); // Little endian mode
+    sdhc_sys_ctrl_dtocv_wrf(&sd->dev, 0xe);
+
+    return SYS_ERR_OK;
+}
+
+
+
+__attribute__((used))
+static sdhc_cmd_xfr_typ_t xfr_typ_for_cmd(struct cmd *cmd){
+    sdhc_cmd_xfr_typ_t c = 0;
+
+    if(cmd->cmdidx == MMC_CMD_READ_SINGLE_BLOCK ||
+       cmd->cmdidx == MMC_CMD_WRITE_SINGLE_BLOCK)
+    {
+        c = sdhc_cmd_xfr_typ_dpsel_insert(c, 1);
+    }
+
+    c = sdhc_cmd_xfr_typ_cmdinx_insert(c, cmd->cmdidx);
+    if (cmd->resp_type & MMC_RSP_CRC)
+        c = sdhc_cmd_xfr_typ_cccen_insert(c, 1);
+    if (cmd->resp_type & MMC_RSP_OPCODE)
+        c = sdhc_cmd_xfr_typ_cicen_insert(c, 1);
+
+    
+    c = sdhc_cmd_xfr_typ_rsptyp_insert(c, sdhc_rsp_tp_none);
+    if (cmd->resp_type & MMC_RSP_136)
+        c = sdhc_cmd_xfr_typ_rsptyp_insert(c, sdhc_rsp_tp_136);
+    else if (cmd->resp_type & MMC_RSP_BUSY)
+        c = sdhc_cmd_xfr_typ_rsptyp_insert(c, sdhc_rsp_tp_48cb);
+    else if (cmd->resp_type & MMC_RSP_PRESENT)
+        c = sdhc_cmd_xfr_typ_rsptyp_insert(c, sdhc_rsp_tp_48);
+                                          
+    c = sdhc_cmd_xfr_typ_cmdtyp_insert(c, sdhc_cmd_tp_norm);
+    if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+        c = sdhc_cmd_xfr_typ_cmdtyp_insert(c, sdhc_cmd_tp_abrt);
+
+    return c;
+}
+
+__attribute__((used))
+static errval_t sdhc_send_cmd(struct sdhc * sd, struct cmd * cmd) {
+    DEBUG("sdhc_send_cmd: cmdidx=%d,cmdarg=%d\n", cmd->cmdidx, cmd->cmdarg);
+
+    uint32_t mask; // TODO: in some cases we don't need to wait for all
+    if(cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) {
+       mask = 1;
+    } else {
+       mask = 3;
+    }
+    while(sdhc_pres_state_rawrd(&sd->dev) & mask){
+        DEBUG("Card busy!\n");
+    }
+    DEBUG("Card ready (data & cmd inhibit are clear)!\n");
+    barrelfish_usleep(10000);
+
+    // Clear interrupts
+    sdhc_int_status_rawwr(&sd->dev, ~0x0);
+
+    // Mask interrupts.
+    sdhc_int_signal_en_rawwr(&sd->dev, 0);
+
+    // CMD argument
+    sdhc_cmd_arg_wr(&sd->dev, cmd->cmdarg);
+
+    // Mixer controler
+    int is_read = cmd->cmdidx == MMC_CMD_READ_SINGLE_BLOCK;
+    int is_write = cmd->cmdidx == MMC_CMD_WRITE_SINGLE_BLOCK;
+    sdhc_mix_ctrl_wr(&sd->dev, 0); 
+    sdhc_mix_ctrl_dmaen_wrf(&sd->dev, is_read || is_write);
+    sdhc_mix_ctrl_dtdsel_wrf(&sd->dev, is_read);
+
+    if(is_read || is_write){
+        // DMA address setup
+        assert((cmd->dma_base >> 32) == 0);
+        sdhc_vend_spec2_acmd23_argu2_en_wrf(&sd->dev, 0);
+        sdhc_ds_addr_wr(&sd->dev, cmd->dma_base);
+
+        //Set watermark
+        sdhc_wtmk_lvl_rd_wml_wrf(&sd->dev, 16);
+        sdhc_wtmk_lvl_wr_wml_wrf(&sd->dev, 16);
+    }
+
+    sdhc_cmd_xfr_typ_t c = xfr_typ_for_cmd(cmd);
+    sdhc_cmd_xfr_typ_wr(&sd->dev, c);
+
+    //DEBUG("%s:%d: Wait until irq_stat.tc || irq_stat.cc \n", __FUNCTION__, __LINE__);
+    uint32_t tc = 0;
+    uint32_t cc = 0;
+    size_t i = 0;
+    do {
+        uint32_t ctoe = sdhc_int_status_ctoe_rdf(&sd->dev);
+        uint32_t cce = sdhc_int_status_cce_rdf(&sd->dev);
+        tc = sdhc_int_status_tc_rdf(&sd->dev);
+        cc = sdhc_int_status_cc_rdf(&sd->dev);
+
+        if (ctoe == 0x1 && cce == 0x1) {
+            DEBUG("%s:%d: ctoe = 1 ccrc = 1: Conflict on cmd line.\n",
+                    __FUNCTION__, __LINE__);
+            dump(sd);
+            return SDHC_ERR_CMD_CONFLICT;
+        }
+        if (ctoe == 0x1 && cce == 0x0) {
+            DEBUG("%s:%d: cto = 1 ccrc = 0: Abort.\n", __FUNCTION__, __LINE__);
+            dump(sd);
+            return SDHC_ERR_CMD_TIMEOUT;
+        }
+
+        if (i++ > 1000) {
+            dump(sd);
+            USER_PANIC("Command not Ackd?");
+        }
+        barrelfish_usleep(100);
+        if(tc || cc) break;
+    } while (true);
+    DEBUG("Command complete!\n");
+
+    if(cmd->resp_type & MMC_RSP_136){
+        uint32_t r0 = sdhc_cmd_rsp0_rd(&sd->dev);
+        uint32_t r1 = sdhc_cmd_rsp1_rd(&sd->dev);
+        uint32_t r2 = sdhc_cmd_rsp2_rd(&sd->dev);
+        uint32_t r3 = sdhc_cmd_rsp3_rd(&sd->dev);
+        cmd->response[0] = (r3 << 8) | (r2 >> 24);
+        cmd->response[1] = (r2 << 8) | (r1 >> 24);
+        cmd->response[2] = (r1 << 8) | (r0 >> 24);
+        cmd->response[3] = (r0 << 8);
+    } else {
+        cmd->response[0] = sdhc_cmd_rsp0_rd(&sd->dev);
+    }
+    return SYS_ERR_OK;
+}
+
+static errval_t sdhc_go_idle(struct sdhc *sd) {
+    errval_t err;
+    struct cmd init = {
+        .cmdidx = MMC_CMD_GO_IDLE_STATE,
+        .cmdarg = 0,
+        .resp_type = MMC_RSP_NONE
+    };
+    err = sdhc_send_cmd(sd, &init);
+    if(err_is_ok(err)){
+        DEBUG("Successfully sent GO_IDLE transaction\n");
+    }
+    barrelfish_usleep(2000);
+    return err;
+}
+
+static errval_t sdhc_send_if_cond(struct sdhc *sd) {
+    errval_t err;
+    uint32_t pattern = 0xde;
+    struct cmd cmd = {
+        .cmdidx = SD_CMD_SEND_IF_COND,
+        .cmdarg = 0x100 | pattern, // Offer 3V voltage followed by pattern
+        .resp_type = MMC_RSP_R7
+    };
+
+    err = sdhc_send_cmd(sd, &cmd);
+    if(err_is_ok(err)){
+        if(cmd.response[0] == (0x100 | pattern)){
+            DEBUG("IF_COND: Success! Got (at least) SD Version 2\n");
+            return SYS_ERR_OK;
+        } else {
+            DEBUG("IF_COND: Legacy cards not supported!\n");
+            err = SDHC_ERR_CMD_TIMEOUT;
+        }
+    }
+    return err;
+}
+
+/*
+ * This function queries the card to determine operating voltage/standard
+ * etc. It does not perform a card initialization.
+ */
+static errval_t sdhc_get_ocr(struct sdhc *sd){
+    errval_t err; 
+    uint32_t ocr=0;
+
+    while(1){
+        struct cmd app_cmd = {
+            .cmdidx = MMC_CMD_APP_CMD,
+            .cmdarg = 0,
+            .resp_type = MMC_RSP_R1
+        };
+
+        err = sdhc_send_cmd(sd, &app_cmd);
+        if(err_is_fail(err)){
+            DEBUG_ERR(err, "send app_cmd");
+            return err;
+        }
+
+        struct cmd op_cond_cmd = {
+            .cmdidx = SD_CMD_APP_SEND_OP_COND,
+            .cmdarg = 0x40300000,  // Have OCS and default voltages 
+            .resp_type = MMC_RSP_R3
+        };
+        err = sdhc_send_cmd(sd, &op_cond_cmd);
+        if(err_is_fail(err)) {
+            DEBUG_ERR(err, "send op_cond_cmd");
+            return err;
+        }
+
+        if(op_cond_cmd.response[0] & OCR_BUSY) {
+            ocr = op_cond_cmd.response[0];
+            break;
+        }
+
+        barrelfish_usleep(10000);
+    }
+
+    DEBUG("Received OCR! ocr=0x%"PRIx32"\n", ocr);
+
+    if((ocr & OCR_HCS) == OCR_HCS){
+        DEBUG("High capacity card found!\n");
+        sd->high_capacity = 1;
+    } else {
+        DEBUG("Non high capacity card. NOT TESTED\n");
+        sd->high_capacity = 0;
+    }
+    sd->rca = 0;
+    return SYS_ERR_OK;
+}
+
+
+
+/** 
+ * Here we perform the actual card initialization
+ */
+static errval_t sdhc_card_init(struct sdhc* sd){
+    errval_t err;
+    // Put card in identify mode
+    struct cmd cid = {
+        .cmdidx = MMC_CMD_ALL_SEND_CID,
+        .resp_type = MMC_RSP_R2,
+        .cmdarg = 0
+    };
+    err = sdhc_send_cmd(sd, &cid);
+    if(err_is_fail(err)){
+        DEBUG_ERR(err, "cid command");
+    }
+    memcpy(sd->cid, cid.response, 16);
+
+    struct cmd send_addr = {
+        .cmdidx = SD_CMD_SEND_RELATIVE_ADDR,
+        .cmdarg = sd->rca << 16,
+        .resp_type = MMC_RSP_R6
+    };
+
+    err = sdhc_send_cmd(sd, &send_addr);
+    if(err_is_fail(err)){
+        DEBUG_ERR(err, "send addr command");
+    }
+    sd->rca = send_addr.response[0] >> 16 & 0xffff;
+    DEBUG("Determined RCA=0x%"PRIx16".\n", sd->rca);
+
+    struct cmd send_csd = {
+        .cmdidx = MMC_CMD_SEND_CSD,
+        .resp_type = MMC_RSP_R2,
+        .cmdarg = sd->rca << 16
+    };
+    err = sdhc_send_cmd(sd, &send_csd);
+    if(err_is_fail(err)){
+        DEBUG_ERR(err, "send csd command");
+    }
+    sd->csd[0] = send_csd.response[0];
+    sd->csd[1] = send_csd.response[1];
+    sd->csd[2] = send_csd.response[2];
+    sd->csd[3] = send_csd.response[3];
+    DEBUG("CSD: [0]=%"PRIx32", [1]=%"PRIx32", [2]=%"PRIx32", [3]=%"PRIx32"\n",
+            sd->csd[0], sd->csd[1], sd->csd[2], sd->csd[3]);
+       sd->read_bl_len = 1 << ((sd->csd[1]>>16) & 0xf);
+    DEBUG("SD read_bl_len: %"PRIx64"\n", sd->read_bl_len);
+       sd->write_bl_len = sd->read_bl_len;
+
+       if(sd->high_capacity) {
+               unsigned int csize, cmult;
+               csize = (sd->csd[1] & 0x3f) << 16 | (sd->csd[2] & 0xffff0000) >> 16;
+               cmult = 8;
+               sd->capacity_user = ((csize + 1) << (cmult + 2)) * sd->read_bl_len;
+               DEBUG("SD capacity: %"PRIx64"\n", sd->capacity_user);
+       }
+
+       // Select the card, this puts it into the "transfer" state
+    struct cmd select_card = {
+        .cmdidx = MMC_CMD_SELECT_CARD,
+        .resp_type = MMC_RSP_R1,
+        .cmdarg = sd->rca << 16
+    };
+       err = sdhc_send_cmd(sd, &select_card);
+    if(err_is_fail(err)) {
+        DEBUG_ERR(err, "select_card cmd");
+        return err;
+    }      
+
+    return SYS_ERR_OK;
+}
+
+/* Read BLOCK_SIZE bytes block index into buffer */
+static errval_t sdhc_read_block(struct sdhc * sd, int index, void **buffer){
+    errval_t err;
+
+    // allocate and map frame for buffer
+    struct capref frame;
+    struct frame_identity frameid;
+    size_t retbytes;
+    err = frame_alloc(&frame, 4096, &retbytes);
+    assert(err_is_ok(err));
+    assert(retbytes == 4096);
+    err = vspace_map_one_frame_attr(buffer, retbytes, frame,
+            VREGION_FLAGS_READ_WRITE_NOCACHE, NULL, NULL);
+    for(int i=0; i<128;i++) ((uint32_t*)*buffer)[i] = 0xdead;
+    assert(err_is_ok(err));     
+    err = frame_identify(frame, &frameid);
+    assert(err_is_ok(err));     
+
+
+    //Clear interrupts
+    sdhc_int_status_rawwr(&sd->dev, ~0x0);
+
+    struct cmd set_blocklen = {
+        .cmdidx = MMC_CMD_SET_BLOCKLEN,
+        .cmdarg = BLOCK_SIZE,
+        .resp_type = MMC_RSP_R1
+    };
+    err = sdhc_send_cmd(sd, &set_blocklen);
+    if(err_is_fail(err)){
+        DEBUG_ERR(err, "set_blocklen");
+        return err;
+    }
+
+    struct cmd read_block = {
+        .cmdidx = MMC_CMD_READ_SINGLE_BLOCK,
+        .cmdarg = index,
+        .resp_type = MMC_RSP_R1,
+        .dma_base = frameid.base
+
+    };
+    err = sdhc_send_cmd(sd, &read_block);
+    if(err_is_fail(err)){
+        DEBUG_ERR(err, "read_block");
+        return err;
+    }
+
+    return SYS_ERR_OK;
+}
+
+/* Write BLOCK_SIZE bytes to block index from data */
+static errval_t sdhc_write_block(struct sdhc * sd, int index, void *data){
+    errval_t err;
+
+    // allocate and map frame for buffer
+    struct capref frame;
+    struct frame_identity frameid;
+    size_t retbytes;
+    err = frame_alloc(&frame, 4096, &retbytes);
+    assert(err_is_ok(err));
+    assert(retbytes == 4096);
+    void *data_dest;
+    err = vspace_map_one_frame_attr(&data_dest, retbytes, frame,
+            VREGION_FLAGS_READ_WRITE_NOCACHE, NULL, NULL);
+    assert(err_is_ok(err));     
+    err = frame_identify(frame, &frameid);
+    assert(err_is_ok(err));     
+
+    memcpy(data_dest, data, BLOCK_SIZE);
+
+
+    struct cmd set_blocklen = {
+        .cmdidx = MMC_CMD_SET_BLOCKLEN,
+        .cmdarg = BLOCK_SIZE,
+        .resp_type = MMC_RSP_R1
+    };
+    err = sdhc_send_cmd(sd, &set_blocklen);
+    if(err_is_fail(err)){
+        DEBUG_ERR(err, "set_blocklen");
+        return err;
+    }
+
+    struct cmd write_block = {
+        .cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK,
+        .cmdarg = index,
+        .resp_type = MMC_RSP_R1,
+        .dma_base = frameid.base
+
+    };
+    err = sdhc_send_cmd(sd, &write_block);
+    if(err_is_fail(err)){
+        DEBUG_ERR(err, "read_block");
+        return err;
+    }
+
+    return SYS_ERR_OK;
+}
+
+static errval_t sdhc_init(struct sdhc * sd){
+    //Initialize and identify the card. Roughly following SDHC specification,
+    //3.6 Card Initialization and Identification.
+    errval_t err;
+    
+    DEBUG("Sending go idle transaction...\n"); err = sdhc_go_idle(sd);
+    if(err_is_fail(err)) return err;
+
+    DEBUG("Sending if_cond transaction...\n");
+    err = sdhc_send_if_cond(sd);
+    if(err_is_fail(err)) return err;
+
+    DEBUG("Query OCR (CMD 55/41)...\n");
+    err = sdhc_get_ocr(sd);
+    if(err_is_fail(err)) return err;
+
+    DEBUG("Card initialization...\n");
+    err = sdhc_card_init(sd);
+    if(err_is_fail(err)) return err;
+
+    DEBUG("Card in transfer state!");
+
+    return SYS_ERR_OK;
+}
+
+static errval_t sdhc_test(struct sdhc *sd) {
+    errval_t err;
+    DEBUG("... testing read block...\n");
+    void *buf;
+    err = sdhc_read_block(sd, 0, &buf);
+    if(err_is_fail(err)) return err;
+    for(int i=0; i<16;i++){
+        DEBUG("    buf[%d] = %"PRIx32"\n", i, ((uint32_t*)buf)[i]);
+    }
+
+    DEBUG("... testing write block...\n");
+
+    uint32_t data[128];
+    for(int i=0; i<128; i+=4){
+        data[i] = 0xdead;
+        data[i+1] = 0xbeef;
+        data[i+2] = 0xc0c0;
+        data[i+3] = 0xcaca;
+    };
+    err = sdhc_write_block(sd, 20, data);
+
+    DEBUG("...  reading just written data\n");
+    void *buf2t;
+    err = sdhc_read_block(sd, 20, &buf2t);
+    uint32_t *buf2 = (uint32_t*) buf2t;
+    if(err_is_fail(err)) return err;
+    for(int i=0; i<16;i++){
+        DEBUG("    buf2[%d] = %"PRIx32"\n", i, buf2[i]);
+    }
+    if(buf2[0] == 0xdead && buf2[1] ==0xbeef){
+        return SYS_ERR_OK;
+    } else {
+        return SDHC_ERR_TEST_FAILED;
+    }
+}
+
+static errval_t init(struct bfdriver_instance *bfi, uint64_t flags, iref_t *dev)
+{
+    DEBUG("sdhc entering init\n");
+    errval_t err;
+    struct sdhc *sd = malloc(sizeof(struct sdhc));
+    bfi->dstate = sd;
+    struct capref devframe_cap = { .slot = DRIVERKIT_ARGCN_SLOT_BAR0,
+                                   .cnode = bfi->argcn };
+    err = map_device_cap(devframe_cap, &sd->vbase);
+    if(err_is_fail(err)){
+        DEBUG_ERR(err, "map devframe");
+        return err;
+    }
+    sdhc_initialize(&sd->dev, (mackerel_addr_t)sd->vbase);
+
+    struct capref irq_src;
+    irq_src.cnode = bfi->argcn;
+    irq_src.slot = PCIARG_SLOT_INT; 
+
+    // Register interrupt handler
+    err = int_route_client_route_and_connect(irq_src, 0,
+           get_default_waitset(), int_handler, sd);
+    if (err_is_fail(err)) {
+       USER_PANIC_ERR(err, "interrupt setup failed.");
+    }
+    DEBUG("interrupt setup complete\n");
+
+    err = software_reset(sd);
+    if (err_is_fail(err)) {
+       DEBUG_ERR(err, "software reset failed");
+       return err;
+    }
+    DEBUG("reset done.\n");
+
+    err = sdhc_init(sd);
+    if (err_is_fail(err)) {
+       DEBUG_ERR(err, "mmc init failed (No card present?)");
+       return err;
+    }
+
+    err = sdhc_test(sd);
+    if (err_is_fail(err)) {
+       DEBUG_ERR(err, "mmc test failed");
+       return err;
+    }
+    printf("SHDC test suceeded!\n");
+
+    return SYS_ERR_OK;
+}
+
+static errval_t attach(struct bfdriver_instance *bfi)
+{
+    return SYS_ERR_OK;
+}
+
+static errval_t detach(struct bfdriver_instance *bfi)
+{
+    return SYS_ERR_OK;
+}
+
+static errval_t set_sleep_level(struct bfdriver_instance *bfi, uint32_t level)
+{
+    return SYS_ERR_OK;
+}
+
+static errval_t destroy(struct bfdriver_instance *bfi)
+{
+    struct sdhc *sd = bfi->dstate;
+    free(sd);
+    bfi->dstate = NULL;
+    bfi->device = 0x0;
+    return SYS_ERR_OK;
+}
+
+static errval_t get_ep(struct bfdriver_instance *bfi, bool lmp, struct capref *ret_cap)
+{
+    USER_PANIC("NIY \n");
+    return SYS_ERR_OK;
+}
+
+DEFINE_MODULE(sdhc, init, attach, detach, set_sleep_level, destroy, get_ep);
index d9fbae9..19bf16f 100644 (file)
@@ -202,6 +202,73 @@ static errval_t start_serial_lpuart(lpaddr_t address, uint32_t irq)
     return err;
 } 
 
+
+#define USDHC1_BASE 0x5B010000
+#define USDHC2_BASE 0x5B020000
+#define USDHC0_INT 264
+#define USDHC1_INT 265
+#define USDHC2_INT 266
+
+__attribute__((used))
+static errval_t start_sdhc(lpaddr_t address, uint32_t irq)
+
+{   errval_t err;
+    struct module_info *mi;
+    
+    // get module
+    mi = find_module("imx8x_sdhc");
+    if(mi == NULL){
+        KALUGA_DEBUG("imx8x_sdhc not found, not starting");
+        return KALUGA_ERR_MODULE_NOT_FOUND;
+    }
+    struct driver_argument arg;
+    init_driver_argument(&arg);
+    arg.module_name = "sdhc";
+
+    // get device frame
+    struct capref device_frame;
+    err = imx8x_get_device_cap(address, 0x00010000, &device_frame);
+    if(err_is_fail(err)){
+        USER_PANIC_ERR(err, "get_device_cap");
+    }
+    KALUGA_DEBUG("got device frame for sdhc\n");
+
+
+    struct capref cap = {
+            .cnode = arg.argnode_ref,
+            .slot = DRIVERKIT_ARGCN_SLOT_BAR0
+    };
+    err = cap_copy(cap, device_frame);
+    if(err_is_fail(err)){
+        USER_PANIC_ERR(err, "get_device_cap");
+    }
+
+    // get irq src for lpuart
+    struct capref irq_src;
+    err = slot_alloc(&irq_src);
+    assert(err_is_ok(err));
+
+    err = sys_debug_create_irq_src_cap(irq_src, irq + DIST_OFFSET, irq + DIST_OFFSET);
+    assert(err_is_ok(err));
+
+    struct capref irq_dst = {
+        .cnode = arg.argnode_ref,
+        .slot = PCIARG_SLOT_INT 
+    };
+    err = cap_copy(irq_dst, irq_src);
+    if(err_is_fail(err)){
+        DEBUG_ERR(err, "cap_copy\n");
+        return err;
+    }
+    KALUGA_DEBUG("got irq src cap frame for lpuart\n");
+     
+    err = default_start_function_pure(0, mi, "sdhc {}", &arg);
+    if(err_is_fail(err)){
+        USER_PANIC_ERR(err, "get_device_cap");
+    }
+    return err;
+} 
+
 static lpaddr_t platform_gic_distributor_base = 0x51a00000;
 //static lpaddr_t platform_gic_redistributor_base = 0x51b00000;
 
@@ -323,5 +390,12 @@ errval_t imx8x_startup(void)
         USER_PANIC_ERR(err, "imx8x serial lpuart");
     }
 
+    // SDHC2 is the one that is broken out on the carrier board.
+    err = start_sdhc(USDHC2_BASE, USDHC2_INT);
+    if(err_is_fail(err) && err_no(err) != KALUGA_ERR_MODULE_NOT_FOUND) {
+        USER_PANIC_ERR(err, "imx8x sdhc");
+    }
+
+
     return SYS_ERR_OK;
 }