diff --git a/miosix/arch/common/drivers/sd_stm32l4.cpp b/miosix/arch/common/drivers/sd_stm32l4.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7a619ae676550acf9a9bcffd735466945dddff5f
--- /dev/null
+++ b/miosix/arch/common/drivers/sd_stm32l4.cpp
@@ -0,0 +1,1140 @@
+
+#include "sd_stm32l4.h"
+#include "interfaces/bsp.h"
+#include "interfaces/arch_registers.h"
+#include "core/cache_cortexMx.h"
+#include "kernel/scheduler/scheduler.h"
+#include "interfaces/delays.h"
+#include "kernel/kernel.h"
+#include "board_settings.h" //For sdVoltage and SD_ONE_BIT_DATABUS definitions
+#include <cstdio>
+#include <cstring>
+#include <errno.h>
+
+
+
+//Note: enabling debugging might cause deadlock when using sleep() or reboot()
+//The bug won't be fixed because debugging is only useful for driver development
+///\internal Debug macro, for normal conditions
+//#define DBG iprintf
+#define DBG(x,...) do {} while(0)
+///\internal Debug macro, for errors only
+//#define DBGERR iprintf
+#define DBGERR(x,...) do {} while(0)
+
+void __attribute__((naked)) SDMMC1_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _ZN6miosix12SDMMCirqImplEv");
+    restoreContext();
+}
+
+
+
+namespace miosix {
+
+static volatile bool transferError; ///< \internal DMA or SDIO transfer error
+static Thread *waiting;             ///< \internal Thread waiting for transfer
+static unsigned int dmaFlags;       ///< \internal DMA status flags
+static unsigned int sdioFlags;      ///< \internal SDIO status flags
+
+
+
+/**
+ * \internal
+ * DMA2 Stream3 interrupt handler actual implementation
+ */
+void __attribute__((used)) SDMMCirqImpl()
+{
+    sdioFlags=SDMMC1->STA;
+
+    if(sdioFlags & (SDMMC_STA_RXOVERR  |
+                    SDMMC_STA_TXUNDERR | SDMMC_STA_DTIMEOUT | SDMMC_STA_DCRCFAIL | SDMMC_STA_DABORT | SDMMC_STA_IDMATE))
+        transferError=true;
+
+
+    //Changed: Old value was 0x7ff
+    SDMMC1->ICR=0x1fe00fff;//Clear flags
+    
+    if(!waiting) return;
+    waiting->IRQwakeup();
+	if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+		Scheduler::IRQfindNextThread();
+    waiting=0;
+}
+
+/*
+ * Operating voltage of device. It is sent to the SD card to check if it can
+ * work at this voltage. Range *must* be within 28..36
+ * Example 33=3.3v
+ */
+//static const unsigned char sdVoltage=33; //Is defined in board_settings.h
+static const unsigned int sdVoltageMask=1<<(sdVoltage-13); //See OCR reg in SD spec
+
+/**
+ * \internal
+ * Possible state of the cardType variable.
+ */
+enum CardType
+{
+    Invalid=0, ///<\internal Invalid card type
+    MMC=1<<0,  ///<\internal if(cardType==MMC) card is an MMC
+    SDv1=1<<1, ///<\internal if(cardType==SDv1) card is an SDv1
+    SDv2=1<<2, ///<\internal if(cardType==SDv2) card is an SDv2
+    SDHC=1<<3  ///<\internal if(cardType==SDHC) card is an SDHC
+};
+
+///\internal Type of card.
+static CardType cardType=Invalid;
+
+//SD card GPIOs
+typedef Gpio<GPIOC_BASE,8>  sdD0;
+typedef Gpio<GPIOC_BASE,9>  sdD1;
+typedef Gpio<GPIOC_BASE,10> sdD2;
+typedef Gpio<GPIOC_BASE,11> sdD3;
+typedef Gpio<GPIOC_BASE,12> sdCLK;
+typedef Gpio<GPIOD_BASE,2>  sdCMD;
+
+//
+// Class CmdResult
+//
+
+/**
+ * \internal
+ * Contains the result of an SD/MMC command
+ */
+class CmdResult
+{
+public:
+
+    /**
+     * \internal
+     * Possible outcomes of sending a command
+     */
+    enum Error
+    {
+        Ok=0,        /// No errors
+        Timeout,     /// Timeout while waiting command reply
+        CRCFail,     /// CRC check failed in command reply
+        RespNotMatch,/// Response index does not match command index
+        ACMDFail     /// Sending CMD55 failed
+    };
+
+    /**
+     * \internal
+     * Default constructor
+     */
+    CmdResult(): cmd(0), error(Ok), response(0) {}
+
+    /**
+     * \internal
+     * Constructor,  set the response data
+     * \param cmd command index of command that was sent
+     * \param result result of command
+     */
+    CmdResult(unsigned char cmd, Error error): cmd(cmd), error(error),
+            response(SDMMC1->RESP1) {}
+
+    /**
+     * \internal
+     * \return the 32 bit of the response.
+     * May not be valid if getError()!=Ok or the command does not send a
+     * response, such as CMD0
+     */
+    unsigned int getResponse() { return response; }
+
+    /**
+     * \internal
+     * \return command index
+     */
+    unsigned char getCmdIndex() { return cmd; }
+
+    /**
+     * \internal
+     * \return the error flags of the response
+     */
+    Error getError() { return error; }
+
+    /**
+     * \internal
+     * Checks if errors occurred while sending the command.
+     * \return true if no errors, false otherwise
+     */
+    bool validateError();
+
+    /**
+     * \internal
+     * interprets this->getResponse() as an R1 response, and checks if there are
+     * errors, or everything is ok
+     * \return true on success, false on failure
+     */
+    bool validateR1Response();
+
+    /**
+     * \internal
+     * Same as validateR1Response, but can be called with interrupts disabled.
+     * \return true on success, false on failure
+     */
+    bool IRQvalidateR1Response();
+
+    /**
+     * \internal
+     * interprets this->getResponse() as an R6 response, and checks if there are
+     * errors, or everything is ok
+     * \return true on success, false on failure
+     */
+    bool validateR6Response();
+
+    /**
+     * \internal
+     * \return the card state from an R1 or R6 resonse
+     */
+    unsigned char getState();
+
+private:
+    unsigned char cmd; ///<\internal Command index that was sent
+    Error error; ///<\internal possible error that occurred
+    unsigned int response; ///<\internal 32bit response
+};
+
+bool CmdResult::validateError()
+{
+    switch(error)
+    {
+        case Ok:
+            return true;
+        case Timeout:
+            DBGERR("CMD%d: Timeout\n",cmd);
+            break;
+        case CRCFail:
+            DBGERR("CMD%d: CRC Fail\n",cmd);
+            break;
+        case RespNotMatch:
+            DBGERR("CMD%d: Response does not match\n",cmd);
+            break;
+        case ACMDFail:
+            DBGERR("CMD%d: ACMD Fail\n",cmd);
+            break;
+    }
+    return false;
+}
+
+bool CmdResult::validateR1Response()
+{
+    if(error!=Ok) return validateError();
+    //Note: this number is obtained with all the flags of R1 which are errors
+    //(flagged as E in the SD specification), plus CARD_IS_LOCKED because
+    //locked card are not supported by this software driver
+    if((response & 0xfff98008)==0) return true;
+    DBGERR("CMD%d: R1 response error(s):\n",cmd);
+    if(response & (1<<31)) DBGERR("Out of range\n");
+    if(response & (1<<30)) DBGERR("ADDR error\n");
+    if(response & (1<<29)) DBGERR("BLOCKLEN error\n");
+    if(response & (1<<28)) DBGERR("ERASE SEQ error\n");
+    if(response & (1<<27)) DBGERR("ERASE param\n");
+    if(response & (1<<26)) DBGERR("WP violation\n");
+    if(response & (1<<25)) DBGERR("card locked\n");
+    if(response & (1<<24)) DBGERR("LOCK_UNLOCK failed\n");
+    if(response & (1<<23)) DBGERR("command CRC failed\n");
+    if(response & (1<<22)) DBGERR("illegal command\n");
+    if(response & (1<<21)) DBGERR("ECC fail\n");
+    if(response & (1<<20)) DBGERR("card controller error\n");
+    if(response & (1<<19)) DBGERR("unknown error\n");
+    if(response & (1<<16)) DBGERR("CSD overwrite\n");
+    if(response & (1<<15)) DBGERR("WP ERASE skip\n");
+    if(response & (1<<3)) DBGERR("AKE_SEQ error\n");
+    return false;
+}
+
+bool CmdResult::IRQvalidateR1Response()
+{
+    if(error!=Ok) return false;
+    if(response & 0xfff98008) return false;
+    return true;
+}
+
+bool CmdResult::validateR6Response()
+{
+    if(error!=Ok) return validateError();
+    if((response & 0xe008)==0) return true;
+    DBGERR("CMD%d: R6 response error(s):\n",cmd);
+    if(response & (1<<15)) DBGERR("command CRC failed\n");
+    if(response & (1<<14)) DBGERR("illegal command\n");
+    if(response & (1<<13)) DBGERR("unknown error\n");
+    if(response & (1<<3)) DBGERR("AKE_SEQ error\n");
+    return false;
+}
+
+unsigned char CmdResult::getState()
+{
+    unsigned char result=(response>>9) & 0xf;
+    DBG("CMD%d: State: ",cmd);
+    switch(result)
+    {
+        case 0:  DBG("Idle\n");  break;
+        case 1:  DBG("Ready\n"); break;
+        case 2:  DBG("Ident\n"); break;
+        case 3:  DBG("Stby\n"); break;
+        case 4:  DBG("Tran\n"); break;
+        case 5:  DBG("Data\n"); break;
+        case 6:  DBG("Rcv\n"); break;
+        case 7:  DBG("Prg\n"); break;
+        case 8:  DBG("Dis\n"); break;
+        case 9:  DBG("Btst\n"); break;
+        default: DBG("Unknown\n"); break;
+    }
+    return result;
+}
+
+//
+// Class Command
+//
+
+/**
+ * \internal
+ * This class allows sending commands to an SD or MMC
+ */
+class Command
+{
+public:
+
+    /**
+     * \internal
+     * SD/MMC commands
+     * - bit #7 is @ 1 if a command is an ACMDxx. send() will send the
+     *   sequence CMD55, CMDxx
+     * - bit from #0 to #5 indicate command index (CMD0..CMD63)
+     * - bit #6 is don't care
+     */
+    enum CommandType
+    {
+        CMD0=0,           //GO_IDLE_STATE
+        CMD2=2,           //ALL_SEND_CID
+        CMD3=3,           //SEND_RELATIVE_ADDR
+        ACMD6=0x80 | 6,   //SET_BUS_WIDTH
+        CMD7=7,           //SELECT_DESELECT_CARD
+        ACMD41=0x80 | 41, //SEND_OP_COND (SD)
+        CMD8=8,           //SEND_IF_COND
+        CMD9=9,           //SEND_CSD
+        CMD12=12,         //STOP_TRANSMISSION
+        CMD13=13,         //SEND_STATUS
+        CMD16=16,         //SET_BLOCKLEN
+        CMD17=17,         //READ_SINGLE_BLOCK
+        CMD18=18,         //READ_MULTIPLE_BLOCK
+        ACMD23=0x80 | 23, //SET_WR_BLK_ERASE_COUNT (SD)
+        CMD24=24,         //WRITE_BLOCK
+        CMD25=25,         //WRITE_MULTIPLE_BLOCK
+        CMD55=55          //APP_CMD
+    };
+
+    /**
+     * \internal
+     * Send a command.
+     * \param cmd command index (CMD0..CMD63) or ACMDxx command
+     * \param arg the 32 bit argument to the command
+     * \return a CmdResult object
+     */
+    static CmdResult send(CommandType cmd, unsigned int arg);
+
+    /**
+     * \internal
+     * Set the relative card address, obtained during initialization.
+     * \param r the card's rca
+     */
+    static void setRca(unsigned short r) { rca=r; }
+
+    /**
+     * \internal
+     * \return the card's rca, as set by setRca
+     */
+    static unsigned int getRca() { return static_cast<unsigned int>(rca); }
+
+private:
+    static unsigned short rca;///<\internal Card's relative address
+};
+
+CmdResult Command::send(CommandType cmd, unsigned int arg)
+{
+    unsigned char cc=static_cast<unsigned char>(cmd);
+    //Handle ACMDxx as CMD55, CMDxx
+    if(cc & 0x80)
+    {
+        DBG("ACMD%d\n",cc & 0x3f);
+        CmdResult r=send(CMD55,(static_cast<unsigned int>(rca))<<16);
+        if(r.validateR1Response()==false)
+            return CmdResult(cc & 0x3f,CmdResult::ACMDFail);
+        //Bit 5 @ 1 = next command will be interpreted as ACMD
+        if((r.getResponse() & (1<<5))==0)
+            return CmdResult(cc & 0x3f,CmdResult::ACMDFail);
+    } else DBG("CMD%d\n",cc & 0x3f);
+
+    //Send command
+    cc &= 0x3f;
+    unsigned int command= SDMMC_CMD_CPSMEN | static_cast<unsigned int>(cc);
+    if(cc!=CMD0) command |= SDMMC_CMD_WAITRESP_0; //CMD0 has no response
+    if(cc==CMD2) command |= SDMMC_CMD_WAITRESP_1; //CMD2 has long response
+    if(cc==CMD9) command |= SDMMC_CMD_WAITRESP_1; //CMD9 has long response
+    SDMMC1->ARG = arg;
+    SDMMC1->CMD = command;
+
+    //CMD0 has no response, so wait until it is sent
+    if(cc==CMD0)
+    {
+        for(int i=0;i<500;i++)
+        {
+            if(SDMMC1->STA & SDMMC_STA_CMDSENT)
+            {
+                SDMMC1->ICR=0x1fe00fff;//Clear flags
+                return CmdResult(cc,CmdResult::Ok);
+            }
+            delayUs(1);
+        }
+        SDMMC1->ICR = 0x1fe00fff;//Clear flags
+        return CmdResult(cc,CmdResult::Timeout);
+    }
+
+    //Command is not CMD0, so wait a reply
+    for(int i=0;i<500;i++)
+    {
+        unsigned int status=SDMMC1->STA;
+        if(status & SDMMC_STA_CMDREND)
+        {
+            SDMMC1->ICR=0x1fe00fff;//Clear flags
+            if(SDMMC1->RESPCMD==cc) return CmdResult(cc,CmdResult::Ok);
+            else return CmdResult(cc,CmdResult::RespNotMatch);
+        }
+        if(status & SDMMC_STA_CCRCFAIL)
+        {
+            SDMMC1->ICR=SDMMC_ICR_CCRCFAILC;
+            return CmdResult(cc,CmdResult::CRCFail);
+        }
+        if(status & SDMMC_STA_CTIMEOUT) break;
+        delayUs(1);
+    }
+    SDMMC1->ICR=SDMMC_ICR_CTIMEOUTC;
+    return CmdResult(cc,CmdResult::Timeout);
+}
+
+unsigned short Command::rca=0;
+
+//
+// Class ClockController
+//
+
+/**
+ * \internal
+ * This class controls the clock speed of the SDIO peripheral. It originated
+ * from a previous version of this driver, where the SDIO was used in polled
+ * mode instead of DMA mode, but has been retained to improve the robustness
+ * of the driver.
+ */
+class ClockController
+{
+public:
+
+    /**
+     * \internal. Set a low clock speed of 400KHz or less, used for
+     * detecting SD/MMC cards. This function as a side effect enables 1bit bus
+     * width, and disables clock powersave, since it is not allowed by SD spec.
+     */
+    static void setLowSpeedClock()
+    {
+        clockReductionAvailable=0;
+        // No hardware flow control, SDIO_CK generated on rising edge, 1bit bus
+        // width, no clock bypass, no powersave.
+        // Set low clock speed 400KHz
+        SDMMC1->CLKCR=CLOCK_400KHz;
+        SDMMC1->DTIMER=240000; //Timeout 600ms expressed in SD_CK cycles
+    }
+
+    /**
+     * \internal
+     * Automatically select the data speed. This routine selects the highest
+     * sustainable data transfer speed. This is done by binary search until
+     * the highest clock speed that causes no errors is found.
+     * This function as a side effect enables 4bit bus width, and clock
+     * powersave.
+     */
+    static void calibrateClockSpeed(SDIODriver *sdio);
+
+    /**
+     * \internal
+     * Since clock speed is set dynamically by binary search at runtime, a
+     * corner case might be that of a clock speed which results in unreliable
+     * data transfer, that sometimes succeeds, and sometimes fail.
+     * For maximum robustness, this function is provided to reduce the clock
+     * speed slightly in case a data transfer should fail after clock
+     * calibration. To avoid inadvertently considering other kind of issues as
+     * clock issues, this function can be called only MAX_ALLOWED_REDUCTIONS
+     * times after clock calibration, subsequent calls will fail. This will
+     * avoid other issues causing an ever decreasing clock speed.
+     * \return true on success, false on failure
+     */
+    static bool reduceClockSpeed();
+
+    /**
+     * \internal
+     * Read and write operation do retry during normal use for robustness, but
+     * during clock claibration they must not retry for speed reasons. This
+     * member function returns 1 during clock claibration and MAX_RETRY during
+     * normal use.
+     */
+    static unsigned char getRetryCount() { return retries; }
+
+private:
+    /**
+     * Set SDIO clock speed
+     * \param clkdiv speed is SDIOCLK/(clkdiv+2) 
+     */
+    static void setClockSpeed(unsigned int clkdiv);
+    
+    static const unsigned int SDIOCLK=48000000; //On stm32f2 SDIOCLK is always 48MHz
+    static const unsigned int CLOCK_400KHz=60; //48MHz/(2*60)=400KHz
+    #ifdef OVERRIDE_SD_CLOCK_DIVIDER_MAX
+    //Some boards using SDRAM cause SDIO TX Underrun occasionally
+    static const unsigned int CLOCK_MAX=OVERRIDE_SD_CLOCK_DIVIDER_MAX;
+    #else //OVERRIDE_SD_CLOCK_DIVIDER_MAX
+    static const unsigned int CLOCK_MAX=0;      //48MHz/(0+2)  =24MHz
+    #endif //OVERRIDE_SD_CLOCK_DIVIDER_MAX
+
+    #ifdef SD_ONE_BIT_DATABUS
+    ///\internal Clock enabled, bus width 1bit, clock powersave enabled.
+    static const unsigned int CLKCR_FLAGS= SDMMC_CLKCR_PWRSAV;
+    #else //SD_ONE_BIT_DATABUS
+    ///\internal Clock enabled, bus width 4bit, clock powersave enabled.
+    static const unsigned int CLKCR_FLAGS= //SDIO_CLKCR_CLKEN |
+        SDMMC_CLKCR_WIDBUS_0 | SDMMC_CLKCR_PWRSAV;
+    #endif //SD_ONE_BIT_DATABUS
+
+    ///\internal Maximum number of calls to IRQreduceClockSpeed() allowed
+    static const unsigned char MAX_ALLOWED_REDUCTIONS=1;
+
+    ///\internal value returned by getRetryCount() while *not* calibrating clock.
+    static const unsigned char MAX_RETRY=10;
+
+    ///\internal Used to allow only one call to reduceClockSpeed()
+    static unsigned char clockReductionAvailable;
+
+    ///\internal value returned by getRetryCount()
+    static unsigned char retries;
+};
+
+void ClockController::calibrateClockSpeed(SDIODriver *sdio)
+{
+
+    //During calibration we call readBlock() which will call reduceClockSpeed()
+    //so not to invalidate calibration clock reduction must not be available
+    clockReductionAvailable=0;
+    retries=1;
+
+    DBG("Automatic speed calibration\n");
+    unsigned int buffer[512/sizeof(unsigned int)];
+    unsigned int minFreq=CLOCK_400KHz;
+    unsigned int maxFreq=CLOCK_MAX;
+    unsigned int selected;
+    while(minFreq-maxFreq>1)
+    {
+        selected=(minFreq+maxFreq)/2;
+        DBG("Trying CLKCR=%d\n",selected);
+        setClockSpeed(selected);
+        if(sdio->readBlock(reinterpret_cast<unsigned char*>(buffer),512,0)==512)
+            minFreq=selected;
+        else maxFreq=selected;
+    }
+    //Last round of algorithm
+    setClockSpeed(maxFreq);
+    if(sdio->readBlock(reinterpret_cast<unsigned char*>(buffer),512,0)==512)
+    {
+        DBG("Optimal CLKCR=%d\n",maxFreq);
+    } else {
+        setClockSpeed(minFreq);
+        DBG("Optimal CLKCR=%d\n",minFreq);
+    }
+
+    //Make clock reduction available
+    clockReductionAvailable=MAX_ALLOWED_REDUCTIONS;
+    retries=MAX_RETRY;
+}
+
+bool ClockController::reduceClockSpeed()
+{
+    DBGERR("clock speed reduction requested\n");
+    //Ensure this function can be called only a few times
+    if(clockReductionAvailable==0) return false;
+    clockReductionAvailable--;
+
+    unsigned int currentClkcr=SDMMC1->CLKCR & 0x3ff;
+    if(currentClkcr==CLOCK_400KHz) return false; //No lower than this value
+
+    //If the value of clockcr is low, increasing it by one is enough since
+    //frequency changes a lot, otherwise increase by 2.
+    if(currentClkcr < 6) currentClkcr++; // was < 10 with the f4
+    else currentClkcr+=2;
+
+    setClockSpeed(currentClkcr);
+    return true;
+}
+
+void ClockController::setClockSpeed(unsigned int clkdiv)
+{
+    SDMMC1->CLKCR = clkdiv | CLKCR_FLAGS;
+    //Timeout 600ms expressed in SD_CK cycles
+    SDMMC1-> DTIMER = (6*SDIOCLK)/((clkdiv == 0 ? 1 : 2 * clkdiv)*10);
+}
+
+unsigned char ClockController::clockReductionAvailable = false;
+unsigned char ClockController::retries = ClockController::MAX_RETRY;
+
+//
+// Data send/receive functions
+//
+
+/**
+ * \internal
+ * Wait until the card is ready for data transfer.
+ * Can be called independently of the card being selected.
+ * \return true on success, false on failure
+ */
+static bool waitForCardReady()
+{
+    const int timeout=1500; //Timeout 1.5 second
+    const int sleepTime=2;
+    for(int i=0;i<timeout/sleepTime;i++) 
+    {
+        CmdResult cr=Command::send(Command::CMD13,Command::getRca()<<16);
+        if(cr.validateR1Response()==false) return false;
+        //Bit 8 in R1 response means ready for data.
+        if(cr.getResponse() & (1<<8)) return true;
+        Thread::sleep(sleepTime);
+    }
+    DBGERR("Timeout waiting card ready\n");
+    return false;
+}
+
+/**
+ * \internal
+ * Prints the errors that may occur during a DMA transfer
+ */
+static void displayBlockTransferError()
+{
+    DBGERR("Block transfer error\n");
+    if(dmaFlags & DMA_ISR_TEIF4)     DBGERR("* DMA Transfer error\n");
+    if(sdioFlags & SDMMC_STA_RXOVERR)  DBGERR("* SDIO RX Overrun\n");
+    if(sdioFlags & SDMMC_STA_TXUNDERR) DBGERR("* SDIO TX Underrun error\n");
+    if(sdioFlags & SDMMC_STA_DCRCFAIL) DBGERR("* SDIO Data CRC fail\n");
+    if(sdioFlags & SDMMC_STA_DTIMEOUT) DBGERR("* SDIO Data timeout\n");
+}
+
+/**
+ * \internal
+ * Contains initial common code between multipleBlockRead and multipleBlockWrite
+ * to clear interrupt and error flags, set the waiting thread.
+ */
+static void dmaTransferCommonSetup(const unsigned char *buffer)
+{
+    //Clear both SDIO and DMA interrupt flags
+    SDMMC1->ICR=0x1fe00fff; //Clear interrupts
+
+    SDMMC1->IDMACTRL = SDMMC_IDMA_IDMAEN & ~(SDMMC_IDMA_IDMABMODE); //Enable dma without double buffer
+    SDMMC1->IDMABASE0 = reinterpret_cast<unsigned int>(buffer); //Set buffer base address (where to read/write)
+    
+    transferError=false;
+    dmaFlags=sdioFlags=0;
+    waiting=Thread::getCurrentThread();
+}
+
+/**
+ * \internal
+ * Read a given number of contiguous 512 byte blocks from an SD/MMC card.
+ * Card must be selected prior to calling this function.
+ * \param buffer, a buffer whose size is 512*nblk bytes
+ * \param nblk number of blocks to read.
+ * \param lba logical block address of the first block to read.
+ */
+static bool multipleBlockRead(unsigned char *buffer, unsigned int nblk,
+    unsigned int lba)
+{
+    if(nblk==0) return true;
+    while(nblk>32767)
+    {
+        if(multipleBlockRead(buffer,32767,lba)==false) return false;
+        buffer+=32767*512;
+        nblk-=32767;
+        lba+=32767;
+    }
+    if(waitForCardReady()==false) return false;
+    
+    if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC
+    
+    dmaTransferCommonSetup(buffer);
+    
+    //Data transfer is considered complete once the DMA transfer complete
+    //interrupt occurs, that happens when the last data was written in the
+    //buffer. Both SDIO and DMA error interrupts are active to catch errors
+    SDMMC1->MASK= SDMMC_MASK_DATAENDIE  |
+               SDMMC_MASK_RXOVERRIE  | //Interrupt on rx underrun
+               SDMMC_MASK_TXUNDERRIE | //Interrupt on tx underrun
+               SDMMC_MASK_DCRCFAILIE | //Interrupt on data CRC fail
+               SDMMC_MASK_DTIMEOUTIE | //Interrupt on data timeout
+               SDMMC_MASK_IDMABTCIE  | //Interrupt on IDMA events
+               SDMMC_MASK_DABORTIE;    //Interrupt on aborted
+    SDMMC1->DLEN=nblk*512;
+
+    if(waiting==0)
+    {
+        DBGERR("Premature wakeup\n");
+        transferError=true;
+    }
+    CmdResult cr=Command::send(nblk>1 ? Command::CMD18 : Command::CMD17,lba);
+    if(cr.validateR1Response())
+    {
+        //Block size 512 bytes, block data xfer, from card to controller
+        //DTMode set to 00 - Block Data Transfer (Not shown here)
+        SDMMC1->DCTRL=(9<<4) | SDMMC_DCTRL_DTDIR | SDMMC_DCTRL_DTEN;
+        DBG("READ STARTED! WAITING FOR INTERRUPT...\n");
+        FastInterruptDisableLock dLock;
+        while(waiting)
+        {
+            Thread::IRQwait();
+            {
+                FastInterruptEnableLock eLock(dLock);
+                Thread::yield();
+            }
+        }
+
+    } else {
+        transferError=true;
+        DBG("TRANSFER ERROR\n");
+    }
+    SDMMC1->DCTRL=0; //Disable data path state machine
+    SDMMC1->MASK=0;
+
+    DBGERR("TRANSFER ERROR: %d\n", transferError);
+
+    // CMD12 is sent to end CMD18 (multiple block read), or to abort an
+    // unfinished read in case of errors
+    if(nblk>1 || transferError) cr=Command::send(Command::CMD12,0);
+    if(transferError || cr.validateR1Response()==false)
+    {
+        displayBlockTransferError();
+        ClockController::reduceClockSpeed();
+        return false;
+    }
+    
+    //Read ok, deal with cache coherence
+    markBufferAfterDmaRead(buffer,nblk*512);
+    return true;
+}
+
+/**
+ * \internal
+ * Write a given number of contiguous 512 byte blocks to an SD/MMC card.
+ * Card must be selected prior to calling this function.
+ * \param buffer, a buffer whose size is 512*nblk bytes
+ * \param nblk number of blocks to write.
+ * \param lba logical block address of the first block to write.
+ */
+static bool multipleBlockWrite(const unsigned char *buffer, unsigned int nblk,
+    unsigned int lba)
+{
+    if(nblk==0) return true;
+    while(nblk>32767)
+    {
+        if(multipleBlockWrite(buffer,32767,lba)==false) return false;
+        buffer+=32767*512;
+        nblk-=32767;
+        lba+=32767;
+    }
+    
+    //Deal with cache coherence
+    markBufferBeforeDmaWrite(buffer,nblk*512);
+    
+    if(waitForCardReady()==false) return false;
+    
+    if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC
+    if(nblk>1)
+    {
+        CmdResult cr=Command::send(Command::ACMD23,nblk);
+        if(cr.validateR1Response()==false) return false;
+    }
+    
+    dmaTransferCommonSetup(buffer);
+    
+    //Data transfer is considered complete once the SDIO transfer complete
+    //interrupt occurs, that happens when the last data was written to the SDIO
+    //Both SDIO and DMA error interrupts are active to catch errors
+    SDMMC1->MASK=SDMMC_MASK_DATAENDIE  | //Interrupt on data end
+               SDMMC_MASK_RXOVERRIE  | //Interrupt on rx underrun
+               SDMMC_MASK_TXUNDERRIE | //Interrupt on tx underrun
+               SDMMC_MASK_DCRCFAILIE | //Interrupt on data CRC fail
+               SDMMC_MASK_DTIMEOUTIE | //Interrupt on data timeout
+               SDMMC_MASK_IDMABTCIE  | //Interrupt on IDMA events
+               SDMMC_MASK_DABORTIE;
+    SDMMC1->DLEN=nblk*512;
+    if(waiting==0)
+    {
+        DBGERR("Premature wakeup\n");
+        transferError=true;
+    }
+    CmdResult cr=Command::send(nblk>1 ? Command::CMD25 : Command::CMD24,lba);
+    if(cr.validateR1Response())
+    {
+        //Block size 512 bytes, block data xfer, from card to controller
+        SDMMC1->DCTRL= ((9<<4) | SDMMC_DCTRL_DTEN) & ~(SDMMC_DCTRL_DTDIR);
+        FastInterruptDisableLock dLock;
+        while(waiting)
+        {
+            Thread::IRQwait();
+            {
+                FastInterruptEnableLock eLock(dLock);
+                Thread::yield();
+            }
+        }
+    } else transferError=true;
+
+    // CMD12 is sent to end CMD25 (multiple block write), or to abort an
+    // unfinished write in case of errors
+    if(nblk>1 || transferError) cr=Command::send(Command::CMD12,0);
+    if(transferError || cr.validateR1Response()==false)
+    {
+        displayBlockTransferError();
+        ClockController::reduceClockSpeed();
+        return false;
+    }
+    return true;
+}
+
+//
+// Class CardSelector
+//
+
+/**
+ * \internal
+ * Simple RAII class for selecting an SD/MMC card an automatically deselect it
+ * at the end of the scope.
+ */
+class CardSelector
+{
+public:
+    /**
+     * \internal
+     * Constructor. Selects the card.
+     * The result of the select operation is available through its succeded()
+     * member function
+     */
+    explicit CardSelector()
+    {
+        success=Command::send(
+                Command::CMD7,Command::getRca()<<16).validateR1Response();
+    }
+
+    /**
+     * \internal
+     * \return true if the card was selected, false on error
+     */
+    bool succeded() { return success; }
+
+    /**
+     * \internal
+     * Destructor, ensures that the card is deselected
+     */
+    ~CardSelector()
+    {
+        Command::send(Command::CMD7,0); //Deselect card. This will timeout
+    }
+
+private:
+    bool success;
+};
+
+//
+// Initialization helper functions
+//
+
+/**
+ * \internal
+ * Initialzes the SDIO peripheral in the STM32
+ */
+static void initSDIOPeripheral()
+{
+    {
+        //Doing read-modify-write on RCC->APBENR2 and gpios, better be safe
+        FastInterruptDisableLock lock;         
+        RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN  
+                      | RCC_AHB2ENR_GPIODEN
+                      | RCC_AHB2ENR_SDMMC1EN;
+
+        //RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
+        RCC->CCIPR |= RCC_CCIPR_CLK48SEL_1;
+        //RCC_SYNC();
+        //RCC_SYNC();
+        sdD0::mode(Mode::ALTERNATE);
+        sdD0::alternateFunction(12);
+        #ifndef SD_ONE_BIT_DATABUS
+        sdD1::mode(Mode::ALTERNATE);
+        sdD1::alternateFunction(12);
+        sdD2::mode(Mode::ALTERNATE);
+        sdD2::alternateFunction(12);
+        sdD3::mode(Mode::ALTERNATE);
+        sdD3::alternateFunction(12);
+        #endif //SD_ONE_BIT_DATABUS
+        sdCLK::mode(Mode::ALTERNATE);
+        sdCLK::alternateFunction(12);
+        sdCMD::mode(Mode::ALTERNATE);
+        sdCMD::alternateFunction(12);
+    }
+    NVIC_SetPriority(SDMMC1_IRQn,15);//Low priority for SDIO
+    NVIC_EnableIRQ(SDMMC1_IRQn);
+    
+    SDMMC1->POWER=0; //Power off state
+    delayUs(1);
+    SDMMC1->CLKCR=0;
+    SDMMC1->CMD=0;
+    SDMMC1->DCTRL=0;
+    SDMMC1->ICR=0x1fe007ff; //Interrupt  //0xc007ff
+    SDMMC1->POWER=SDMMC_POWER_PWRCTRL_1 | SDMMC_POWER_PWRCTRL_0; //Power on state
+    DBG("\nIDMACTRL: 0x%x\n", SDMMC1->IDMACTRL);
+    
+    //This delay is particularly important: when setting the POWER register a
+    //glitch on the CMD pin happens. This glitch has a fast fall time and a slow
+    //rise time resembling an RC charge with a ~6us rise time. If the clock is
+    //started too soon, the card sees a clock pulse while CMD is low, and
+    //interprets it as a start bit. No, setting POWER to powerup does not
+    //eliminate the glitch.
+    delayUs(10);
+    ClockController::setLowSpeedClock(); 
+}
+
+/**
+ * \internal
+ * Detect if the card is an SDHC, SDv2, SDv1, MMC
+ * \return Type of card: (1<<0)=MMC (1<<1)=SDv1 (1<<2)=SDv2 (1<<2)|(1<<3)=SDHC
+ * or Invalid if card detect failed.
+ */
+static CardType detectCardType()
+{
+    const int INIT_TIMEOUT=200; //200*10ms= 2 seconds
+    CmdResult r=Command::send(Command::CMD8,0x1aa);
+    if(r.validateError())
+    {
+        //We have an SDv2 card connected
+        if(r.getResponse()!=0x1aa)
+        {
+            DBGERR("CMD8 validation: voltage range fail\n");
+            return Invalid;
+        }
+        for(int i=0;i<INIT_TIMEOUT;i++)
+        {
+            //Bit 30 @ 1 = tell the card we like SDHCs
+            r=Command::send(Command::ACMD41,(1<<30) | sdVoltageMask);
+            //ACMD41 sends R3 as response, whose CRC is wrong.
+            if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::CRCFail)
+            {
+                r.validateError();
+                return Invalid;
+            }
+            if((r.getResponse() & (1<<31))==0) //Busy bit
+            {
+                Thread::sleep(10);
+                continue;
+            }
+            if((r.getResponse() & sdVoltageMask)==0)
+            {
+                DBGERR("ACMD41 validation: voltage range fail\n");
+                return Invalid;
+            }
+            DBG("ACMD41 validation: looped %d times\n",i);
+            if(r.getResponse() & (1<<30))
+            {
+                DBG("SDHC\n");
+                return SDHC;
+            } else {
+                DBG("SDv2\n");
+                return SDv2;
+            }
+        }
+        DBGERR("ACMD41 validation: looped until timeout\n");
+        return Invalid;
+    } else {
+        //We have an SDv1 or MMC
+        r=Command::send(Command::ACMD41,sdVoltageMask);
+        //ACMD41 sends R3 as response, whose CRC is wrong.
+        if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::CRCFail)
+        {
+            //MMC card
+            DBG("MMC card\n");
+            return MMC;
+        } else {
+            //SDv1 card
+            for(int i=0;i<INIT_TIMEOUT;i++)
+            {
+                //ACMD41 sends R3 as response, whose CRC is wrong.
+                if(r.getError()!=CmdResult::Ok &&
+                        r.getError()!=CmdResult::CRCFail)
+                {
+                    r.validateError();
+                    return Invalid;
+                }
+                if((r.getResponse() & (1<<31))==0) //Busy bit
+                {
+                    Thread::sleep(10);
+                    //Send again command
+                    r=Command::send(Command::ACMD41,sdVoltageMask);
+                    continue;
+                }
+                if((r.getResponse() & sdVoltageMask)==0)
+                {
+                    DBGERR("ACMD41 validation: voltage range fail\n");
+                    return Invalid;
+                }
+                DBG("ACMD41 validation: looped %d times\nSDv1\n",i);
+                return SDv1;
+            }
+            DBGERR("ACMD41 validation: looped until timeout\n");
+            return Invalid;
+        }
+    }
+}
+
+
+intrusive_ref_ptr<SDIODriver> SDIODriver::instance()
+{
+    static FastMutex m;
+    static intrusive_ref_ptr<SDIODriver> instance;
+    Lock<FastMutex> l(m);
+    if(!instance) instance=new SDIODriver();
+    return instance;
+}
+
+ssize_t SDIODriver::readBlock(void* buffer, size_t size, off_t where)
+{
+    if(where % 512 || size % 512) return -EFAULT;
+    unsigned int lba=where/512;
+    unsigned int nSectors=size/512;
+    Lock<FastMutex> l(mutex);
+    DBG("SDIODriver::readBlock(): nSectors=%d\n",nSectors);
+    
+    for(int i=0;i < ClockController::getRetryCount();i++)
+    {
+        CardSelector selector;
+        if(selector.succeded()==false) continue;
+        bool error=false;
+        if(multipleBlockRead(reinterpret_cast<unsigned char*>(buffer),
+                nSectors,lba)==false) error=true;
+        
+        if(error==false)
+        {
+            if(i>0) DBGERR("Read: required %d retries\n",i);
+            return size;
+        }
+    }
+    return -EBADF;
+}
+
+ssize_t SDIODriver::writeBlock(const void* buffer, size_t size, off_t where)
+{
+    if(where % 512 || size % 512) return -EFAULT;
+    unsigned int lba=where/512;
+    unsigned int nSectors=size/512;
+    Lock<FastMutex> l(mutex);
+    DBG("SDIODriver::writeBlock(): nSectors=%d\n",nSectors);
+    for(int i=0;i<ClockController::getRetryCount();i++)
+    {
+        CardSelector selector;
+        if(selector.succeded()==false) continue;
+        bool error=false;
+
+        if(multipleBlockWrite(reinterpret_cast<const unsigned char*>(buffer),
+            nSectors,lba)==false) error=true;
+        
+        if(error==false)
+        {
+            if(i>0) DBGERR("Write: required %d retries\n",i);
+            return size;
+        }
+    }
+    return -EBADF;
+}
+
+int SDIODriver::ioctl(int cmd, void* arg)
+{
+    DBG("SDIODriver::ioctl()\n");
+    if(cmd!=IOCTL_SYNC) return -ENOTTY;
+    Lock<FastMutex> l(mutex);
+    //Note: no need to select card, since status can be queried even with card
+    //not selected.
+    return waitForCardReady() ? 0 : -EFAULT;
+}
+
+SDIODriver::SDIODriver() : Device(Device::BLOCK)
+{
+
+    initSDIOPeripheral();
+
+    // This is more important than it seems, since CMD55 requires the card's RCA
+    // as argument. During initalization, after CMD0 the card has an RCA of zero
+    // so without this line ACMD41 will fail and the card won't be initialized.
+    Command::setRca(0);
+
+    //Send card reset command
+    CmdResult r=Command::send(Command::CMD0,0);
+    if(r.validateError()==false) return;
+
+    cardType=detectCardType();
+    if(cardType==Invalid) return; //Card detect failed
+    if(cardType==MMC) return; //MMC cards currently unsupported
+
+    // Now give an RCA to the card. In theory we should loop and enumerate all
+    // the cards but this driver supports only one card.
+    r=Command::send(Command::CMD2,0);
+    //CMD2 sends R2 response, whose CMDINDEX field is wrong
+    if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::RespNotMatch)
+    {
+        r.validateError();
+        return;
+    }
+    r=Command::send(Command::CMD3,0);
+    if(r.validateR6Response()==false) return;
+    Command::setRca(r.getResponse()>>16);
+    DBG("Got RCA=%u\n",Command::getRca());
+    if(Command::getRca()==0)
+    {
+        //RCA=0 can't be accepted, since it is used to deselect cards
+        DBGERR("RCA=0 is invalid\n");
+        return;
+    }
+
+    //Lastly, try selecting the card and configure the latest bits
+    {
+        CardSelector selector;
+        if(selector.succeded()==false) return;
+
+        r=Command::send(Command::CMD13,Command::getRca()<<16);//Get status
+        if(r.validateR1Response()==false) return;
+        if(r.getState()!=4) //4=Tran state
+        {
+            DBGERR("CMD7 was not able to select card\n");
+            return;
+        }
+
+        #ifndef SD_ONE_BIT_DATABUS
+        r=Command::send(Command::ACMD6,2);   //Set 4 bit bus width
+        if(r.validateR1Response()==false) return;
+        #endif //SD_ONE_BIT_DATABUS
+
+        if(cardType!=SDHC)
+        {
+            r=Command::send(Command::CMD16,512); //Set 512Byte block length
+            if(r.validateR1Response()==false) return;
+        }
+    }
+
+    // Now that card is initialized, perform self calibration of maximum
+    // possible read/write speed. This as a side effect enables 4bit bus width.
+    ClockController::calibrateClockSpeed(this);
+
+
+    DBG("SDIO init: Success\n");
+
+}
+}
diff --git a/miosix/arch/common/drivers/sd_stm32l4.h b/miosix/arch/common/drivers/sd_stm32l4.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc65cfd9bf2514de7fd8b9c13adce9c45b163ff4
--- /dev/null
+++ b/miosix/arch/common/drivers/sd_stm32l4.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Terraneo Federico                               *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   As a special exception, if other files instantiate templates or use   *
+ *   macros or inline functions from this file, or you compile this file   *
+ *   and link it with other works to produce a work based on this file,    *
+ *   this file does not by itself cause the resulting work to be covered   *
+ *   by the GNU General Public License. However the source code for this   *
+ *   file must still be made available in accordance with the GNU General  *
+ *   Public License. This exception does not invalidate any other reasons  *
+ *   why a work based on this file might be covered by the GNU General     *
+ *   Public License.                                                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
+ ***************************************************************************/
+
+#ifndef SD_STM32L4_H
+#define	SD_STM32L4_H
+
+#include "kernel/sync.h"
+#include "filesystem/devfs/devfs.h"
+#include "filesystem/ioctl.h"
+
+namespace miosix {
+
+/**
+ * Driver for the SDIO peripheral in STM32F2 and F4 microcontrollers
+ */
+class SDIODriver : public Device
+{
+public:
+    /**
+     * \return an instance to this class, singleton
+     */
+    static intrusive_ref_ptr<SDIODriver> instance();
+    
+    virtual ssize_t readBlock(void *buffer, size_t size, off_t where);
+    
+    virtual ssize_t writeBlock(const void *buffer, size_t size, off_t where);
+    
+    virtual int ioctl(int cmd, void *arg);
+private:
+    /**
+     * Constructor
+     */
+    SDIODriver();
+    
+    FastMutex mutex;
+};
+
+} //namespace miosix
+
+#endif //SD_STM32L4_H
\ No newline at end of file
diff --git a/miosix/arch/cortexM4_stm32l4/stm32l4r9zi_sensortile/interfaces-impl/bsp.cpp b/miosix/arch/cortexM4_stm32l4/stm32l4r9zi_sensortile/interfaces-impl/bsp.cpp
index e27c104dd48bf78a5025f4b23ad8f770c72834eb..53d1b0ea286f916da78ccb070030df5823045aaa 100644
--- a/miosix/arch/cortexM4_stm32l4/stm32l4r9zi_sensortile/interfaces-impl/bsp.cpp
+++ b/miosix/arch/cortexM4_stm32l4/stm32l4r9zi_sensortile/interfaces-impl/bsp.cpp
@@ -97,7 +97,7 @@ void IRQbspInit()
 void bspInit2()
 {
     #ifdef WITH_FILESYSTEM
-    // basicFilesystemSetup(SDIODriver::instance());
+    basicFilesystemSetup(SDIODriver::instance());
     #endif //WITH_FILESYSTEM
 }
 
