// adapted for Arduino SD library by Paul Stoffregen
// following code is modified by Walter Zimmer from
// from version provided by
// Petr Gargulak (NXP Employee)
//https://community.nxp.com/servlet/JiveServlet/download/339474-1-263510/SDHC_K60_Baremetal.ZIP
//see also
//https://community.nxp.com/thread/99202

#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__)

#include "core_pins.h"  // include calls to kinetis.h or imxrt.h
#include "usb_serial.h" // for Serial

#include "NXP_SDHC.h"

/******************************************************************************
  Constants
******************************************************************************/

enum {
  SDHC_RESULT_OK = 0,             /* 0: Successful */
  SDHC_RESULT_ERROR,              /* 1: R/W Error */
  SDHC_RESULT_WRPRT,              /* 2: Write Protected */
  SDHC_RESULT_NOT_READY,          /* 3: Not Ready */
  SDHC_RESULT_PARERR,             /* 4: Invalid Parameter */
  SDHC_RESULT_NO_RESPONSE         /* 5: No Response */ // from old diskio.h
};

/*void print_result(int n)
  {
        switch (n) {
        case SDHC_RESULT_OK: serial_print("OK\n"); break;
        case SDHC_RESULT_ERROR: serial_print("R/W Error\n"); break;
        case SDHC_RESULT_WRPRT: serial_print("Write Protect\n"); break;
        case SDHC_RESULT_NOT_READY: serial_print("Not Ready\n"); break;
        case SDHC_RESULT_PARERR: serial_print("Invalid Param\n"); break;
        case SDHC_RESULT_NO_RESPONSE: serial_print("No Response\n"); break;
        default: serial_print("Unknown result\n");
        }
  }*/


#define IO_SDHC_ATTRIBS (IO_DEV_ATTR_READ | IO_DEV_ATTR_REMOVE | IO_DEV_ATTR_SEEK | IO_DEV_ATTR_WRITE | IO_DEV_ATTR_BLOCK_MODE)

#define SDHC_XFERTYP_RSPTYP_NO              (0x00)
#define SDHC_XFERTYP_RSPTYP_136             (0x01)
#define SDHC_XFERTYP_RSPTYP_48              (0x02)
#define SDHC_XFERTYP_RSPTYP_48BUSY          (0x03)

#define SDHC_XFERTYP_CMDTYP_ABORT           (0x03)

#define SDHC_PROCTL_EMODE_INVARIANT         (0x02)

#define SDHC_PROCTL_DTW_1BIT                (0x00)
#define SDHC_PROCTL_DTW_4BIT                (0x01)
#define SDHC_PROCTL_DTW_8BIT                (0x10)

#define SDHC_INITIALIZATION_MAX_CNT 100000

/* SDHC commands */
#define SDHC_CMD0                           (0)
#define SDHC_CMD1                           (1)
#define SDHC_CMD2                           (2)
#define SDHC_CMD3                           (3)
#define SDHC_CMD4                           (4)
#define SDHC_CMD5                           (5)
#define SDHC_CMD6                           (6)
#define SDHC_CMD7                           (7)
#define SDHC_CMD8                           (8)
#define SDHC_CMD9                           (9)
#define SDHC_CMD10                          (10)
#define SDHC_CMD11                          (11)
#define SDHC_CMD12                          (12)
#define SDHC_CMD13                          (13)
#define SDHC_CMD15                          (15)
#define SDHC_CMD16                          (16)
#define SDHC_CMD17                          (17)
#define SDHC_CMD18                          (18)
#define SDHC_CMD20                          (20)
#define SDHC_CMD24                          (24)
#define SDHC_CMD25                          (25)
#define SDHC_CMD26                          (26)
#define SDHC_CMD27                          (27)
#define SDHC_CMD28                          (28)
#define SDHC_CMD29                          (29)
#define SDHC_CMD30                          (30)
#define SDHC_CMD32                          (32)
#define SDHC_CMD33                          (33)
#define SDHC_CMD34                          (34)
#define SDHC_CMD35                          (35)
#define SDHC_CMD36                          (36)
#define SDHC_CMD37                          (37)
#define SDHC_CMD38                          (38)
#define SDHC_CMD39                          (39)
#define SDHC_CMD40                          (40)
#define SDHC_CMD42                          (42)
#define SDHC_CMD52                          (52)
#define SDHC_CMD53                          (53)
#define SDHC_CMD55                          (55)
#define SDHC_CMD56                          (56)
#define SDHC_CMD59                          (59)
#define SDHC_CMD60                          (60)
#define SDHC_CMD61                          (61)
#define SDHC_ACMD6                          (0x40 + 6)
#define SDHC_ACMD13                         (0x40 + 13)
#define SDHC_ACMD22                         (0x40 + 22)
#define SDHC_ACMD23                         (0x40 + 23)
#define SDHC_ACMD41                         (0x40 + 41)
#define SDHC_ACMD42                         (0x40 + 42)
#define SDHC_ACMD51                         (0x40 + 51)

#define SDHC_FIFO_BUFFER_SIZE               16
#define SDHC_BLOCK_SIZE                     512

#if defined(__IMXRT1062__)
#define MAKE_REG_MASK(m,s) (((uint32_t)(((uint32_t)(m) << s))))
#define MAKE_REG_GET(x,m,s) (((uint32_t)(((uint32_t)(x)>>s) & m)))
#define MAKE_REG_SET(x,m,s) (((uint32_t)(((uint32_t)(x) & m) << s)))

#define SDHC_BLKATTR_BLKSIZE_MASK MAKE_REG_MASK(0x1FFF,0) //uint32_t)(((n) & 0x1FFF)<<0) // Transfer Block Size Mask
#define SDHC_BLKATTR_BLKSIZE(n)   MAKE_REG_SET(n,0x1FFF,0) //uint32_t)(((n) & 0x1FFF)<<0) // Transfer Block Size
#define SDHC_BLKATTR_BLKCNT_MASK  MAKE_REG_MASK(0x1FFF,16) //((uint32_t)0x1FFF<<16)
#define SDHC_BLKATTR_BLKCNT(n)    MAKE_REG_SET(n,0x1FFF,16) //(uint32_t)(((n) & 0x1FFF)<<16) // Blocks Count For Current Transfer

#define SDHC_XFERTYP_CMDINX(n)  MAKE_REG_SET(n,0x3F,24) //(uint32_t)(((n) & 0x3F)<<24)// Command Index
#define SDHC_XFERTYP_CMDTYP(n)  MAKE_REG_SET(n,0x3,22) //(uint32_t)(((n) & 0x3)<<22)  // Command Type
#define SDHC_XFERTYP_DPSEL    MAKE_REG_MASK(0x1,21) //((uint32_t)0x00200000)    // Data Present Select
#define SDHC_XFERTYP_CICEN    MAKE_REG_MASK(0x1,20) //((uint32_t)0x00100000)    // Command Index Check Enable
#define SDHC_XFERTYP_CCCEN    MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000)    // Command CRC Check Enable
#define SDHC_XFERTYP_RSPTYP(n)  MAKE_REG_SET(n,0x3,16) //(uint32_t)(((n) & 0x3)<<16)  // Response Type Select
#define SDHC_XFERTYP_MSBSEL   MAKE_REG_MASK(0x1,5) //((uint32_t)0x00000020)   // Multi/Single Block Select
#define SDHC_XFERTYP_DTDSEL   MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010)   // Data Transfer Direction Select
#define SDHC_XFERTYP_AC12EN   MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004)   // Auto CMD12 Enable
#define SDHC_XFERTYP_BCEN   MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002)   // Block Count Enable
#define SDHC_XFERTYP_DMAEN    MAKE_REG_MASK(0x3,0) //((uint32_t)0x00000001)   // DMA Enable

#define SDHC_PRSSTAT_DLSL_MASK    MAKE_REG_MASK(0xFF,24)  //((uint32_t)0xFF000000)    // DAT Line Signal Level
#define SDHC_PRSSTAT_CLSL     MAKE_REG_MASK(0x1,23) //((uint32_t)0x00800000)    // CMD Line Signal Level
#define SDHC_PRSSTAT_WPSPL      MAKE_REG_MASK(0x1,19) //
#define SDHC_PRSSTAT_CDPL     MAKE_REG_MASK(0x1,18) //
#define SDHC_PRSSTAT_CINS     MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000)    // Card Inserted
#define SDHC_PRSSTAT_TSCD     MAKE_REG_MASK(0x1,15)
#define SDHC_PRSSTAT_RTR      MAKE_REG_MASK(0x1,12)
#define SDHC_PRSSTAT_BREN     MAKE_REG_MASK(0x1,11) //((uint32_t)0x00000800)    // Buffer Read Enable
#define SDHC_PRSSTAT_BWEN     MAKE_REG_MASK(0x1,10) //((uint32_t)0x00000400)    // Buffer Write Enable
#define SDHC_PRSSTAT_RTA      MAKE_REG_MASK(0x1,9) //((uint32_t)0x00000200)   // Read Transfer Active
#define SDHC_PRSSTAT_WTA      MAKE_REG_MASK(0x1,8) //((uint32_t)0x00000100)   // Write Transfer Active
#define SDHC_PRSSTAT_SDOFF      MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080)   // SD Clock Gated Off Internally
#define SDHC_PRSSTAT_PEROFF     MAKE_REG_MASK(0x1,6) //((uint32_t)0x00000040)   // SDHC clock Gated Off Internally
#define SDHC_PRSSTAT_HCKOFF     MAKE_REG_MASK(0x1,5) //((uint32_t)0x00000020)   // System Clock Gated Off Internally
#define SDHC_PRSSTAT_IPGOFF     MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010)   // Bus Clock Gated Off Internally
#define SDHC_PRSSTAT_SDSTB      MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008)   // SD Clock Stable
#define SDHC_PRSSTAT_DLA      MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004)   // Data Line Active
#define SDHC_PRSSTAT_CDIHB      MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002)   // Command Inhibit (DAT)
#define SDHC_PRSSTAT_CIHB     MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001)   // Command Inhibit (CMD)

#define SDHC_PROTCT_NONEXACT_BLKRD  MAKE_REG_MASK(0x1,30) //
#define SDHC_PROTCT_BURST_LENEN(n)  MAKE_REG_SET(n,0x7,12) //
#define SDHC_PROCTL_WECRM     MAKE_REG_MASK(0x1,26) //((uint32_t)0x04000000)    // Wakeup Event Enable On SD Card Removal
#define SDHC_PROCTL_WECINS      MAKE_REG_MASK(0x1,25) //((uint32_t)0x02000000)    // Wakeup Event Enable On SD Card Insertion
#define SDHC_PROCTL_WECINT      MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000)    // Wakeup Event Enable On Card Interrupt
#define SDHC_PROCTL_RD_DONE_NOBLK MAKE_REG_MASK(0x1,20) //
#define SDHC_PROCTL_IABG      MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000)    // Interrupt At Block Gap
#define SDHC_PROCTL_RWCTL     MAKE_REG_MASK(0x1,18) //((uint32_t)0x00040000)    // Read Wait Control
#define SDHC_PROCTL_CREQ      MAKE_REG_MASK(0x1,17) //((uint32_t)0x00020000)    // Continue Request
#define SDHC_PROCTL_SABGREQ     MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000)    // Stop At Block Gap Request
#define SDHC_PROCTL_DMAS(n)     MAKE_REG_SET(n,0x3,8) //(uint32_t)(((n) & 0x3)<<8)  // DMA Select
#define SDHC_PROCTL_CDSS      MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080)   // Card Detect Signal Selection
#define SDHC_PROCTL_CDTL      MAKE_REG_MASK(0x1,6) //((uint32_t)0x00000040)   // Card Detect Test Level
#define SDHC_PROCTL_EMODE(n)    MAKE_REG_SET(n,0x3,4) //(uint32_t)(((n) & 0x3)<<4)  // Endian Mode
#define SDHC_PROCTL_EMODE_MASK  MAKE_REG_MASK(0x3,4) //(uint32_t)((0x3)<<4) // Endian Mode
#define SDHC_PROCTL_D3CD      MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008)   // DAT3 As Card Detection Pin
#define SDHC_PROCTL_DTW(n)      MAKE_REG_SET(n,0x3,1) //(uint32_t)(((n) & 0x3)<<1)  // Data Transfer Width, 0=1bit, 1=4bit, 2=8bit
#define SDHC_PROCTL_DTW_MASK    MAKE_REG_MASK(0x3,1) //((uint32_t)0x00000006)
#define SDHC_PROCTL_LCTL      MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001)   // LED Control

#define SDHC_SYSCTL_RSTT      MAKE_REG_MASK(0x1,28) //
#define SDHC_SYSCTL_INITA     MAKE_REG_MASK(0x1,27) //((uint32_t)0x08000000)    // Initialization Active
#define SDHC_SYSCTL_RSTD      MAKE_REG_MASK(0x1,26) //((uint32_t)0x04000000)    // Software Reset For DAT Line
#define SDHC_SYSCTL_RSTC      MAKE_REG_MASK(0x1,25) //((uint32_t)0x02000000)    // Software Reset For CMD Line
#define SDHC_SYSCTL_RSTA      MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000)    // Software Reset For ALL
#define SDHC_SYSCTL_DTOCV(n)  MAKE_REG_SET(n,0xF,16) //(uint32_t)(((n) & 0xF)<<16)  // Data Timeout Counter Value
#define SDHC_SYSCTL_DTOCV_MASK    MAKE_REG_MASK(0xF,16) //((uint32_t)0x000F0000)
#define SDHC_SYSCTL_SDCLKFS(n)    MAKE_REG_SET(n,0xFF,8) //(uint32_t)(((n) & 0xFF)<<8)  // SDCLK Frequency Select
#define SDHC_SYSCTL_SDCLKFS_MASK  MAKE_REG_MASK(0xFF,8) //((uint32_t)0x0000FF00)
#define SDHC_SYSCTL_DVS(n)      MAKE_REG_SET(n,0xF,4) //(uint32_t)(((n) & 0xF)<<4)  // Divisor
#define SDHC_SYSCTL_DVS_MASK    MAKE_REG_MASK(0xF,4)  //((uint32_t)0x000000F0)

#define SDHC_SYSCTL_SDCLKEN   ((uint32_t)0x00000008)    // SD Clock Enable
#define SDHC_SYSCTL_PEREN     ((uint32_t)0x00000004)    // Peripheral Clock Enable
#define SDHC_SYSCTL_HCKEN     ((uint32_t)0x00000002)    // System Clock Enable
#define SDHC_SYSCTL_IPGEN     ((uint32_t)0x00000001)    // IPG Clock Enable

#define SDHC_IRQSTAT_DMAE     MAKE_REG_MASK(0x1,28) //((uint32_t)0x10000000)    // DMA Error
#define SDHC_IRQSTAT_TNE      MAKE_REG_MASK(0x1,26) //
#define SDHC_IRQSTAT_AC12E    MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000)    // Auto CMD12 Error
#define SDHC_IRQSTAT_DEBE     MAKE_REG_MASK(0x1,22) //((uint32_t)0x00400000)    // Data End Bit Error
#define SDHC_IRQSTAT_DCE      MAKE_REG_MASK(0x1,21) //((uint32_t)0x00200000)    // Data CRC Error
#define SDHC_IRQSTAT_DTOE     MAKE_REG_MASK(0x1,20) //((uint32_t)0x00100000)    // Data Timeout Error
#define SDHC_IRQSTAT_CIE      MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000)    // Command Index Error
#define SDHC_IRQSTAT_CEBE     MAKE_REG_MASK(0x1,18) //((uint32_t)0x00040000)    // Command End Bit Error
#define SDHC_IRQSTAT_CCE      MAKE_REG_MASK(0x1,17) //((uint32_t)0x00020000)    // Command CRC Error
#define SDHC_IRQSTAT_CTOE     MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000)    // Command Timeout Error
#define SDHC_IRQSTAT_TP       MAKE_REG_MASK(0x1,14) //
#define SDHC_IRQSTAT_RTE      MAKE_REG_MASK(0x1,12) //
#define SDHC_IRQSTAT_CINT     MAKE_REG_MASK(0x1,8) //((uint32_t)0x00000100)   // Card Interrupt
#define SDHC_IRQSTAT_CRM      MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080)   // Card Removal
#define SDHC_IRQSTAT_CINS     MAKE_REG_MASK(0x1,6) //((uint32_t)0x00000040)   // Card Insertion
#define SDHC_IRQSTAT_BRR      MAKE_REG_MASK(0x1,5) //((uint32_t)0x00000020)   // Buffer Read Ready
#define SDHC_IRQSTAT_BWR      MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010)   // Buffer Write Ready
#define SDHC_IRQSTAT_DINT     MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008)   // DMA Interrupt
#define SDHC_IRQSTAT_BGE      MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004)   // Block Gap Event
#define SDHC_IRQSTAT_TC       MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002)   // Transfer Complete
#define SDHC_IRQSTAT_CC       MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001)   // Command Complete

#define SDHC_IRQSTATEN_DMAESEN    MAKE_REG_MASK(0x1,28) //((uint32_t)0x10000000)    // DMA Error Status Enable
#define SDHC_IRQSTATEN_TNESEN   MAKE_REG_MASK(0x1,26) //
#define SDHC_IRQSTATEN_AC12ESEN   MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000)    // Auto CMD12 Error Status Enable
#define SDHC_IRQSTATEN_DEBESEN    MAKE_REG_MASK(0x1,22) //((uint32_t)0x00400000)    // Data End Bit Error Status Enable
#define SDHC_IRQSTATEN_DCESEN   MAKE_REG_MASK(0x1,21) //((uint32_t)0x00200000)    // Data CRC Error Status Enable
#define SDHC_IRQSTATEN_DTOESEN    MAKE_REG_MASK(0x1,20) //((uint32_t)0x00100000)    // Data Timeout Error Status Enable
#define SDHC_IRQSTATEN_CIESEN   MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000)    // Command Index Error Status Enable
#define SDHC_IRQSTATEN_CEBESEN    MAKE_REG_MASK(0x1,18) //((uint32_t)0x00040000)    // Command End Bit Error Status Enable
#define SDHC_IRQSTATEN_CCESEN   MAKE_REG_MASK(0x1,17) //((uint32_t)0x00020000)    // Command CRC Error Status Enable
#define SDHC_IRQSTATEN_CTOESEN    MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000)    // Command Timeout Error Status Enable
#define SDHC_IRQSTATEN_TPSEN    MAKE_REG_MASK(0x1,14) //
#define SDHC_IRQSTATEN_RTESEN   MAKE_REG_MASK(0x1,12) //
#define SDHC_IRQSTATEN_CINTSEN    MAKE_REG_MASK(0x1,8) //((uint32_t)0x00000100)   // Card Interrupt Status Enable
#define SDHC_IRQSTATEN_CRMSEN   MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080)   // Card Removal Status Enable
#define SDHC_IRQSTATEN_CINSEN   MAKE_REG_MASK(0x1,6) //((uint32_t)0x00000040)   // Card Insertion Status Enable
#define SDHC_IRQSTATEN_BRRSEN   MAKE_REG_MASK(0x1,5) //((uint32_t)0x00000020)   // Buffer Read Ready Status Enable
#define SDHC_IRQSTATEN_BWRSEN   MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010)   // Buffer Write Ready Status Enable
#define SDHC_IRQSTATEN_DINTSEN    MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008)   // DMA Interrupt Status Enable
#define SDHC_IRQSTATEN_BGESEN   MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004)   // Block Gap Event Status Enable
#define SDHC_IRQSTATEN_TCSEN    MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002)   // Transfer Complete Status Enable
#define SDHC_IRQSTATEN_CCSEN    MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001)   // Command Complete Status Enable