diff --git a/miosix/config/Makefile.inc b/miosix/config/Makefile.inc
index bc6b2dc469ed35613a18b00ecc2aadb3469d712f..495675267e16340e919d025ff7a3e5297ac03f5f 100644
--- a/miosix/config/Makefile.inc
+++ b/miosix/config/Makefile.inc
@@ -3358,6 +3358,36 @@ else ifeq ($(ARCH),cortexM4_stm32l4)
         ## error message saying that 'make program' is not supported for that
         ## board.
         PROGRAM_CMDLINE := echo 'make program not supported.'
+    
+    ##-------------------------------------------------------------------------
+    ## BOARD: stm32l4r9zi_sensortile
+    ##
+    else ifeq ($(OPT_BOARD),stm32l4r9zi_sensortile)
+        ## Base directory with header files for this board
+        BOARD_INC := arch/cortexM4_stm32l4/stm32l4r9zi_sensortile
+
+        ## Select linker script and boot file
+        ## Their path must be relative to the miosix directory.
+        BOOT_FILE := $(BOARD_INC)/core/stage_1_boot.o
+        LINKER_SCRIPT := $(BOARD_INC)/stm32_2m+640k_rom.ld
+
+        ## Select architecture specific files
+        ## These are the files in arch/<arch name>/<board name>
+        ARCH_SRC :=                                  \
+        $(BOARD_INC)/interfaces-impl/bsp.cpp         \
+        arch/common/drivers/sd_stm32l4.cpp
+
+        ## Add a #define to allow querying board name
+        CFLAGS_BASE   += -D_BOARD_STM32L4R9ZI_SENSORTILE
+        CXXFLAGS_BASE += -D_BOARD_STM32L4R9ZI_SENSORTILE
+
+        ## Select programmer command line
+        ## This is the program that is invoked when the user types
+        ## 'make program'
+        ## The command must provide a way to program the board, or print an
+        ## error message saying that 'make program' is not supported for that
+        ## board.
+        PROGRAM_CMDLINE := st-flash --reset write "main.bin" 0x8000000
 
     ##-------------------------------------------------------------------------
     ## End of board list
diff --git a/miosix/config/options.cmake b/miosix/config/options.cmake
index 298b72019c1803758f0a4e7d17bb0f1bad91ac46..f6ecc4449ca15bb409ac6c81ba95996dc83d5862 100644
--- a/miosix/config/options.cmake
+++ b/miosix/config/options.cmake
@@ -3494,6 +3494,7 @@ elseif(${ARCH} STREQUAL cortexM4_stm32l4)
         ## These are the files in arch/<arch name>/<board name>
         set(ARCH_SRC
             ${KPATH}/${BOARD_INC}/interfaces-impl/bsp.cpp
+            ${KPATH}/arch/common/drivers/sd_stm32l4.cpp
         )
 
         ## Add a #define to allow querying board name