#define SDHC_IRQSIGEN_DMAEIEN   MAKE_REG_MASK(0x1,28) //((uint32_t)0x10000000)    // DMA Error Interrupt Enable
#define SDHC_IRQSIGEN_TNEIEN    MAKE_REG_MASK(0x1,26) //
#define SDHC_IRQSIGEN_AC12EIEN    MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000)    // Auto CMD12 Error Interrupt Enable
#define SDHC_IRQSIGEN_DEBEIEN   MAKE_REG_MASK(0x1,22) //((uint32_t)0x00400000)    // Data End Bit Error Interrupt Enable
#define SDHC_IRQSIGEN_DCEIEN    MAKE_REG_MASK(0x1,21) //((uint32_t)0x00200000)    // Data CRC Error Interrupt Enable
#define SDHC_IRQSIGEN_DTOEIEN   MAKE_REG_MASK(0x1,20) //((uint32_t)0x00100000)    // Data Timeout Error Interrupt Enable
#define SDHC_IRQSIGEN_CIEIEN    MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000)    // Command Index Error Interrupt Enable
#define SDHC_IRQSIGEN_CEBEIEN   MAKE_REG_MASK(0x1,18) //((uint32_t)0x00040000)    // Command End Bit Error Interrupt Enable
#define SDHC_IRQSIGEN_CCEIEN    MAKE_REG_MASK(0x1,17) //((uint32_t)0x00020000)    // Command CRC Error Interrupt Enable
#define SDHC_IRQSIGEN_CTOEIEN   MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000)    // Command Timeout Error Interrupt Enable
#define SDHC_IRQSIGEN_TPIEN     MAKE_REG_MASK(0x1,14) //
#define SDHC_IRQSIGEN_RTEIEN    MAKE_REG_MASK(0x1,12) //
#define SDHC_IRQSIGEN_CINTIEN   MAKE_REG_MASK(0x1,8)  //((uint32_t)0x00000100)    // Card Interrupt Interrupt Enable
#define SDHC_IRQSIGEN_CRMIEN    MAKE_REG_MASK(0x1,7)  //((uint32_t)0x00000080)    // Card Removal Interrupt Enable
#define SDHC_IRQSIGEN_CINSIEN   MAKE_REG_MASK(0x1,6)  //((uint32_t)0x00000040)    // Card Insertion Interrupt Enable
#define SDHC_IRQSIGEN_BRRIEN    MAKE_REG_MASK(0x1,5)  //((uint32_t)0x00000020)    // Buffer Read Ready Interrupt Enable
#define SDHC_IRQSIGEN_BWRIEN    MAKE_REG_MASK(0x1,4)  //((uint32_t)0x00000010)    // Buffer Write Ready Interrupt Enable
#define SDHC_IRQSIGEN_DINTIEN   MAKE_REG_MASK(0x1,3)  //((uint32_t)0x00000008)    // DMA Interrupt Interrupt Enable
#define SDHC_IRQSIGEN_BGEIEN    MAKE_REG_MASK(0x1,2)  //((uint32_t)0x00000004)    // Block Gap Event Interrupt Enable
#define SDHC_IRQSIGEN_TCIEN     MAKE_REG_MASK(0x1,1)  //((uint32_t)0x00000002)    // Transfer Complete Interrupt Enable
#define SDHC_IRQSIGEN_CCIEN     MAKE_REG_MASK(0x1,0)  //((uint32_t)0x00000001)    // Command Complete Interrupt Enable

#define SDHC_AC12ERR_SMPLCLK_SEL  MAKE_REG_MASK(0x1,23) //
#define SDHC_AC12ERR_EXEC_TUNING  MAKE_REG_MASK(0x1,22) //
#define SDHC_AC12ERR_CNIBAC12E    MAKE_REG_MASK(0x1,7)  //((uint32_t)0x00000080)    // Command Not Issued By Auto CMD12 Error
#define SDHC_AC12ERR_AC12IE     MAKE_REG_MASK(0x1,4)  //((uint32_t)0x00000010)    // Auto CMD12 Index Error
#define SDHC_AC12ERR_AC12CE     MAKE_REG_MASK(0x1,3)  //((uint32_t)0x00000008)    // Auto CMD12 CRC Error
#define SDHC_AC12ERR_AC12EBE    MAKE_REG_MASK(0x1,2)  //((uint32_t)0x00000004)    // Auto CMD12 End Bit Error
#define SDHC_AC12ERR_AC12TOE    MAKE_REG_MASK(0x1,1)  //((uint32_t)0x00000002)    // Auto CMD12 Timeout Error
#define SDHC_AC12ERR_AC12NE     MAKE_REG_MASK(0x1,0)  //((uint32_t)0x00000001)    // Auto CMD12 Not Executed

#define SDHC_HTCAPBLT_VS18      MAKE_REG_MASK(0x1,26) //
#define SDHC_HTCAPBLT_VS30      MAKE_REG_MASK(0x1,25) //
#define SDHC_HTCAPBLT_VS33      MAKE_REG_MASK(0x1,24) //
#define SDHC_HTCAPBLT_SRS     MAKE_REG_MASK(0x1,23) //
#define SDHC_HTCAPBLT_DMAS      MAKE_REG_MASK(0x1,22) //
#define SDHC_HTCAPBLT_HSS     MAKE_REG_MASK(0x1,21) //
#define SDHC_HTCAPBLT_ADMAS     MAKE_REG_MASK(0x1,20) //
#define SDHC_HTCAPBLT_MBL_VAL   MAKE_REG_GET((USDHC1_HOST_CTRL_CAP),0x7,16) //
#define SDHC_HTCAPBLT_RETUN_MODE  MAKE_REG_GET((USDHC1_HOST_CTRL_CAP),0x3,14) //
#define SDHC_HTCAPBLT_TUNE_SDR50  MAKE_REG_MASK(0x1,13) //
#define SDHC_HTCAPBLT_TIME_RETUN(n) MAKE_REG_SET(n,0xF,8) //

#define SDHC_WML_WR_BRSTLEN_MASK    MAKE_REG_MASK(0x1F,24)  //
#define SDHC_WML_RD_BRSTLEN_MASK    MAKE_REG_MASK(0x1F,8)   //
#define SDHC_WML_WR_WML_MASK      MAKE_REG_MASK(0xFF,16)  //
#define SDHC_WML_RD_WML_MASK      MAKE_REG_MASK(0xFF,0)   //
#define SDHC_WML_WR_BRSTLEN(n)    MAKE_REG_SET(n,0x1F,24) //(uint32_t)(((n) & 0x7F)<<16)  // Write Burst Len
#define SDHC_WML_RD_BRSTLEN(n)    MAKE_REG_SET(n,0x1F,8)  //(uint32_t)(((n) & 0x7F)<<0)   // Read Burst Len
#define SDHC_WML_WR_WML(n)      MAKE_REG_SET(n,0xFF,16) //(uint32_t)(((n) & 0x7F)<<16)  // Write Watermark Level
#define SDHC_WML_RD_WML(n)      MAKE_REG_SET(n,0xFF,0)  //(uint32_t)(((n) & 0x7F)<<0)   // Read Watermark Level
#define SDHC_WML_WRWML(n)     MAKE_REG_SET(n,0xFF,16) //(uint32_t)(((n) & 0x7F)<<16)  // Write Watermark Level
#define SDHC_WML_RDWML(n)     MAKE_REG_SET(n,0xFF,0)  //(uint32_t)(((n) & 0x7F)<<0)   // Read Watermark Level

// Teensy 4.0 only
#define SDHC_MIX_CTRL_DMAEN     MAKE_REG_MASK(0x1,0)  //
#define SDHC_MIX_CTRL_BCEN      MAKE_REG_MASK(0x1,1)  //
#define SDHC_MIX_CTRL_AC12EN    MAKE_REG_MASK(0x1,2)  //
#define SDHC_MIX_CTRL_DDR_EN    MAKE_REG_MASK(0x1,3)  //
#define SDHC_MIX_CTRL_DTDSEL    MAKE_REG_MASK(0x1,4)  //
#define SDHC_MIX_CTRL_MSBSEL    MAKE_REG_MASK(0x1,5)  //
#define SDHC_MIX_CTRL_NIBBLE_POS  MAKE_REG_MASK(0x1,6)  //
#define SDHC_MIX_CTRL_AC23EN    MAKE_REG_MASK(0x1,7)  //

#define SDHC_FEVT_CINT        MAKE_REG_MASK(0x1,31) //((uint32_t)0x80000000)    // Force Event Card Interrupt
#define SDHC_FEVT_DMAE        MAKE_REG_MASK(0x1,28) //((uint32_t)0x10000000)    // Force Event DMA Error
#define SDHC_FEVT_AC12E       MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000)    // Force Event Auto CMD12 Error
#define SDHC_FEVT_DEBE        MAKE_REG_MASK(0x1,22) //((uint32_t)0x00400000)    // Force Event Data End Bit Error
#define SDHC_FEVT_DCE       MAKE_REG_MASK(0x1,21) //((uint32_t)0x00200000)    // Force Event Data CRC Error
#define SDHC_FEVT_DTOE        MAKE_REG_MASK(0x1,20) //((uint32_t)0x00100000)    // Force Event Data Timeout Error
#define SDHC_FEVT_CIE       MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000)    // Force Event Command Index Error
#define SDHC_FEVT_CEBE        MAKE_REG_MASK(0x1,18) //((uint32_t)0x00040000)    // Force Event Command End Bit Error
#define SDHC_FEVT_CCE       MAKE_REG_MASK(0x1,17) //((uint32_t)0x00020000)    // Force Event Command CRC Error
#define SDHC_FEVT_CTOE        MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000)    // Force Event Command Timeout Error
#define SDHC_FEVT_CNIBAC12E     MAKE_REG_MASK(0x1,7)  //((uint32_t)0x00000080)    // Force Event Command Not Executed By Auto Command 12 Error
#define SDHC_FEVT_AC12IE      MAKE_REG_MASK(0x1,4)  //((uint32_t)0x00000010)    // Force Event Auto Command 12 Index Error
#define SDHC_FEVT_AC12EBE     MAKE_REG_MASK(0x1,3)  //((uint32_t)0x00000008)    // Force Event Auto Command 12 End Bit Error
#define SDHC_FEVT_AC12CE      MAKE_REG_MASK(0x1,2)  //((uint32_t)0x00000004)    // Force Event Auto Command 12 CRC Error
#define SDHC_FEVT_AC12TOE     MAKE_REG_MASK(0x1,1)  //((uint32_t)0x00000002)    // Force Event Auto Command 12 Time Out Error
#define SDHC_FEVT_AC12NE      MAKE_REG_MASK(0x1,0)  //((uint32_t)0x00000001)    // Force Event Auto Command 12 Not Executed

#define SDHC_ADMAES_ADMADCE     MAKE_REG_MASK(0x1,3)  //((uint32_t)0x00000008)
#define SDHC_ADMAES_ADMALME     MAKE_REG_MASK(0x1,2)  //((uint32_t)0x00000004)
#define SDHC_ADMAES_ADMAES_MASK   MAKE_REG_MASK(0x3,0)  //((uint32_t)0x00000003)

#define SDHC_MMCBOOT_BOOTBLKCNT(n)  MAKE_REG_MASK(0xFF,16)  //(uint32_t)(((n) & 0xFFF)<<16) // stop at block gap value of automatic mode
#define SDHC_MMCBOOT_AUTOSABGEN   MAKE_REG_MASK(0x1,7)  //((uint32_t)0x00000080)    // enable auto stop at block gap function
#define SDHC_MMCBOOT_BOOTEN     MAKE_REG_MASK(0x1,6)  //((uint32_t)0x00000040)    // Boot Mode Enable
#define SDHC_MMCBOOT_BOOTMODE   MAKE_REG_MASK(0x1,5)  //((uint32_t)0x00000020)    // Boot Mode Select
#define SDHC_MMCBOOT_BOOTACK    MAKE_REG_MASK(0x1,4)  //((uint32_t)0x00000010)    // Boot Ack Mode Select
#define SDHC_MMCBOOT_DTOCVACK(n)  MAKE_REG_MASK(0xF,0)  //(uint32_t)(((n) & 0xF)<<0)  // Boot ACK Time Out Counter Value
//#define SDHC_HOSTVER    (*(volatile uint32_t *)0x400B10FC) // Host Controller Version

#define CCM_ANALOG_PFD_528_PFD0_FRAC_MASK 0x3f
#define CCM_ANALOG_PFD_528_PFD0_FRAC(n) ((n) & CCM_ANALOG_PFD_528_PFD0_FRAC_MASK)
#define CCM_ANALOG_PFD_528_PFD1_FRAC_MASK (0x3f<<8)
#define CCM_ANALOG_PFD_528_PFD1_FRAC(n) (((n)<<8) & CCM_ANALOG_PFD_528_PFD1_FRAC_MASK)
#define CCM_ANALOG_PFD_528_PFD2_FRAC_MASK (0x3f<<16)
#define CCM_ANALOG_PFD_528_PFD2_FRAC(n) (((n)<<16) & CCM_ANALOG_PFD_528_PFD2_FRAC_MASK)
#define CCM_ANALOG_PFD_528_PFD3_FRAC_MASK ((0x3f<<24)
#define CCM_ANALOG_PFD_528_PFD3_FRAC(n) (((n)<<24) & CCM_ANALOG_PFD_528_PFD3_FRAC_MASK)

#define SDHC_DSADDR       (USDHC1_DS_ADDR ) // DMA System Address register
#define SDHC_BLKATTR      (USDHC1_BLK_ATT) // Block Attributes register
#define SDHC_CMDARG       (USDHC1_CMD_ARG) // Command Argument register
#define SDHC_XFERTYP      (USDHC1_CMD_XFR_TYP) // Transfer Type register
#define SDHC_CMDRSP0      (USDHC1_CMD_RSP0) // Command Response 0
#define SDHC_CMDRSP1      (USDHC1_CMD_RSP1) // Command Response 1
#define SDHC_CMDRSP2      (USDHC1_CMD_RSP2) // Command Response 2
#define SDHC_CMDRSP3      (USDHC1_CMD_RSP3) // Command Response 3
#define SDHC_DATPORT      (USDHC1_DATA_BUFF_ACC_PORT) // Buffer Data Port register
#define SDHC_PRSSTAT      (USDHC1_PRES_STATE) // Present State register
#define SDHC_PROCTL       (USDHC1_PROT_CTRL) // Protocol Control register
#define SDHC_SYSCTL       (USDHC1_SYS_CTRL) // System Control register
#define SDHC_IRQSTAT      (USDHC1_INT_STATUS) // Interrupt Status register
#define SDHC_IRQSTATEN      (USDHC1_INT_STATUS_EN) // Interrupt Status Enable register
#define SDHC_IRQSIGEN     (USDHC1_INT_SIGNAL_EN) // Interrupt Signal Enable register
#define SDHC_AC12ERR      (USDHC1_AUTOCMD12_ERR_STATUS) // Auto CMD12 Error Status Register
#define SDHC_HTCAPBLT     (USDHC1_HOST_CTRL_CAP) // Host Controller Capabilities
#define SDHC_WML        (USDHC1_WTMK_LVL) // Watermark Level Register
#define SDHC_MIX_CTRL     (USDHC1_MIX_CTRL) // Mixer Control
#define SDHC_FEVT       (USDHC1_FORCE_EVENT) // Force Event register
#define SDHC_ADMAES       (USDHC1_ADMA_ERR_STATUS) // ADMA Error Status register
#define SDHC_ADSADDR      (USDHC1_ADMA_SYS_ADDR) // ADMA System Addressregister
#define SDHC_VENDOR       (USDHC1_VEND_SPEC) // Vendor Specific register
#define SDHC_MMCBOOT      (USDHC1_MMC_BOOT) // MMC Boot register
#define SDHC_VENDOR2    (USDHC2_VEND_SPEC2) // Vendor Specific2 register
//
//#define IRQ_SDHC    IRQ_SDHC1

#define SDHC_MAX_DVS (0xF + 1U)
#define SDHC_MAX_CLKFS (0xFF + 1U)
#define SDHC_PREV_DVS(x) ((x) -= 1U)
#define SDHC_PREV_CLKFS(x, y) ((x) >>= (y))

#define CCM_CSCDR1_USDHC1_CLK_PODF_MASK (0x7<<11)
#define CCM_CSCDR1_USDHC1_CLK_PODF(n) (((n)&0x7)<<11)

#define IOMUXC_SW_PAD_CTL_PAD_SRE     ((0x1<)<0)
#define IOMUXC_SW_PAD_CTL_PAD_PKE     ((0x1)<<12)
#define IOMUXC_SW_PAD_CTL_PAD_PUE       ((0x1)<<13)
#define IOMUXC_SW_PAD_CTL_PAD_HYS       ((0x1)<<16)
#define IOMUXC_SW_PAD_CTL_PAD_SPEED(n)  (((n)&0x3)<<6)
#define IOMUXC_SW_PAD_CTL_PAD_PUS(n)    (((n)&0x3)<<14)
#define IOMUXC_SW_PAD_CTL_PAD_PUS_MASK  ((0x3)<<14)
#define IOMUXC_SW_PAD_CTL_PAD_DSE(n)    (((n)&0x7)<<3)
#define IOMUXC_SW_PAD_CTL_PAD_DSE_MASK  ((0x7)<<3)

#endif // __IMXRT1062__

#define SDHC_IRQSIGEN_DMA_MASK (SDHC_IRQSIGEN_TCIEN | SDHC_IRQSIGEN_DINTIEN | SDHC_IRQSIGEN_DMAEIEN)
#define CARD_STATUS_READY_FOR_DATA  (1UL << 8)

/******************************************************************************
  Types
******************************************************************************/
typedef struct {
  uint8_t  status;
  uint8_t  highCapacity;
  uint8_t  version2;
  uint8_t  tranSpeed;
  uint32_t address;
  uint32_t numBlocks;
  uint32_t lastCardStatus;
} SD_CARD_DESCRIPTOR;

/******************************************************************************
  Global functions
******************************************************************************/

/******************************************************************************
  Private variables
******************************************************************************/

static SD_CARD_DESCRIPTOR sdCardDesc;
static volatile uint32_t dmaDone=0;
//

/******************************************************************************
  Forward declaration of private functions
******************************************************************************/
static void sdhc_setSdclk(uint32_t kHzMax);

static uint8_t SDHC_Init(void);
static void SDHC_InitGPIO(void);
static void SDHC_ReleaseGPIO(void);
//static void SDHC_SetClock(uint32_t sysctl);
static uint32_t SDHC_WaitStatus(uint32_t mask);
static int SDHC_ReadBlock(uint32_t* pData);
static int SDHC_WriteBlock(const uint32_t* pData);
static int SDHC_CMD_Do(uint32_t xfertyp);
static int SDHC_CMD0_GoToIdle(void);
static int SDHC_CMD2_Identify(void);
static int SDHC_CMD3_GetAddress(void);
static int SDHC_ACMD6_SetBusWidth(uint32_t address, uint32_t width);
static int SDHC_CMD7_SelectCard(uint32_t address);
static int SDHC_CMD8_SetInterface(uint32_t cond);
static int SDHC_CMD9_GetParameters(uint32_t address);
static int SDHC_CMD12_StopTransfer(void);
static int SDHC_CMD12_StopTransferWaitForBusy(void);
static int SDHC_CMD16_SetBlockSize(uint32_t block_size);
static int SDHC_CMD17_ReadBlock(uint32_t sector);
static int SDHC_CMD24_WriteBlock(uint32_t sector);
static int SDHC_ACMD41_SendOperationCond(uint32_t cond);

/******************************************************************************

    Public functions

******************************************************************************/
uint8_t SDHC_CardGetType(void)
{
  if (sdCardDesc.status) return 0;
  if (sdCardDesc.version2 == 0) return 1; // SD_CARD_TYPE_SD1
  if (sdCardDesc.highCapacity == 0) return 2; // SD_CARD_TYPE_SD2
  return 3; // SD_CARD_TYPE_SDHC
}

//-----------------------------------------------------------------------------
// initialize the SDHC Controller and SD Card
// returns status of initialization(OK, nonInit, noCard, CardProtected)
uint8_t SDHC_CardInit(void)
{
  uint8_t resS;
  int resR;

  resS = SDHC_Init();

  sdCardDesc.status = resS;
  sdCardDesc.address = 0;
  sdCardDesc.highCapacity = 0;
  sdCardDesc.version2 = 0;
  sdCardDesc.numBlocks = 0;

  if (resS)
    return resS;

  SDHC_IRQSIGEN = 0;

  resR = SDHC_CMD0_GoToIdle();
  if (resR) {
    sdCardDesc.status = SDHC_STATUS_NOINIT;
    return SDHC_STATUS_NOINIT;
  }

  resR = SDHC_CMD8_SetInterface(0x000001AA); // 3.3V and AA check pattern
  if (resR == SDHC_RESULT_OK) {
      if (SDHC_CMDRSP0 != 0x000001AA) {
        sdCardDesc.status = SDHC_STATUS_NOINIT;
        return SDHC_STATUS_NOINIT;
      }
      sdCardDesc.highCapacity = 1;
  } else if (resR == SDHC_RESULT_NO_RESPONSE) {
      // version 1 cards do not respond to CMD8
  } else {
    sdCardDesc.status = SDHC_STATUS_NOINIT;
    return SDHC_STATUS_NOINIT;
  }

  if (SDHC_ACMD41_SendOperationCond(0))  return sdCardDesc.status = SDHC_STATUS_NOINIT;

  if (SDHC_CMDRSP0 & 0x300000) {
    uint32_t condition = 0x00300000;
    if (sdCardDesc.highCapacity) condition |= 0x40000000;
    //
    uint32_t ii = 0;
    do {
      ii++;
      if (SDHC_ACMD41_SendOperationCond(condition)) {
        resS = SDHC_STATUS_NOINIT;
        break;
      }
    } while ((!(SDHC_CMDRSP0 & 0x80000000)) && (ii < SDHC_INITIALIZATION_MAX_CNT));

    if (resS) return resS;

    if ((ii >= SDHC_INITIALIZATION_MAX_CNT) || (!(SDHC_CMDRSP0 & 0x40000000)))
      sdCardDesc.highCapacity = 0;
  }

  // Card identify
  if (SDHC_CMD2_Identify())  return sdCardDesc.status = SDHC_STATUS_NOINIT;

  // Get card address
  if (SDHC_CMD3_GetAddress())  return sdCardDesc.status = SDHC_STATUS_NOINIT;

  sdCardDesc.address = SDHC_CMDRSP0 & 0xFFFF0000;

  // Get card parameters
  if (SDHC_CMD9_GetParameters(sdCardDesc.address))  return sdCardDesc.status = SDHC_STATUS_NOINIT;

  if (!(SDHC_CMDRSP3 & 0x00C00000)) {
    uint32_t read_bl_len, c_size, c_size_mult;

    read_bl_len = (SDHC_CMDRSP2 >> 8) & 0x0F;
    c_size = SDHC_CMDRSP2 & 0x03;
    c_size = (c_size << 10) | (SDHC_CMDRSP1 >> 22);
    c_size_mult = (SDHC_CMDRSP1 >> 7) & 0x07;
    sdCardDesc.numBlocks = (c_size + 1) * (1 << (c_size_mult + 2)) * (1 << (read_bl_len - 9));
  } else {
    uint32_t c_size;
    sdCardDesc.version2 = 1;
    c_size = (SDHC_CMDRSP1 >> 8) & 0x003FFFFF;
    sdCardDesc.numBlocks = (c_size + 1) << 10;
  }

  // Select card
  if (SDHC_CMD7_SelectCard(sdCardDesc.address)) return sdCardDesc.status = SDHC_STATUS_NOINIT;

  // Set Block Size to 512
  // Block Size in SDHC Controller is already set to 512 by SDHC_Init();
  // Set 512 Block size in SD card
  if (SDHC_CMD16_SetBlockSize(SDHC_BLOCK_SIZE))  return sdCardDesc.status = SDHC_STATUS_NOINIT;

  // Set 4 bit data bus width
  if (SDHC_ACMD6_SetBusWidth(sdCardDesc.address, 2))  return sdCardDesc.status = SDHC_STATUS_NOINIT;

  // Set Data bus width also in SDHC controller
  SDHC_PROCTL &= ~SDHC_PROCTL_DTW_MASK;
  SDHC_PROCTL |= SDHC_PROCTL_DTW(SDHC_PROCTL_DTW_4BIT);

  // De-Init GPIO
  SDHC_ReleaseGPIO();

  // Set the SDHC default baud rate
  sdhc_setSdclk(25000);
  // SDHC_SetClock(SDHC_SYSCTL_25MHZ);
  // TODO: use CMD6 and CMD9 to detect if card supports 50 MHz
  // then use CMD4 to configure card to high speed mode,
  // and SDHC_SetClock() for 50 MHz config

  // Init GPIO
  SDHC_InitGPIO();

  return sdCardDesc.status;
}


//-----------------------------------------------------------------------------
// FUNCTION:    SDHC_CardReadBlock (disk_read)
// SCOPE:       SDHC public related function
// DESCRIPTION: Function read block to disk
//
// PARAMETERS:  buff - pointer on buffer where read data should be stored
//              sector - index of sector
//
// RETURNS:     result of operation
//-----------------------------------------------------------------------------
#if 1
// read a block from disk, using polling
//   buff - pointer on buffer where read data should be stored
//   sector - index of start sector
int SDHC_CardReadBlock(void * buff, uint32_t sector)
{
  int result;
  uint32_t* pData = (uint32_t*)buff;

  // Check if this is ready
  if (sdCardDesc.status != 0)
     return SDHC_RESULT_NOT_READY;

  // Convert LBA to uint8_t address if needed
  if (!sdCardDesc.highCapacity)
    sector *= 512;

  SDHC_IRQSTAT = 0xffff;
#if defined(__IMXRT1062__)
  SDHC_MIX_CTRL |= SDHC_MIX_CTRL_DTDSEL;
#endif

  // Just single block mode is needed
  result = SDHC_CMD17_ReadBlock(sector);
  if(result != SDHC_RESULT_OK) return result;
  result = SDHC_ReadBlock(pData);

  // finish up
  while (!(SDHC_IRQSTAT & SDHC_IRQSTAT_TC)) { }  // wait for transfer to complete
  SDHC_IRQSTAT = (SDHC_IRQSTAT_TC | SDHC_IRQSTAT_BRR | SDHC_IRQSTAT_AC12E);

  return result;
}
#else
// read a block from disk, using DMA & interrupts
int SDHC_CardReadBlock(void * buff, uint32_t sector)
{
  int result=0;
  uint32_t* pData = (uint32_t*)buff;
  /* check alignment for DMA */
  if (reinterpret_cast<uintptr_t>(static_cast<const void*>(buff)) % 4) {
    return -1;
  }
//  Serial.print("Sector: "); Serial.println(sector); Serial.flush();
  // Check if this is ready
  if (sdCardDesc.status != 0) return SDHC_RESULT_NOT_READY;

  while ((SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB) || (SDHC_PRSSTAT & SDHC_PRSSTAT_CDIHB));

  // Convert LBA to BYTE address if needed
  if (!sdCardDesc.highCapacity)  sector *= 512;

  // clear status
  SDHC_IRQSTAT = SDHC_IRQSTAT;

  // use dma: disabling polling
  uint32_t irqstat = SDHC_IRQSTATEN;
  irqstat &= ~(SDHC_IRQSTATEN_BRRSEN | SDHC_IRQSTATEN_BWRSEN | SDHC_IRQSTATEN_CCSEN) ;
  irqstat &= ~(SDHC_IRQSTATEN_DCESEN | SDHC_IRQSTATEN_CCESEN) ;
  // enable status
  irqstat |= SDHC_IRQSTATEN_DMAESEN | SDHC_IRQSTATEN_DINTSEN | SDHC_IRQSTATEN_TCSEN ;
  SDHC_IRQSTATEN = irqstat;

  uint32_t sigen = SDHC_IRQSIGEN;
  sigen |= SDHC_IRQSIGEN_DMA_MASK ;

  SDHC_SYSCTL |= SDHC_SYSCTL_HCKEN;
  #if defined(__IMXRT1052__) || defined(__IMXRT1062__)
    SDHC_MIX_CTRL |= SDHC_MIX_CTRL_DTDSEL ; // read
    SDHC_MIX_CTRL |=  SDHC_MIX_CTRL_DMAEN ; // DMA
  #endif

  uint32_t xfertyp = SDHC_XFERTYP_CMDINX(SDHC_CMD17) | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48) | SDHC_XFERTYP_DPSEL
                     | SDHC_XFERTYP_DTDSEL | SDHC_XFERTYP_DMAEN;

  dmaDone=0;
  SDHC_DSADDR  = (uint32_t)buff;
  SDHC_CMDARG = sector;
  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(512);
  SDHC_IRQSIGEN = sigen;
  SDHC_XFERTYP = xfertyp;
  //
  while(!dmaDone);
  SDHC_IRQSTAT &= (SDHC_IRQSTAT_CC | SDHC_IRQSTAT_TC);

  return result;
}
#endif

//-----------------------------------------------------------------------------
// FUNCTION:    SDHC_CardWriteBlock (disk_write)
// SCOPE:       SDHC public related function
// DESCRIPTION: Function write block to disk
//
// PARAMETERS:  buff - pointer on buffer where is stored data
//              sector - index of sector
//
// RETURNS:     result of operation
//-----------------------------------------------------------------------------
#if 1
int SDHC_CardWriteBlock(const void * buff, uint32_t sector)
{
  int result;
  const uint32_t *pData = (const uint32_t *)buff;

  // Check if this is ready
  if (sdCardDesc.status != 0) return SDHC_RESULT_NOT_READY;

  // Convert LBA to uint8_t address if needed
  if(!sdCardDesc.highCapacity)
    sector *= 512;

  //SDHC_IRQSTAT = 0xffff;
  SDHC_IRQSTAT = SDHC_IRQSTAT;
#if defined(__IMXRT1062__)
	SDHC_MIX_CTRL &= ~SDHC_MIX_CTRL_DTDSEL;
#endif

  // Just single block mode is needed
  result = SDHC_CMD24_WriteBlock(sector);
  if (result != SDHC_RESULT_OK) return result;
  result = SDHC_WriteBlock(pData);

  // finish up
  while (!(SDHC_IRQSTAT & SDHC_IRQSTAT_TC)) { }  // wait for transfer to complete
  SDHC_IRQSTAT = (SDHC_IRQSTAT_TC | SDHC_IRQSTAT_BWR | SDHC_IRQSTAT_AC12E);

  return result;
}
#else
int SDHC_CardWriteBlock(const void * buff, uint32_t sector)
{
  int result=0;
  const uint32_t *pData = (const uint32_t *)buff;
  /* check alignment for DMA */
  if (reinterpret_cast<uintptr_t>(static_cast<const void*>(buff)) % 4) {
    return -1;
  }

  // Check if this is ready
  if (sdCardDesc.status != 0) return SDHC_RESULT_NOT_READY;

  while ((SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB) || (SDHC_PRSSTAT & SDHC_PRSSTAT_CDIHB)) ;

  // Convert LBA to uint8_t address if needed
  if (!sdCardDesc.highCapacity) sector *= 512;

  // clear status
  SDHC_IRQSTAT = SDHC_IRQSTAT;

  uint32_t irqstat = SDHC_IRQSTATEN;
  // use dma: disabling polling
  irqstat &= ~(SDHC_IRQSTATEN_BRRSEN | SDHC_IRQSTATEN_BWRSEN | SDHC_IRQSTATEN_CCSEN) ;
  irqstat &= ~(SDHC_IRQSTATEN_DCESEN | SDHC_IRQSTATEN_CCESEN) ;
  // enable status
  irqstat |= /*SDHC_IRQSTATEN_DCESEN | SDHC_IRQSTATEN_CCESEN | */SDHC_IRQSTATEN_DMAESEN ;
  irqstat |= SDHC_IRQSTATEN_DINTSEN | SDHC_IRQSTATEN_TCSEN ;
  SDHC_IRQSTATEN = irqstat;

  uint32_t sigen = SDHC_IRQSIGEN;
  sigen |= SDHC_IRQSIGEN_DMA_MASK ;

  SDHC_SYSCTL |= SDHC_SYSCTL_HCKEN;

  #if defined(__IMXRT1052__)
    SDHC_MIX_CTRL &= ~ SDHC_MIX_CTRL_DTDSEL;  // write
    SDHC_MIX_CTRL |=  SDHC_MIX_CTRL_DMAEN ;   //DMA
  #endif

  uint32_t xfertyp = SDHC_XFERTYP_CMDINX(SDHC_CMD24) | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48) | SDHC_XFERTYP_DPSEL
                     | SDHC_XFERTYP_DMAEN;

  dmaDone=0;
  SDHC_DSADDR  = (uint32_t)buff;
  SDHC_CMDARG = sector;
  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(512);
  SDHC_IRQSIGEN = sigen;
  SDHC_XFERTYP = xfertyp;
  //
  while(!dmaDone);
  SDHC_IRQSTAT &= (SDHC_IRQSTAT_CC | SDHC_IRQSTAT_TC);

  while(SDHC_PRSSTAT & SDHC_PRSSTAT_DLA);

  //check for uSD status
  do
  { while ((SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB) || (SDHC_PRSSTAT & SDHC_PRSSTAT_CDIHB)) ;
    SDHC_IRQSTATEN |= SDHC_IRQSTATEN_CCSEN;
    SDHC_IRQSTAT=SDHC_IRQSTAT;
    // CMD13 to check uSD status
    xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD13) | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48));
    //
    SDHC_CMDARG = sdCardDesc.address;
    SDHC_XFERTYP = xfertyp;
    while(!(SDHC_IRQSTAT & SDHC_IRQSTAT_CC)); SDHC_IRQSTAT &= SDHC_IRQSTAT_CC;
  } while(SDHC_CMDRSP0 & 0x200); // while data?
//  } while(!(SDHC_CMDRSP0 & CARD_STATUS_READY_FOR_DATA ));

  return result;
}
#endif

/******************************************************************************

    Private functions

******************************************************************************/
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
  // Teensy 3.5 & 3.6
  // initialize the SDHC Controller signals
  static void SDHC_InitGPIO(void)
  {
    PORTE_PCR0 = PORT_PCR_MUX(4) | PORT_PCR_PS | PORT_PCR_PE | PORT_PCR_DSE; /* SDHC.D1  */
    PORTE_PCR1 = PORT_PCR_MUX(4) | PORT_PCR_PS | PORT_PCR_PE | PORT_PCR_DSE; /* SDHC.D0  */
    PORTE_PCR2 = PORT_PCR_MUX(4) | PORT_PCR_DSE;                             /* SDHC.CLK */
    PORTE_PCR3 = PORT_PCR_MUX(4) | PORT_PCR_PS | PORT_PCR_PE | PORT_PCR_DSE; /* SDHC.CMD */
    PORTE_PCR4 = PORT_PCR_MUX(4) | PORT_PCR_PS | PORT_PCR_PE | PORT_PCR_DSE; /* SDHC.D3  */
    PORTE_PCR5 = PORT_PCR_MUX(4) | PORT_PCR_PS | PORT_PCR_PE | PORT_PCR_DSE; /* SDHC.D2  */
  }

  // release the SDHC Controller signals
  static void SDHC_ReleaseGPIO(void)
  {
    PORTE_PCR0 = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS;   /* PULLUP SDHC.D1  */
    PORTE_PCR1 = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS;   /* PULLUP SDHC.D0  */
    PORTE_PCR2 = 0;           /* SDHC.CLK */
    PORTE_PCR3 = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS;   /* PULLUP SDHC.CMD */
    PORTE_PCR4 = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; /* PULLUP SDHC.D3  */
    PORTE_PCR5 = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS;   /* PULLUP SDHC.D2  */
  }

  void initClock()
  {
  #ifdef HAS_KINETIS_MPU
    // Allow SDHC Bus Master access.
    MPU_RGDAAC0 |= 0x0C000000;
  #endif
    // Enable SDHC clock.
    SIM_SCGC3 |= SIM_SCGC3_SDHC;
  }

  uint32_t sdhcClock()
  { return F_CPU;
  }

#else
  // Teensy 4.0
  static void SDHC_InitGPIO(void)
  {
      IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_04 = 0; //DAT2  
      IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_05 = 0; //DAT3  
      IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_00 = 0; //CMD   
      //3.3V                                           
      IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_01 = 0; //CLK   
      //GND                                           
      IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_02 = 0; //DAT0 
      IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_03 = 0; //DAT1 
  
      const uint32_t CLOCK_MASK = IOMUXC_SW_PAD_CTL_PAD_PKE |
                                  IOMUXC_SW_PAD_CTL_PAD_DSE(1) |
                                  IOMUXC_SW_PAD_CTL_PAD_SPEED(2);
  
      const uint32_t DATA_MASK = CLOCK_MASK |
                                 (IOMUXC_SW_PAD_CTL_PAD_PUE | IOMUXC_SW_PAD_CTL_PAD_PUS(1));
  
      IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_04 = DATA_MASK;
      IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_05 = DATA_MASK;
      IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_00 = DATA_MASK;
      IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_01 = CLOCK_MASK;
      IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_02 = DATA_MASK;
      IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_03 = DATA_MASK;
  }
  
  static void SDHC_ReleaseGPIO(void)
  {
    IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_04 = 5; //GPIO3_IO16
    IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_05 = 5; //GPIO3_IO17
    IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_00 = 5; //GPIO3_IO12
    //3.3V
    IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_01 = 5; //GPIO3_IO13
    //GND
    IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_02 = 5; //GPIO3_IO14
    IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_03 = 5; //GPIO3_IO15
  }
  
  void initClock()
  {
    /* set PDF_528 PLL2PFD0 */
    CCM_ANALOG_PFD_528 |= (1 << 7);
    CCM_ANALOG_PFD_528 &= ~(0x3F << 0);
    CCM_ANALOG_PFD_528 |= ((24) & 0x3F << 0); // 12 - 35
    CCM_ANALOG_PFD_528 &= ~(1 << 7);
  
    /* Enable USDHC clock. */
    CCM_CCGR6 |= CCM_CCGR6_USDHC1(CCM_CCGR_ON);
    CCM_CSCDR1 &= ~(CCM_CSCDR1_USDHC1_CLK_PODF_MASK);
    //
    //  CCM_CSCMR1 &= ~(CCM_CSCMR1_USDHC1_CLK_SEL);     // PLL2PFD2
    CCM_CSCMR1 |= CCM_CSCMR1_USDHC1_CLK_SEL;          // PLL2PFD0
    CCM_CSCDR1 |= CCM_CSCDR1_USDHC1_CLK_PODF((7)); // &0x7
  
    // for testing
    //CCM_CCOSR = CCM_CCOSR_CLKO1_EN | CCM_CCOSR_CLKO1_DIV(7) | CCM_CCOSR_CLKO1_SEL(1); //(1: SYS_PLL/2)
    //IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_04 = 6; //CCM_CLKO1 (0 is USDHC1_DAT2)
    // for testing
    //CCM_CCOSR |= (CCM_CCOSR_CLKO2_EN | CCM_CCOSR_CLKO2_DIV(7) | CCM_CCOSR_CLKO2_SEL(3)); //(3: usdhc1_clk_root))
    //IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_05 = 6; //CCM_CLKO2 (0 is USDHC1_DAT3)
  }
  
  uint32_t sdhcClock()
  {
    uint32_t divider = ((CCM_CSCDR1 >> 11) & 0x7) + 1;
    uint32_t PLL2PFD0 = (528000000U * 3) / ((CCM_ANALOG_PFD_528 & 0x3F) / 6) / divider;
    return PLL2PFD0;
  }

#endif
/* //may be useful for debugging
static void printRegs()
{
// Serial.print("DS_ADDR: "); Serial.println(SDHC_DSADDR,HEX);        // DMA System Address register
 Serial.print("BLK_ATT:       "); Serial.println(SDHC_BLKATTR,HEX);       // Block Attributes register
// Serial.print("CMD_ARG: "); Serial.println(SDHC_CMDARG,HEX);        // Command Argument register
// Serial.print("CMD_XFR_TYP: "); Serial.println(SDHC_XFERTYP,HEX);    // Transfer Type register
// Serial.print("CMD_RSP0: "); Serial.println(SDHC_CMDRSP0,HEX);       // Command Response 0
// Serial.print("CMD_RSP1: "); Serial.println(SDHC_CMDRSP1,HEX);       // Command Response 1
// Serial.print("CMD_RSP2: "); Serial.println(SDHC_CMDRSP2,HEX);       // Command Response 2
// Serial.print("CMD_RSP3: "); Serial.println(SDHC_CMDRSP3,HEX);       // Command Response 3
// Serial.print("DATA_BUFF_ACC_POR: "); Serial.println(SDHC_DATPORT,HEX); // Buffer Data Port register
 Serial.print("PRES_STATE:    "); Serial.println(SDHC_PRSSTAT,HEX);     // Present State register
 Serial.print("PROT_CTRL:     "); Serial.println(SDHC_PROCTL,HEX);      // Protocol Control register
 Serial.print("SYS_CTRL:      "); Serial.println(SDHC_SYSCTL,HEX);        // System Control register
 Serial.print("INT_STATUS:    "); Serial.println(SDHC_IRQSTAT,HEX);      // Interrupt Status register
 Serial.print("INT_STATUS_EN: "); Serial.println(SDHC_IRQSTATEN,HEX);    // Interrupt Status Enable register
 Serial.print("INT_SIGNAL_EN: "); Serial.println(SDHC_IRQSIGEN,HEX);      // Interrupt Signal Enable register
// Serial.print("AUTOCMD12_ERR_STATUS: "); Serial.println(SDHC_AC12ERR,HEX); // Auto CMD12 Error Status Register
 Serial.print("HOST_CTRL_CAP: "); Serial.println(SDHC_HTCAPBLT,HEX);  // Host Controller Capabilities
 Serial.print("WTMK_LVL:      "); Serial.println(SDHC_WML,HEX);        // Watermark Level Register
 #if defined(__IMXRT1052__)
 Serial.print("MIX_CTRL:      "); Serial.println(SDHC_MIX_CTRL,HEX);     // Mixer Control
 #endif
// Serial.print("FORCE_EVENT:   "); Serial.println(SDHC_FEVT,HEX);      // Force Event register
// Serial.print("ADMA_ERR_STATUS: "); Serial.println(SDHC_ADMAES,HEX); // ADMA Error Status register
// Serial.print("ADMA_SYS_ADDR: "); Serial.println(SDHC_ADSADDR,HEX);  // ADMA System Addressregister
 Serial.print("VEND_SPEC:     "); Serial.println(SDHC_VENDOR,HEX);       // Vendor Specific register
// Serial.print("MMC_BOOT:      "); Serial.println(SDHC_MMCBOOT,HEX);      // MMC Boot register
 #if defined(__IMXRT1052__)
 Serial.print("VEND_SPEC2:    "); Serial.println(SDHC_VENDOR2,HEX);    // Vendor Specific2 register
 #endif
}
*/

static void sdhc_setSdclk(uint32_t kHzMax) {
  const uint32_t DVS_LIMIT = 0X10;
  const uint32_t SDCLKFS_LIMIT = 0X100;
  uint32_t dvs = 1;
  uint32_t sdclkfs = 1;
  uint32_t maxSdclk = 1000 * kHzMax;

  //  uint32_t f_pll = F_CPU;
  uint32_t f_pll = sdhcClock();

  while ((f_pll / (sdclkfs * DVS_LIMIT) > maxSdclk) && (sdclkfs < SDCLKFS_LIMIT)) {
    sdclkfs <<= 1;
  }
  while ((f_pll / (sdclkfs * dvs) > maxSdclk) && (dvs < DVS_LIMIT)) {
    dvs++;
  }
  //uint32_t m_sdClkKhz = f_pll / (1000 * sdclkfs * dvs);

  sdclkfs >>= 1;
  dvs--;

#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
  // Disable SDHC clock.
  SDHC_SYSCTL &= ~SDHC_SYSCTL_SDCLKEN;
#endif

  // Change dividers.
  uint32_t sysctl = SDHC_SYSCTL & ~(SDHC_SYSCTL_DTOCV_MASK
                                    | SDHC_SYSCTL_DVS_MASK | SDHC_SYSCTL_SDCLKFS_MASK);

  SDHC_SYSCTL = sysctl | SDHC_SYSCTL_DTOCV(0x0E) | SDHC_SYSCTL_DVS(dvs)
                | SDHC_SYSCTL_SDCLKFS(sdclkfs);

  // Wait until the SDHC clock is stable.
  while (!(SDHC_PRSSTAT & SDHC_PRSSTAT_SDSTB)) { }

#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
  // Enable the SDHC clock.
  SDHC_SYSCTL |= SDHC_SYSCTL_SDCLKEN;
#endif

//  Serial.printf("setSdclk: %d %d : %x %x\n\r", f_pll, m_sdClkKhz, sdclkfs, dvs);
}

#if 0
void sdhc_isr(void)
{ SDHC_IRQSIGEN &= ~SDHC_IRQSIGEN_DMA_MASK;
  //
//  Serial.print("IRQ1: "); Serial.println(SDHC_IRQSTAT,HEX);
  while(!(SDHC_IRQSTAT & SDHC_IRQSTAT_TC));//  SDHC_IRQSTAT &= ~SDHC_IRQSTAT_TC;

  #if defined(__IMXRT1052__)
    SDHC_MIX_CTRL &= ~(SDHC_MIX_CTRL_AC23EN | SDHC_MIX_CTRL_DMAEN) ;  
  #endif
  // for T3.6, seems not to hurt for T4  
  if(SDHC_SYSCTL & SDHC_SYSCTL_HCKEN) SDHC_SYSCTL &=  ~SDHC_SYSCTL_HCKEN;
  SDHC_PROCTL &= ~SDHC_PROCTL_D3CD; SDHC_PROCTL |=  SDHC_PROCTL_D3CD;

  dmaDone=1;
}
#endif

// initialize the SDHC Controller
// returns status of initialization(OK, nonInit, noCard, CardProtected)
static uint8_t SDHC_Init(void)
{
  initClock();

  // De-init GPIO - to prevent unwanted clocks on bus
  SDHC_ReleaseGPIO();

  #if defined (__IMXRT1062__)
    //SDHC_SYSCTL   |= 0xF;
    SDHC_MIX_CTRL = 0x80000000;
  #endif

  /* Reset SDHC */
  SDHC_SYSCTL |= SDHC_SYSCTL_RSTA | SDHC_SYSCTL_SDCLKFS(0x80);
  while (SDHC_SYSCTL & SDHC_SYSCTL_RSTA) ; // wait

  /* Set the SDHC initial baud rate divider and start */
  sdhc_setSdclk(400);

  /* Poll inhibit bits */
  while (SDHC_PRSSTAT & (SDHC_PRSSTAT_CIHB | SDHC_PRSSTAT_CDIHB)) ;

  /* Init GPIO again */
  SDHC_InitGPIO();

  /* Initial values */ // to do - Check values
  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(512);
  //SDHC_PROCTL &= ~SDHC_PROCTL_DMAS(3); // clear ADMA
  //SDHC_PROCTL |=  SDHC_PROCTL_D3CD;
  //SDHC_PROCTL = SDHC_PROCTL_EMODE(SDHC_PROCTL_EMODE_INVARIANT) | SDHC_PROCTL_D3CD;
  SDHC_PROCTL = (SDHC_PROCTL & ~(SDHC_PROCTL_EMODE(3)))
	| (SDHC_PROCTL_EMODE(SDHC_PROCTL_EMODE_INVARIANT) | SDHC_PROCTL_D3CD );
  //SDHC_WML = SDHC_WML_RDWML(SDHC_FIFO_BUFFER_SIZE) | SDHC_WML_WRWML(SDHC_FIFO_BUFFER_SIZE);
  //Serial.printf("SDHC_WML = %08X\n", SDHC_WML); // prints 08100810 (good)
  //#if defined(__IMXRT1062__)
    //SDHC_VENDOR = 0x2000F801; // (1<<29 | 0x1F<<11 | 1);
    //SDHC_VENDOR2 &= ~(1<<12); //switch off ACMD23 sharing SDMA
  //#endif

  /* Enable requests */
  // clear interrupt status
  SDHC_IRQSTAT = SDHC_IRQSTAT;

#if 1
  SDHC_IRQSTATEN = SDHC_IRQSTATEN_DMAESEN | SDHC_IRQSTATEN_AC12ESEN | SDHC_IRQSTATEN_DEBESEN |
	SDHC_IRQSTATEN_DCESEN | SDHC_IRQSTATEN_DTOESEN | SDHC_IRQSTATEN_CIESEN |
	SDHC_IRQSTATEN_CEBESEN | SDHC_IRQSTATEN_CCESEN | SDHC_IRQSTATEN_CTOESEN |
	SDHC_IRQSTATEN_BRRSEN | SDHC_IRQSTATEN_BWRSEN | SDHC_IRQSTATEN_DINTSEN |
	SDHC_IRQSTATEN_CRMSEN | SDHC_IRQSTATEN_TCSEN | SDHC_IRQSTATEN_CCSEN;
#else
  SDHC_IRQSTATEN =  //SDHC_IRQSTAT_CRM | SDHC_IRQSTATEN_CIESEN |
                    SDHC_IRQSTATEN_TCSEN | SDHC_IRQSTATEN_CCSEN;

  attachInterruptVector(IRQ_SDHC, sdhc_isr);
  NVIC_SET_PRIORITY(IRQ_SDHC, 6 * 16);
  NVIC_ENABLE_IRQ(IRQ_SDHC);
#endif

  // initial clocks... SD spec says only 74 clocks are needed, but if Teensy rebooted
  // while the card was in middle of an operation, thousands of clock cycles can be
  // needed to get the card to complete a prior command and return to a usable state.
  for (int ii = 0; ii < 1500; ii++) {
    SDHC_SYSCTL |= SDHC_SYSCTL_INITA;
    while (SDHC_SYSCTL & SDHC_SYSCTL_INITA) ;
  }

  // to do - check if this needed
  SDHC_IRQSTAT |= SDHC_IRQSTAT_CRM;
  // Check card
  if (SDHC_PRSSTAT & SDHC_PRSSTAT_CINS) {
    return 0;
  } else {
    return SDHC_STATUS_NODISK;
  }
}

/******************************************************************************

    Private SD Card functions

******************************************************************************/

// waits for status bits sets
static uint32_t SDHC_WaitStatus(uint32_t mask)
{
  uint32_t             result;
  uint32_t             timeout = 1 << 24;
  do
  { result = SDHC_IRQSTAT & mask;
    timeout--;
  } while (!result && (timeout));
  if (timeout) return result;
  return 0;
}

// reads one block
static int SDHC_ReadBlock(uint32_t* pData)
{
	uint32_t i, irqstat;
	const uint32_t i_max = ((SDHC_BLOCK_SIZE) / (4 * SDHC_FIFO_BUFFER_SIZE));

	for (i = 0; i < i_max; i++) {
		irqstat = SDHC_IRQSTAT;
		SDHC_IRQSTAT = irqstat | SDHC_IRQSTAT_BRR;
		if (irqstat & (SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE)) {
			SDHC_IRQSTAT = irqstat | SDHC_IRQSTAT_BRR |
				SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE;
			SDHC_CMD12_StopTransferWaitForBusy();
			return SDHC_RESULT_ERROR;
		}
		while (!(SDHC_PRSSTAT & SDHC_PRSSTAT_BREN)) { };
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
		*pData++ = SDHC_DATPORT;
	}
	return SDHC_RESULT_OK;
}

// writes one block
static int SDHC_WriteBlock(const uint32_t* pData)
{
	uint32_t i, i_max, j;
	i_max = ((SDHC_BLOCK_SIZE) / (4 * SDHC_FIFO_BUFFER_SIZE));

	for(i = 0; i < i_max; i++) {
		while (!(SDHC_IRQSTAT & SDHC_IRQSTAT_BWR)) ; // wait
		if (SDHC_IRQSTAT & (SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE)) {
			SDHC_IRQSTAT |= SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE |
				SDHC_IRQSTAT_DTOE | SDHC_IRQSTAT_BWR;
			(void)SDHC_CMD12_StopTransferWaitForBusy();
			return SDHC_RESULT_ERROR;
		}
		for(j=0; j<SDHC_FIFO_BUFFER_SIZE; j++) {
			SDHC_DATPORT = *pData++;
		}
		SDHC_IRQSTAT |= SDHC_IRQSTAT_BWR;

		if (SDHC_IRQSTAT & (SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE)) {
			SDHC_IRQSTAT |= SDHC_IRQSTAT_DEBE | SDHC_IRQSTAT_DCE |
				SDHC_IRQSTAT_DTOE | SDHC_IRQSTAT_BWR;
			(void)SDHC_CMD12_StopTransferWaitForBusy();
			return SDHC_RESULT_ERROR;
		}
	}
	return SDHC_RESULT_OK;
}

// sends the command to SDcard
static int SDHC_CMD_Do(uint32_t xfertyp)
{
  // Card removal check preparation
  SDHC_IRQSTAT |= SDHC_IRQSTAT_CRM;

  // Wait for cmd line idle // to do timeout PRSSTAT[CDIHB] and the PRSSTAT[CIHB]
  while ((SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB) || (SDHC_PRSSTAT & SDHC_PRSSTAT_CDIHB)) { };
  SDHC_XFERTYP = xfertyp;

  /* Wait for response */
  const uint32_t mask = SDHC_IRQSTAT_CIE | SDHC_IRQSTAT_CEBE | SDHC_IRQSTAT_CCE | SDHC_IRQSTAT_CC;
  if (SDHC_WaitStatus(mask) != SDHC_IRQSTAT_CC) {
      //SDHC_IRQSTAT |= mask;
      SDHC_IRQSTAT |= (mask | SDHC_IRQSTAT_CTOE);
      return SDHC_RESULT_ERROR;
  }
  /* Check card removal */
  if (SDHC_IRQSTAT & SDHC_IRQSTAT_CRM) {
      SDHC_IRQSTAT |= SDHC_IRQSTAT_CTOE | SDHC_IRQSTAT_CC;
      return SDHC_RESULT_NOT_READY;
  }

  /* Get response, if available */
  if (SDHC_IRQSTAT & SDHC_IRQSTAT_CTOE) {
      SDHC_IRQSTAT |= SDHC_IRQSTAT_CTOE | SDHC_IRQSTAT_CC;
      return SDHC_RESULT_NO_RESPONSE;
  }
  SDHC_IRQSTAT |= SDHC_IRQSTAT_CC;

  return SDHC_RESULT_OK;
}

// sends CMD0 to put SDCARD to idle
static int SDHC_CMD0_GoToIdle(void)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = 0;

  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD0) | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_NO));

  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) { (void)SDHC_CMDRSP0; }
  return result;
}

// sends CMD2 to identify card
static int SDHC_CMD2_Identify(void)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = 0;

  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD2) | SDHC_XFERTYP_CCCEN
            | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_136));

  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) { (void)SDHC_CMDRSP0; }

  return result;
}

// sends CMD 3 to get address
static int SDHC_CMD3_GetAddress(void)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = 0;

  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD3) | SDHC_XFERTYP_CICEN |
             SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48));

  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) { (void)SDHC_CMDRSP0; }
  return result;
}

// sends ACMD6 to set bus width
static int SDHC_ACMD6_SetBusWidth(uint32_t address, uint32_t width)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = address;
  // first send CMD 55 Application specific command
  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD55) | SDHC_XFERTYP_CICEN |
             SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48));

  result = SDHC_CMD_Do(xfertyp);
  if (result == SDHC_RESULT_OK) { (void)SDHC_CMDRSP0;} else { return result; }
  SDHC_CMDARG = width;

  // Send CMD6
  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD6) | SDHC_XFERTYP_CICEN |
             SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48));

  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) {  (void)SDHC_CMDRSP0; }
  return result;
}


// sends CMD 7 to select card
static int SDHC_CMD7_SelectCard(uint32_t address)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = address;

  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD7) | SDHC_XFERTYP_CICEN |
             SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48BUSY));

  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) {(void)SDHC_CMDRSP0; }
  return result;
}

// CMD8 to send interface condition
static int SDHC_CMD8_SetInterface(uint32_t cond)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = cond;

  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD8) | SDHC_XFERTYP_CICEN |
             SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48));

  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) { (void)SDHC_CMDRSP0; }
  return result;
}

// sends CMD 9 to get interface condition
static int SDHC_CMD9_GetParameters(uint32_t address)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = address;

  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD9) | SDHC_XFERTYP_CCCEN |
             SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_136));

  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) {
    //(void)SDHC_CMDRSP0;
    sdCardDesc.tranSpeed = SDHC_CMDRSP2 >> 24;
  }

  return result;
}

// sends CMD12 to stop transfer
static int SDHC_CMD12_StopTransfer(void)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = 0;
  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD12) | SDHC_XFERTYP_CMDTYP(SDHC_XFERTYP_CMDTYP_ABORT) |
             SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48BUSY));

  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) { }
  return result;
}

// sends CMD12 to stop transfer and first waits to ready SDCArd
static int SDHC_CMD12_StopTransferWaitForBusy(void)
{
  uint32_t timeOut = 1000;
  int result;
  do {
    result = SDHC_CMD12_StopTransfer();
    timeOut--;
  } while (timeOut && (SDHC_PRSSTAT & SDHC_PRSSTAT_DLA) && result == SDHC_RESULT_OK);

  if (result != SDHC_RESULT_OK)  return result;
  if (!timeOut)  return SDHC_RESULT_NO_RESPONSE;

  return SDHC_RESULT_OK;
}

// sends CMD16 to set block size
static int SDHC_CMD16_SetBlockSize(uint32_t block_size)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = block_size;

  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD16) | SDHC_XFERTYP_CICEN |
             SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48));

  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) { (void)SDHC_CMDRSP0; }

  return result;
}

// sends CMD17 to read one block
static int SDHC_CMD17_ReadBlock(uint32_t sector)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = sector;

  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | 512;

  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD17) | SDHC_XFERTYP_CICEN |
             SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48) |
             SDHC_XFERTYP_DTDSEL | SDHC_XFERTYP_DPSEL);

  result = SDHC_CMD_Do(xfertyp);
  if (result == SDHC_RESULT_OK) { ( void)SDHC_CMDRSP0; }

  return result;
}

// sends CMD24 to write one block
static int SDHC_CMD24_WriteBlock(uint32_t sector)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = sector;
  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | 512;

  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD24) | SDHC_XFERTYP_CICEN |
             SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48) |
             SDHC_XFERTYP_DPSEL);

  result = SDHC_CMD_Do(xfertyp);
  if (result == SDHC_RESULT_OK) { (void)SDHC_CMDRSP0; }

  return result;
}

// ACMD 41 to send operation condition
static int SDHC_ACMD41_SendOperationCond(uint32_t cond)
{
  uint32_t xfertyp;
  int result;

  SDHC_CMDARG = 0;
  // first send CMD 55 Application specific command
  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_CMD55) | SDHC_XFERTYP_CICEN |
             SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48));

  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) { (void)SDHC_CMDRSP0; } else { return result; }

  SDHC_CMDARG = cond;

  // Send 41CMD
  xfertyp = (SDHC_XFERTYP_CMDINX(SDHC_ACMD41) | SDHC_XFERTYP_RSPTYP(SDHC_XFERTYP_RSPTYP_48));
  result = SDHC_CMD_Do(xfertyp);

  if (result == SDHC_RESULT_OK) { (void)SDHC_CMDRSP0; }

  return result;
}
#endif // __MK64FX512__ or __MK66FX1M0__ or __IMXRT1052__
