diff --git a/boards/remote-revb/include/board.h b/boards/remote-revb/include/board.h
index 619861cda74917f4920965c76b06acf901b635b8..7e1e212e99c341a889a24794b4bebdb06ee06c07 100644
--- a/boards/remote-revb/include/board.h
+++ b/boards/remote-revb/include/board.h
@@ -110,6 +110,19 @@
 #define CC1200_GPD2_GPIO    GPIO_PB0
 /** @} */
 
+/**
+ * @name Onboard micro-sd slot pin definitions
+ * @{
+ */
+#define SDCARD_SPI_PARAM_SPI       SPI_1
+#define SDCARD_SPI_PARAM_CS        GPIO_PIN(0,7)
+#define SDCARD_SPI_PARAM_CLK       GPIO_PIN(2,4)
+#define SDCARD_SPI_PARAM_MOSI      GPIO_PIN(2,5)
+#define SDCARD_SPI_PARAM_MISO      GPIO_PIN(2,6)
+#define SDCARD_SPI_PARAM_POWER     GPIO_PIN(0,6)
+#define SDCARD_SPI_PARAM_POWER_AH  false
+/** @} */
+
 #ifdef __cplusplus
 } /* end extern "C" */
 #endif
diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep
index fcfe91e73c87c9873db347c061a7912d146f5d7e..e511c53a5b315bb5818cf99c57bc577d4af2dbc2 100644
--- a/drivers/Makefile.dep
+++ b/drivers/Makefile.dep
@@ -143,6 +143,12 @@ ifneq (,$(filter rgbled,$(USEMODULE)))
   USEMODULE += color
 endif
 
+ifneq (,$(filter sdcard_spi,$(USEMODULE)))
+  FEATURES_REQUIRED += periph_gpio
+  FEATURES_REQUIRED += periph_spi
+  USEMODULE += xtimer
+endif
+
 ifneq (,$(filter sht11,$(USEMODULE)))
     USEMODULE += xtimer
 endif
diff --git a/drivers/Makefile.include b/drivers/Makefile.include
index da1880178b2628019093c7bb8d0fa50baa81ab77..d1ba0fb1eb46154e84603e4e7575143073177725 100644
--- a/drivers/Makefile.include
+++ b/drivers/Makefile.include
@@ -91,3 +91,6 @@ endif
 ifneq (,$(filter lis3dh,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/drivers/lis3dh/include
 endif
+ifneq (,$(filter sdcard_spi,$(USEMODULE)))
+    USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sdcard_spi/include
+endif
diff --git a/drivers/include/sdcard_spi.h b/drivers/include/sdcard_spi.h
new file mode 100644
index 0000000000000000000000000000000000000000..87758921285dfadcfbd690a1264fb46f05b4e171
--- /dev/null
+++ b/drivers/include/sdcard_spi.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2016 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup    drivers_sdcard_spi SPI SD-Card driver
+ * @ingroup     drivers
+ * @brief       Driver for reading and writing sd-cards via spi interface.
+ * @{
+ *
+ * @file
+ * @brief       Public interface for the sdcard_spi driver.
+ *
+ * @author      Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ */
+
+#ifndef SDCARD_SPI_H
+#define SDCARD_SPI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "periph/spi.h"
+#include "periph/gpio.h"
+#include "stdbool.h"
+
+#define SD_HC_BLOCK_SIZE      (512)  /**< size of a single block on SDHC cards */
+#define SDCARD_SPI_INIT_ERROR (-1)   /**< returned on failed init */
+#define SDCARD_SPI_OK         (0)    /**< returned on successful init */
+
+#define SD_SIZE_OF_OID 2 /**< OID (OEM/application ID field in CID reg) */
+#define SD_SIZE_OF_PNM 5 /**< PNM (product name field in CID reg) */
+
+/**
+ * @brief   CID register see section 5.2 in SD-Spec v5.00
+ */
+struct {
+    uint8_t MID;              /**< Manufacturer ID */
+    char OID[SD_SIZE_OF_OID]; /**< OEM/Application ID*/
+    char PNM[SD_SIZE_OF_PNM]; /**< Product name */
+    uint8_t PRV;              /**< Product revision */
+    uint32_t PSN;             /**< Product serial number */
+    uint16_t MDT;             /**< Manufacturing date */
+    uint8_t CID_CRC;          /**< CRC7 checksum */
+} typedef cid_t;
+
+/**
+ * @brief   CSD register with csd structure version 1.0
+ *          see section 5.3.2 in SD-Spec v5.00
+ */
+struct {
+    uint8_t CSD_STRUCTURE : 2;        /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t TAAC : 8;                 /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t NSAC : 8;                 /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t TRAN_SPEED : 8;           /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint16_t CCC : 12;                /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t READ_BL_LEN : 4;          /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t READ_BL_PARTIAL : 1;      /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t WRITE_BLK_MISALIGN : 1;   /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t READ_BLK_MISALIGN : 1;    /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t DSR_IMP : 1;              /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint16_t C_SIZE : 12;             /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t VDD_R_CURR_MIN : 3;       /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t VDD_R_CURR_MAX : 3;       /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t VDD_W_CURR_MIN : 3;       /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t VDD_W_CURR_MAX : 3;       /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t C_SIZE_MULT : 3;          /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t ERASE_BLK_EN : 1;         /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t SECTOR_SIZE : 7;          /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t WP_GRP_SIZE : 7;          /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t WP_GRP_ENABLE : 1;        /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t R2W_FACTOR : 3;           /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t WRITE_BL_LEN : 4;         /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t WRITE_BL_PARTIAL : 1;     /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t FILE_FORMAT_GRP : 1;      /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t COPY : 1;                 /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t PERM_WRITE_PROTECT : 1;   /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t TMP_WRITE_PROTECT : 1;    /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t FILE_FORMAT : 2;          /**< see section 5.3.2 in SD-Spec v5.00 */
+    uint8_t CSD_CRC : 8;              /**< see section 5.3.2 in SD-Spec v5.00 */
+} typedef csd_v1_t;
+
+/**
+ * @brief   CSD register with csd structure version 2.0
+ *          see section 5.3.3 in SD-Spec v5.00
+ */
+struct {
+    uint8_t CSD_STRUCTURE : 2;        /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t TAAC : 8;                 /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t NSAC : 8;                 /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t TRAN_SPEED : 8;           /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint16_t CCC : 12;                /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t READ_BL_LEN : 4;          /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t READ_BL_PARTIAL : 1;      /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t WRITE_BLK_MISALIGN : 1;   /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t READ_BLK_MISALIGN : 1;    /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t DSR_IMP : 1;              /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint32_t C_SIZE : 22;             /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t ERASE_BLK_EN : 1;         /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t SECTOR_SIZE : 7;          /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t WP_GRP_SIZE : 7;          /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t WP_GRP_ENABLE : 1;        /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t R2W_FACTOR : 3;           /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t WRITE_BL_LEN : 4;         /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t WRITE_BL_PARTIAL : 1;     /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t FILE_FORMAT_GRP : 1;      /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t COPY : 1;                 /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t PERM_WRITE_PROTECT : 1;   /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t TMP_WRITE_PROTECT : 1;    /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t FILE_FORMAT : 2;          /**< see section 5.3.3 in SD-Spec v5.00 */
+    uint8_t CSD_CRC : 8;              /**< see section 5.3.3 in SD-Spec v5.00 */
+} typedef csd_v2_t;
+
+/**
+ * @brief   CSD register (see section 5.3 in SD-Spec v5.00)
+ */
+union {
+    csd_v1_t v1;   /**< see section 5.3.2 in SD-Spec v5.00 */
+    csd_v2_t v2;   /**< see section 5.3.3 in SD-Spec v5.00 */
+} typedef csd_t;
+
+/**
+ * @brief   SD status register (see section 4.10.2 in SD-Spec v5.00)
+ */
+struct {
+    uint32_t SIZE_OF_PROTECTED_AREA : 32;   /**< see section 4.10.2 in SD-Spec v5.00 */
+    uint32_t SUS_ADDR : 22;                 /**< see section 4.10.2.12 in SD-Spec v5.00 */
+    uint32_t VSC_AU_SIZE : 10;              /**< see section 4.10.2.11 in SD-Spec v5.00 */
+    uint16_t SD_CARD_TYPE : 16;             /**< see section 4.10.2 in SD-Spec v5.00 */
+    uint16_t ERASE_SIZE : 16;               /**< see section 4.10.2.5 in SD-Spec v5.00 */
+    uint8_t  SPEED_CLASS : 8;               /**< see section 4.10.2.2 in SD-Spec v5.00 */
+    uint8_t  PERFORMANCE_MOVE : 8;          /**< see section 4.10.2.3 in SD-Spec v5.00 */
+    uint8_t  VIDEO_SPEED_CLASS : 8;         /**< see section 4.10.2.10 in SD-Spec v5.00 */
+    uint8_t  ERASE_TIMEOUT : 6;             /**< see section 4.10.2.6 in SD-Spec v5.00 */
+    uint8_t  ERASE_OFFSET : 2;              /**< see section 4.10.2.7 in SD-Spec v5.00 */
+    uint8_t  UHS_SPEED_GRADE : 4;           /**< see section 4.10.2.8 in SD-Spec v5.00 */
+    uint8_t  UHS_AU_SIZE : 4;               /**< see section 4.10.2.9 in SD-Spec v5.00 */
+    uint8_t  AU_SIZE : 4;                   /**< see section 4.10.2.4 in SD-Spec v5.00 */
+    uint8_t  DAT_BUS_WIDTH : 2;             /**< see section 4.10.2 in SD-Spec v5.00 */
+    uint8_t  SECURED_MODE : 1;              /**< see section 4.10.2 in SD-Spec v5.00 */
+} typedef sd_status_t;
+
+/**
+ * @brief   version type of SD-card
+ */
+typedef enum {
+    SD_V2,                 /**< SD version 2  */
+    SD_V1,                 /**< SD version 1  */
+    MMC_V3,                /**< MMC version 3 */
+    SD_UNKNOWN             /**< SD-version unknown */
+} sd_version_t;
+
+/**
+ * @brief   sdcard_spi r/w-operation return values
+ */
+typedef enum {
+    SD_RW_OK = 0,           /**< no error */
+    SD_RW_NO_TOKEN,         /**< no token was received (on block read) */
+    SD_RW_TIMEOUT,          /**< cmd timed out (not-busy-state wasn't entered) */
+    SD_RW_RX_TX_ERROR,      /**< error while performing SPI read/write */
+    SD_RW_WRITE_ERROR,      /**< data-packet response indicates error */
+    SD_RW_CRC_MISMATCH,     /**< CRC-mismatch of received data */
+    SD_RW_NOT_SUPPORTED     /**< operation not supported on used card */
+} sd_rw_response_t;
+
+/**
+ * @brief   sdcard_spi device params
+ */
+typedef struct {
+    spi_t spi_dev;          /**< SPI bus used */
+    gpio_t cs;              /**< pin connected to the DAT3 sd pad */
+    gpio_t clk;             /**< pin connected to the CLK sd pad */
+    gpio_t mosi;            /**< pin connected to the CMD sd pad*/
+    gpio_t miso;            /**< pin connected to the DAT0 sd pad*/
+    gpio_t power;           /**< pin that controls sd power circuit*/
+    bool power_act_high;    /**< true if card power is enabled by 'power'-pin HIGH*/
+} sdcard_spi_params_t;
+
+/**
+ * @brief   Device descriptor for sdcard_spi
+ */
+struct {
+    sdcard_spi_params_t params;  /**< parameters for pin and spi config */
+    bool use_block_addr;         /**< true if block adressing (vs. byte adressing) is used */
+    bool init_done;              /**< set to true once the init procedure completed sucessfully */
+    sd_version_t card_type;      /**< version of SD-card */
+    int csd_structure;           /**< version of the CSD register structure */
+    cid_t cid;                   /**< CID register */
+    csd_t csd;                   /**< CSD register */
+} typedef sdcard_spi_t;
+
+/**
+ * @brief              Initializes the sd-card with the given parameters in sdcard_spi_t structure.
+ *                     The init procedure also takes care of initializing the spi peripheral to master
+ *                     mode and performing all neccecary steps to set the sd-card to spi-mode. Reading
+ *                     the CID and CSD registers is also done within this routine and their
+ *                     values are copied to the given sdcard_spi_t struct.
+ *
+ * @param[out] card    the device descriptor
+ * @param[in]  params  parameters for this device (pins and spi device are initialized by this driver)
+ *
+ * @return             0 if the card could be initialized successfully
+ * @return             false if an error occured while initializing the card
+ */
+int sdcard_spi_init(sdcard_spi_t *card, const sdcard_spi_params_t *params);
+
+/**
+ * @brief                 Reads data blocks (usually multiples of 512 Bytes) from card to buffer.
+ *
+ * @param[in] card        Initialized sd-card struct
+ * @param[in] blockaddr   Start adress to read from. Independet of the actual adressing scheme of
+ *                        the used card the adress needs to be given as block address
+ *                        (e.g. 0, 1, 2... NOT: 0, 512... ). The driver takes care of mapping to
+ *                        byte adressing if needed.
+ * @param[out] data       Buffer to store the read data in. The user is responsible for providing a
+ *                        suitable buffer size.
+ * @param[in]  blocksize  Size of data blocks. For now only 512 byte blocks are supported because
+ *                        only older (SDSC) cards support variable blocksizes anyway.
+ *                        With SDHC/SDXC-cards this is always fixed to 512 bytes. SDSC cards are
+ *                        automatically forced to use 512 byte as blocksize by the init procedure.
+ * @param[in]  nblocks    Number of blocks to read
+ * @param[out] state      Contains information about the error state if something went wrong
+ *                        (if return value is lower than nblocks).
+ *
+ * @return                number of sucessfully read blocks (0 if no block was read).
+ */
+int sdcard_spi_read_blocks(sdcard_spi_t *card, int blockaddr, char *data, int blocksize,
+                           int nblocks, sd_rw_response_t *state);
+
+/**
+ * @brief                 Writes data blocks (usually multiples of 512 Bytes) from buffer to card.
+ *
+ * @param[in] card        Initialized sd-card struct
+ * @param[in] blockaddr   Start adress to read from. Independet of the actual adressing scheme of
+ *                        the used card the adress needs to be given as block address
+ *                        (e.g. 0, 1, 2... NOT: 0, 512... ). The driver takes care of mapping to
+ *                        byte adressing if needed.
+ * @param[out] data       Buffer that contains the data to be sent.
+ * @param[in]  blocksize  Size of data blocks. For now only 512 byte blocks are supported because
+ *                        only older (SDSC) cards support variable blocksizes anyway.
+ *                        With SDHC/SDXC-cards this is always fixed to 512 bytes. SDSC cards are
+ *                        automatically forced to use 512 byte as blocksize by the init procedure.
+ * @param[in]  nblocks    Number of blocks to write
+ * @param[out] state      Contains information about the error state if something went wrong
+ *                         (if return value is lower than nblocks).
+ *
+ * @return                number of sucessfully written blocks (0 if no block was written).
+ */
+int sdcard_spi_write_blocks(sdcard_spi_t *card, int blockaddr, char *data, int blocksize,
+                            int nblocks, sd_rw_response_t *state);
+
+/**
+ * @brief                 Gets the capacity of the card.
+ *
+ * @param[in] card        Initialized sd-card struct
+ *
+ * @return                capacity of the card in bytes
+ */
+uint64_t sdcard_spi_get_capacity(sdcard_spi_t *card);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SDCARD_SPI_H */
+/** @} */
diff --git a/drivers/sdcard_spi/Makefile b/drivers/sdcard_spi/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..f36db234e39db2a0c03bd03dd3ece18fb03a0e14
--- /dev/null
+++ b/drivers/sdcard_spi/Makefile
@@ -0,0 +1,3 @@
+MODULE = sdcard_spi
+
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/sdcard_spi/include/sdcard_spi_internal.h b/drivers/sdcard_spi/include/sdcard_spi_internal.h
new file mode 100644
index 0000000000000000000000000000000000000000..5ca30cfd205e365dd35f220d352df981ae27b5b9
--- /dev/null
+++ b/drivers/sdcard_spi/include/sdcard_spi_internal.h
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2016 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_sdcard_spi
+ * @{
+ *
+ * @file
+ * @brief       Interface for issuing commands on sd-cards via SPI.
+ * @details     For details of the sd card standard and the spi mode refer to
+ *              "SD Specifications Part 1 Physical Layer Simplified Specification".
+ *              References to the sd specs in this file apply to Version 5.00
+ *              from August 10, 2016. For further details see
+ *              https://www.sdcard.org/downloads/pls/pdf/part1_500.pdf.
+ *
+ * @author      Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ */
+
+#ifndef SDCARD_SPI_INTERNAL_H
+#define SDCARD_SPI_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "periph/spi.h"
+#include "periph/gpio.h"
+#include "stdbool.h"
+#include "sdcard_spi.h"
+
+
+/* number of clocks that should be applied to the card on init
+   before taking furter actions (see sd spec. 6.4.1.1 Power Up Time of Card) */
+#define SD_POWERSEQUENCE_CLOCK_COUNT 74
+
+#define SD_CARD_PREINIT_CLOCK_PERIOD_US 10 /* used to generate 100 kHz clock in init phase*/
+#define SD_CARD_WAIT_AFTER_POWER_UP_US  1000
+
+/* R1 response bits (see sd spec. 7.3.2.1 Format R1) */
+#define SD_R1_RESPONSE_PARAM_ERROR       (1<<6)
+#define SD_R1_RESPONSE_ADDR_ERROR        (1<<5)
+#define SD_R1_RESPONSE_ERASE_SEQ_ERROR   (1<<4)
+#define SD_R1_RESPONSE_CMD_CRC_ERROR     (1<<3)
+#define SD_R1_RESPONSE_ILLEGAL_CMD_ERROR (1<<2)
+#define SD_R1_RESPONSE_ERASE_RESET       (1<<1)
+#define SD_R1_RESPONSE_IN_IDLE_STATE     (0x01)
+#define SD_INVALID_R1_RESPONSE           (1<<7)
+
+#define R1_VALID(X) (((X) >> 7) == 0)
+#define R1_PARAM_ERR(X)   ((((X) &SD_R1_RESPONSE_PARAM_ERROR) != 0))
+#define R1_ADDR_ERR(X)    ((((X) &SD_R1_RESPONSE_ADDR_ERROR) != 0))
+#define R1_ERASE_ERR(X)   ((((X) &SD_R1_RESPONSE_ERASE_SEQ_ERROR) != 0))
+#define R1_CMD_CRC_ERR(X) ((((X) &SD_R1_RESPONSE_CMD_CRC_ERROR) != 0))
+#define R1_ILL_CMD_ERR(X) ((((X) &SD_R1_RESPONSE_ILLEGAL_CMD_ERROR) != 0))
+#define R1_IDLE_BIT_SET(X) (((X) &SD_R1_RESPONSE_IN_IDLE_STATE) != 0)
+#define R1_ERROR(X) (R1_PARAM_ERR(X) || R1_ADDR_ERR(X) || R1_ERASE_ERR(X) || \
+                     R1_CMD_CRC_ERR(X) || R1_ILL_CMD_ERR(X))
+
+/* see sd spec. 7.3.3.1 Data Response Token */
+#define DATA_RESPONSE_IS_VALID(X)  (((X) & 0x11) == 0x01)
+#define DATA_RESPONSE_ACCEPTED(X)  (((X) & 0x0E) == (1<<2))
+#define DATA_RESPONSE_CRC_ERR(X)   (((X) & 0x0E) == 0x0A)
+#define DATA_RESPONSE_WRITE_ERR(X) (((X) & 0x0E) == 0x0C)
+
+/* see sd spec. 5.1 OCR register */
+#define OCR_VOLTAGE_3_2_TO_3_3 (1L << 20)
+#define OCR_VOLTAGE_3_3_TO_3_4 (1L << 21)
+
+/* card capacity status (CCS=0: the card is SDSD; CCS=1: card is SDHC or SDXC) */
+#define OCR_CCS (1L << 30)
+
+/* This bit is set to low if the card has not finished power up routine */
+#define OCR_POWER_UP_STATUS (1L << 31)
+
+/* to ensure the voltage range check on init is done properly you need to
+   define this according to your actual interface/wiring with the sd-card */
+#define SYSTEM_VOLTAGE (OCR_VOLTAGE_3_2_TO_3_3 | OCR_VOLTAGE_3_2_TO_3_3)
+
+
+/* see sd spec. 7.3.1.3 Detailed Command Description */
+#define SD_CMD_PREFIX_MASK (1<<6)
+
+#define SD_CMD_0 0   /* Resets the SD Memory Card */
+#define SD_CMD_1 1   /* Sends host capacity support info and starts the cards init process */
+#define SD_CMD_8 8   /* Sends SD Card interface condition incl. host supply voltage info */
+#define SD_CMD_9 9   /* Asks the selected card to send its card-specific data (CSD) */
+#define SD_CMD_10 10 /* Asks the selected card to send its card identification (CID) */
+#define SD_CMD_12 12 /* Forces the card to stop transmission in Multiple Block Read Operation */
+#define SD_CMD_13 13 /* Sent as ACMD13 asks the card to send it's SD status */
+
+#define SD_CMD_16 16 /* In case of SDSC Card, block length is set by this command */
+#define SD_CMD_17 17 /* Reads a block of the size selected by the SET_BLOCKLEN command */
+#define SD_CMD_18 18 /* Continuously transfers data blocks from card to host
+                        until interrupted by a STOP_TRANSMISSION command */
+#define SD_CMD_24 24 /* Writes a block of the size selected by the SET_BLOCKLEN command */
+#define SD_CMD_25 25 /* Continuously writes blocks of data until 'Stop Tran'token is sent */
+#define SD_CMD_41 41 /* Reserved (used for ACMD41) */
+#define SD_CMD_55 55 /* Defines to the card that the next commmand is an application specific
+                        command rather than a standard command */
+#define SD_CMD_58 58 /* Reads the OCR register of a card */
+#define SD_CMD_59 59 /* Turns the CRC option on or off. Argument: 1:on; 0:off */
+
+#define SD_CMD_8_VHS_2_7_V_TO_3_6_V 0x01
+#define SD_CMD_8_CHECK_PATTERN      0xB5
+#define SD_CMD_NO_ARG     0x00000000
+#define SD_ACMD_41_ARG_HC 0x40000000
+#define SD_CMD_59_ARG_EN  0x00000001
+#define SD_CMD_59_ARG_DIS 0x00000000
+
+/* see sd spec. 7.3.3 Control Tokens */
+#define SD_DATA_TOKEN_CMD_17_18_24 0xFE
+#define SD_DATA_TOKEN_CMD_25       0xFC
+#define SD_DATA_TOKEN_CMD_25_STOP  0xFD
+
+#define SD_SIZE_OF_CID_AND_CSD_REG 16
+#define SD_SIZE_OF_SD_STATUS 64
+#define SD_BLOCKS_FOR_REG_READ 1
+#define SD_GET_CSD_STRUCTURE(CSD_RAW_DATA) ((CSD_RAW_DATA)[0] >> 6)
+#define SD_CSD_V1 0
+#define SD_CSD_V2 1
+#define SD_CSD_VUNSUPPORTED -1
+
+/* the retry counters below are used as timeouts for specific actions.
+   The values may need some adjustments to either give the card more time to respond
+   to commands or to achieve a lower delay / avoid infinite blocking. */
+#define R1_POLLING_RETRY_CNT       1000000
+#define SD_DATA_TOKEN_RETRY_CNT    1000000
+#define INIT_CMD_RETRY_CNT         1000000
+#define INIT_CMD0_RETRY_CNT        3
+#define SD_WAIT_FOR_NOT_BUSY_CNT   1000000 /* use -1 for full blocking till the card isn't busy */
+#define SD_BLOCK_READ_CMD_RETRIES  10     /* only affects sending of cmd not whole transaction! */
+#define SD_BLOCK_WRITE_CMD_RETRIES 10    /* only affects sending of cmd not whole transaction! */
+
+/* memory capacity in bytes = (C_SIZE+1) * SD_CSD_V2_C_SIZE_BLOCK_MULT * BLOCK_LEN */
+#define SD_CSD_V2_C_SIZE_BLOCK_MULT 1024
+
+#define SD_CARD_SPI_MODE SPI_CONF_FIRST_RISING
+
+/* this speed setting is only used while the init procedure is performed */
+#define SD_CARD_SPI_SPEED_PREINIT SPI_SPEED_400KHZ
+
+/* after init procedure is finished the driver auto sets the card to this speed */
+#define SD_CARD_SPI_SPEED_POSTINIT SPI_SPEED_10MHZ
+
+#define SD_CARD_DUMMY_BYTE 0xFF
+
+#define SDCARD_SPI_IEC_KIBI (1024L)
+#define SDCARD_SPI_SI_KILO  (1000L)
+
+typedef enum {
+    SD_INIT_START,
+    SD_INIT_SPI_POWER_SEQ,
+    SD_INIT_SEND_CMD0,
+    SD_INIT_SEND_CMD8,
+    SD_INIT_CARD_UNKNOWN,
+    SD_INIT_SEND_ACMD41_HCS,
+    SD_INIT_SEND_ACMD41,
+    SD_INIT_SEND_CMD1,
+    SD_INIT_SEND_CMD58,
+    SD_INIT_SEND_CMD16,
+    SD_INIT_ENABLE_CRC,
+    SD_INIT_READ_CID,
+    SD_INIT_READ_CSD,
+    SD_INIT_SET_MAX_SPI_SPEED,
+    SD_INIT_FINISH
+} sd_init_fsm_state_t;
+
+/**
+ * @brief                 Sends a cmd to the sd card.
+ *
+ * @param[in] card        Initialized sd-card struct
+ * @param[in] sd_cmd_idx  A supported sd-card command index for SPI-mode like defined in
+ *                        "7.3.1.3 Detailed Command Description" of sd spec.
+ *                        (for CMDX this parameter is simply the integer value X).
+ * @param[in] argument    The argument for the given cmd. As described by "7.3.1.1 Command Format".
+ *                        This argument is transmitted byte wise with most significant byte first.
+ * @param[in] max_retry   Specifies how often the command should be retried if an error occures.
+ *                        Use 0 to try only once, -1 to try forever, or n to retry n times.
+ *
+ * @return                R1 response of the command if no (low-level) communication error occured
+ * @return                SD_INVALID_R1_RESPONSE if either waiting for the card to enter
+ *                        not-busy-state timed out or spi communication failed
+ */
+char sdcard_spi_send_cmd(sdcard_spi_t *card, char sd_cmd_idx, uint32_t argument, int32_t max_retry);
+
+/**
+ * @brief                 Sends an acmd to the sd card. ACMD<n> consists of sending CMD55 + CMD<n>
+ *
+ * @param[in] card        Initialized sd-card struct
+ * @param[in] sd_cmd_idx  A supported sd-card command index for SPI-mode like defined in
+ *                        "7.3.1.3 Detailed Command Description" of sd spec.
+ *                        (for ACMDX this parameter is simply the integer value X).
+ * @param[in] argument    The argument for the given cmd. As described by "7.3.1.1 Command Format".
+ *                        This argument is transmitted byte wise with most significant byte first.
+ * @param[in] max_retry   Specifies how often the command should be retried if an error occures.
+ *                        Use 0 to try only once, -1 to try forever, or n to retry n times.
+ *
+ * @return                R1 response of the command if no (low-level) communication error occured
+ * @return                SD_INVALID_R1_RESPONSE if either waiting for the card to enter
+ *                        not-busy-state timed out or spi communication failed
+ */
+char sdcard_spi_send_acmd(sdcard_spi_t *card, char sd_cmd_idx, uint32_t argument, int32_t max_retry);
+
+/**
+ * @brief                 Gets the sector count of the card.
+ *
+ * @param[in] card        Initialized sd-card struct
+ *
+ * @return                number of available sectors
+ */
+uint32_t sdcard_spi_get_sector_count(sdcard_spi_t *card);
+
+/**
+ * @brief                 Gets the allocation unit size of the card.
+ *
+ * @param[in] card        Initialized sd-card struct
+ *
+ * @return                size of AU in bytes
+ */
+uint32_t sdcard_spi_get_au_size(sdcard_spi_t *card);
+
+/**
+ * @brief                 Gets the SD status of the card.
+ *
+ * @param[in]  card       Initialized sd-card struct
+ * @param[out] sd_status  memory location where status struct is stored
+ *
+ * @return                sd_status_t struct that contains all SD status information
+ */
+sd_rw_response_t sdcard_spi_read_sds(sdcard_spi_t *card, sd_status_t *sd_status);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SDCARD_SPI_INTERNAL_H */
+/** @} */
diff --git a/drivers/sdcard_spi/include/sdcard_spi_params.h b/drivers/sdcard_spi/include/sdcard_spi_params.h
new file mode 100644
index 0000000000000000000000000000000000000000..0f46304054ff4335a3ceb2ab8e563a7f00f98908
--- /dev/null
+++ b/drivers/sdcard_spi/include/sdcard_spi_params.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_sdcard_spi
+ * @{
+ *
+ * @file
+ * @brief       Default parameters for sdcard_spi driver
+ *
+ * @author      Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ */
+
+#ifndef SDCARD_SPI_PARAMS_H
+#define SDCARD_SPI_PARAMS_H
+
+#include "board.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Set default configuration parameters for the sdcard_spi driver
+ * @{
+ */
+#ifndef SDCARD_SPI_PARAM_SPI
+#define SDCARD_SPI_PARAM_SPI         (SPI_0)
+#endif
+#ifndef SDCARD_SPI_PARAM_CS
+#define SDCARD_SPI_PARAM_CS          (GPIO_PIN(2,4))
+#endif
+#ifndef SDCARD_SPI_PARAM_CLK
+#define SDCARD_SPI_PARAM_CLK         (GPIO_PIN(2,5))
+#endif
+#ifndef SDCARD_SPI_PARAM_MOSI
+#define SDCARD_SPI_PARAM_MOSI        (GPIO_PIN(2,6))
+#endif
+#ifndef SDCARD_SPI_PARAM_MISO
+#define SDCARD_SPI_PARAM_MISO        (GPIO_PIN(2,7))
+#endif
+#ifndef SDCARD_SPI_PARAM_POWER
+#define SDCARD_SPI_PARAM_POWER       (GPIO_UNDEF)
+#endif
+#ifndef SDCARD_SPI_PARAM_POWER_AH
+/** treated as 'don't care' if SDCARD_SPI_PARAM_POWER is GPIO_UNDEF */
+#define SDCARD_SPI_PARAM_POWER_AH    (true)
+#endif
+/** @} */
+
+/**
+ * @brief   sdcard_spi configuration
+ */
+static const  sdcard_spi_params_t sdcard_spi_params[] = {
+    {
+        .spi_dev   = SDCARD_SPI_PARAM_SPI,
+        .cs    = SDCARD_SPI_PARAM_CS,
+        .clk   = SDCARD_SPI_PARAM_CLK,
+        .mosi  = SDCARD_SPI_PARAM_MOSI,
+        .miso  = SDCARD_SPI_PARAM_MISO,
+        .power = SDCARD_SPI_PARAM_POWER,
+        .power_act_high = SDCARD_SPI_PARAM_POWER_AH
+    },
+};
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SDCARD_SPI_PARAMS_H */
+/** @} */
diff --git a/drivers/sdcard_spi/sdcard_spi.c b/drivers/sdcard_spi/sdcard_spi.c
new file mode 100644
index 0000000000000000000000000000000000000000..ceeff4d204457b2bfa2c6c202712a6547d9807c7
--- /dev/null
+++ b/drivers/sdcard_spi/sdcard_spi.c
@@ -0,0 +1,1086 @@
+/*
+ * Copyright (C) 2016 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_sdcard_spi
+ * @{
+ *
+ * @file
+ * @brief       low level driver for accessing sd-cards via spi interface.
+ *
+ * @author      Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ *
+ * @}
+ */
+#define ENABLE_DEBUG (0)
+#include "debug.h"
+#include "sdcard_spi_internal.h"
+#include "sdcard_spi.h"
+#include "sdcard_spi_params.h"
+#include "periph/spi.h"
+#include "periph/gpio.h"
+#include "xtimer.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+static inline void _select_card_spi(sdcard_spi_t *card);
+static inline void _unselect_card_spi(sdcard_spi_t *card);
+static inline char _wait_for_r1(sdcard_spi_t *card, int32_t max_retries);
+static inline void _send_dummy_byte(sdcard_spi_t *card);
+static inline bool _wait_for_not_busy(sdcard_spi_t *card, int32_t max_retries);
+static inline bool _wait_for_token(sdcard_spi_t *card, char token, int32_t max_retries);
+static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_state_t state);
+static sd_rw_response_t _read_cid(sdcard_spi_t *card);
+static sd_rw_response_t _read_csd(sdcard_spi_t *card);
+static sd_rw_response_t _read_data_packet(sdcard_spi_t *card, char token, char *data, int size);
+static sd_rw_response_t _write_data_packet(sdcard_spi_t *card, char token, char *data, int size);
+
+/* CRC-7 (polynomial: x^7 + x^3 + 1) LSB of CRC-7 in a 8-bit variable is always 1*/
+static char _crc_7(const char *data, int n);
+
+/* CRC-16 (CRC-CCITT) (polynomial: x^16 + x^12 + x^5 + x^1) */
+static uint16_t _crc_16(const char *data, size_t n);
+
+/* use this transfer method instead of _transfer_bytes to force the use of 0xFF as dummy bytes */
+static inline int _transfer_bytes(sdcard_spi_t *card, char *out, char *in, unsigned int length);
+
+/* uses bitbanging for spi communication which allows to enable pull-up on the miso pin for
+greater card compatibility on platforms that don't have a hw pull up installed */
+static inline int _sw_spi_rxtx_byte(sdcard_spi_t *card, char out, char *in);
+
+/* wrapper for default spi_transfer_byte function */
+static inline int _hw_spi_rxtx_byte(sdcard_spi_t *card, char out, char *in);
+
+/* function pointer to switch to hw spi mode after init sequence */
+static int (*_dyn_spi_rxtx_byte)(sdcard_spi_t *card, char out, char *in);
+
+int sdcard_spi_init(sdcard_spi_t *card, const sdcard_spi_params_t *params)
+{
+    sd_init_fsm_state_t state = SD_INIT_START;
+    memcpy(&card->params, params, sizeof(sdcard_spi_params_t));
+
+    do {
+        state = _init_sd_fsm_step(card, state);
+    } while (state != SD_INIT_FINISH);
+
+    if (card->card_type != SD_UNKNOWN) {
+        card->init_done = true;
+        return SDCARD_SPI_OK;
+    }
+    card->init_done = false;
+    return SDCARD_SPI_INIT_ERROR;
+}
+
+static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_state_t state)
+{
+    switch (state) {
+
+        case SD_INIT_START:
+            DEBUG("SD_INIT_START\n");
+
+            if ((gpio_init(card->params.mosi, GPIO_OUT) == 0) &&
+                (gpio_init(card->params.clk,  GPIO_OUT) == 0) &&
+                (gpio_init(card->params.cs,   GPIO_OUT) == 0) &&
+                (gpio_init(card->params.miso, GPIO_IN) == 0) &&
+                ( (card->params.power == GPIO_UNDEF) ||
+                  (gpio_init(card->params.power, GPIO_OUT) == 0)) ) {
+
+                DEBUG("gpio_init(): [OK]\n");
+                xtimer_init();
+                return SD_INIT_SPI_POWER_SEQ;
+            }
+
+            DEBUG("gpio_init(): [ERROR]\n");
+            return SD_INIT_CARD_UNKNOWN;
+
+        case SD_INIT_SPI_POWER_SEQ:
+            DEBUG("SD_INIT_SPI_POWER_SEQ\n");
+
+            if (card->params.power != GPIO_UNDEF) {
+                gpio_write(card->params.power, card->params.power_act_high);
+                xtimer_usleep(SD_CARD_WAIT_AFTER_POWER_UP_US);
+            }
+
+            gpio_set(card->params.mosi);
+            gpio_set(card->params.cs);   /* unselect sdcard for power up sequence */
+
+            /* powersequence: perform at least 74 clockcycles with mosi_pin being high
+             * (same as sending dummy bytes with 0xFF) */
+            for (int i = 0; i < SD_POWERSEQUENCE_CLOCK_COUNT; i += 1) {
+                gpio_set(card->params.clk);
+                xtimer_usleep(SD_CARD_PREINIT_CLOCK_PERIOD_US/2);
+                gpio_clear(card->params.clk);
+                xtimer_usleep(SD_CARD_PREINIT_CLOCK_PERIOD_US/2);
+            }
+            return SD_INIT_SEND_CMD0;
+
+        case SD_INIT_SEND_CMD0:
+            DEBUG("SD_INIT_SEND_CMD0\n");
+
+            gpio_clear(card->params.mosi);
+            gpio_clear(card->params.cs);   /* select sdcard for cmd0 */
+
+            /* use soft-spi to perform init command to allow use of internal pull-ups on miso */
+            _dyn_spi_rxtx_byte = &_sw_spi_rxtx_byte;
+
+            _select_card_spi(card);
+            char cmd0_r1 = sdcard_spi_send_cmd(card, SD_CMD_0, SD_CMD_NO_ARG, INIT_CMD0_RETRY_CNT);
+            if (R1_VALID(cmd0_r1) && !R1_ERROR(cmd0_r1) && R1_IDLE_BIT_SET(cmd0_r1)) {
+                DEBUG("CMD0: [OK]\n");
+                _unselect_card_spi(card);
+
+                if (spi_init_master(card->params.spi_dev, SD_CARD_SPI_MODE,
+                                    SD_CARD_SPI_SPEED_PREINIT) == 0) {
+                    DEBUG("spi_init_master(): [OK]\n");
+
+                    /* switch to hw spi since sd card is now in real spi mode */
+                    _dyn_spi_rxtx_byte = &_hw_spi_rxtx_byte;
+                    return SD_INIT_ENABLE_CRC;
+                }
+
+                DEBUG("spi_init_master(): [ERROR]\n");
+                return SD_INIT_CARD_UNKNOWN;
+            }
+
+            _unselect_card_spi(card);
+            return SD_INIT_CARD_UNKNOWN;
+
+        case SD_INIT_ENABLE_CRC:
+            DEBUG("SD_INIT_ENABLE_CRC\n");
+            _select_card_spi(card);
+            char r1 = sdcard_spi_send_cmd(card, SD_CMD_59, SD_CMD_59_ARG_EN, INIT_CMD_RETRY_CNT);
+            if (R1_VALID(r1) && !R1_ERROR(r1)) {
+                DEBUG("CMD59: [OK]\n");
+                _unselect_card_spi(card);
+                return SD_INIT_SEND_CMD8;
+            }
+            _unselect_card_spi(card);
+            return SD_INIT_CARD_UNKNOWN;
+
+        case SD_INIT_SEND_CMD8:
+            DEBUG("SD_INIT_SEND_CMD8\n");
+            _select_card_spi(card);
+            int cmd8_arg = (SD_CMD_8_VHS_2_7_V_TO_3_6_V << 8) | SD_CMD_8_CHECK_PATTERN;
+            char cmd8_r1 = sdcard_spi_send_cmd(card, SD_CMD_8, cmd8_arg, INIT_CMD_RETRY_CNT);
+
+            if (R1_VALID(cmd8_r1) && !R1_ERROR(cmd8_r1)) {
+                DEBUG("CMD8: [OK] --> reading remaining bytes for R7\n");
+
+                char r7[4];
+
+                if (_transfer_bytes(card, 0, &r7[0], sizeof(r7)) == sizeof(r7)) {
+                    DEBUG("R7 response: 0x%02x 0x%02x 0x%02x 0x%02x\n", r7[0], r7[1], r7[2], r7[3]);
+                    /* check if lower 12 bits (voltage range and check pattern) of response and arg
+                       are equal to verify compatibility and communication is working properly */
+                    if (((r7[2] & 0x0F) == ((cmd8_arg >> 8) & 0x0F)) &&
+                        (r7[3] == (cmd8_arg & 0xFF))) {
+                        DEBUG("CMD8: [R7 MATCH]\n");
+                        return SD_INIT_SEND_ACMD41_HCS;
+                    }
+
+                    DEBUG("CMD8: [R7 MISMATCH]\n");
+                    _unselect_card_spi(card);
+                    return SD_INIT_CARD_UNKNOWN;;
+                }
+
+                DEBUG("CMD8: _transfer_bytes (R7): [ERROR]\n");
+                return SD_INIT_CARD_UNKNOWN;
+            }
+
+            DEBUG("CMD8: [ERROR / NO RESPONSE]\n");
+            return SD_INIT_SEND_ACMD41;
+
+        case SD_INIT_CARD_UNKNOWN:
+            DEBUG("SD_INIT_CARD_UNKNOWN\n");
+            card->card_type = SD_UNKNOWN;
+            return SD_INIT_FINISH;
+
+        case SD_INIT_SEND_ACMD41_HCS:
+            DEBUG("SD_INIT_SEND_ACMD41_HCS\n");
+            int acmd41_hcs_retries = 0;
+            do {
+                char acmd41hcs_r1 = sdcard_spi_send_acmd(card, SD_CMD_41, SD_ACMD_41_ARG_HC, 0);
+                if (R1_VALID(acmd41hcs_r1) && !R1_ERROR(acmd41hcs_r1) &&
+                    !R1_IDLE_BIT_SET(acmd41hcs_r1)) {
+                    DEBUG("ACMD41: [OK]\n");
+                    return SD_INIT_SEND_CMD58;
+                }
+                acmd41_hcs_retries++;
+            } while (INIT_CMD_RETRY_CNT < 0 || acmd41_hcs_retries <= INIT_CMD_RETRY_CNT);;
+            _unselect_card_spi(card);
+            return SD_INIT_CARD_UNKNOWN;
+
+        case SD_INIT_SEND_ACMD41:
+            DEBUG("SD_INIT_SEND_ACMD41\n");
+            int acmd41_retries = 0;
+            do {
+                char acmd41_r1 = sdcard_spi_send_acmd(card, SD_CMD_41, SD_CMD_NO_ARG, 0);
+                if (R1_VALID(acmd41_r1) && !R1_ERROR(acmd41_r1) && !R1_IDLE_BIT_SET(acmd41_r1)) {
+                    DEBUG("ACMD41: [OK]\n");
+                    card->use_block_addr = false;
+                    card->card_type = SD_V1;
+                    return SD_INIT_SEND_CMD16;
+                }
+                acmd41_retries++;
+            } while (INIT_CMD_RETRY_CNT < 0 || acmd41_retries <= INIT_CMD_RETRY_CNT);
+
+            DEBUG("ACMD41: [ERROR]\n");
+            return SD_INIT_SEND_CMD1;
+
+        case SD_INIT_SEND_CMD1:
+            DEBUG("SD_INIT_SEND_CMD1\n");
+            DEBUG("COULD TRY CMD1 (for MMC-card)-> currently not suported\n");
+            _unselect_card_spi(card);
+            return SD_INIT_CARD_UNKNOWN;
+
+        case SD_INIT_SEND_CMD58:
+            DEBUG("SD_INIT_SEND_CMD58\n");
+            char cmd58_r1 = sdcard_spi_send_cmd(card, SD_CMD_58, SD_CMD_NO_ARG, INIT_CMD_RETRY_CNT);
+            if (R1_VALID(cmd58_r1) && !R1_ERROR(cmd58_r1)) {
+                DEBUG("CMD58: [OK]\n");
+                card->card_type = SD_V2;
+
+                char r3[4];
+                if (_transfer_bytes(card, 0, r3, sizeof(r3)) == sizeof(r3)) {
+                    uint32_t ocr = ((uint32_t)r3[0] << (3 * 8)) |
+                                   ((uint32_t)r3[1] << (2 * 8)) | (r3[2] << 8) | r3[3];
+                    DEBUG("R3 RESPONSE: 0x%02x 0x%02x 0x%02x 0x%02x\n", r3[0], r3[1], r3[2], r3[3]);
+                    DEBUG("OCR: 0x%"PRIx32"\n", ocr);
+
+                    if ((ocr & SYSTEM_VOLTAGE) != 0) {
+                        DEBUG("OCR: SYS VOLTAGE SUPPORTED\n");
+
+                        if ((ocr & OCR_POWER_UP_STATUS) != 0) { //if power up outine is finished
+                            DEBUG("OCR: POWER UP ROUTINE FINISHED\n");
+                            if ((ocr & OCR_CCS) != 0) {         //if sd card is sdhc
+                                DEBUG("OCR: CARD TYPE IS SDHC (SD_V2 with block adressing)\n");
+                                card->use_block_addr = true;
+                                _unselect_card_spi(card);
+                                return SD_INIT_READ_CID;
+                            }
+
+                            DEBUG("OCR: CARD TYPE IS SDSC (SD_v2 with byte adressing)\n");
+                            card->use_block_addr = false;
+                            return SD_INIT_SEND_CMD16;
+                        }
+
+                        DEBUG("OCR: POWER UP ROUTINE NOT FINISHED!\n");
+                        /* poll status till power up is finished */
+                        return SD_INIT_SEND_CMD58;
+                    }
+
+                    DEBUG("OCR: SYS VOLTAGE NOT SUPPORTED!\n");
+                }
+
+                DEBUG("CMD58 response: [READ ERROR]\n");
+            }
+
+            DEBUG("CMD58: [ERROR]\n");
+            _unselect_card_spi(card);
+            return SD_INIT_CARD_UNKNOWN;
+
+        case SD_INIT_SEND_CMD16:
+            DEBUG("SD_INIT_SEND_CMD16\n");
+            char r1_16 = sdcard_spi_send_cmd(card, SD_CMD_16, SD_HC_BLOCK_SIZE, INIT_CMD_RETRY_CNT);
+            if (R1_VALID(r1_16) && !R1_ERROR(r1_16)) {
+                DEBUG("CARD TYPE IS SDSC (SD_V1 with byte adressing)\n");
+                _unselect_card_spi(card);
+                return SD_INIT_READ_CID;
+            }
+            else {
+                _unselect_card_spi(card);
+                return SD_INIT_CARD_UNKNOWN;
+            }
+
+        case SD_INIT_READ_CID:
+            DEBUG("SD_INIT_READ_CID\n");
+            if (_read_cid(card) == SD_RW_OK) {
+                return SD_INIT_READ_CSD;
+            }
+            else {
+                DEBUG("reading cid register failed!\n");
+                return SD_INIT_CARD_UNKNOWN;
+            }
+
+        case SD_INIT_READ_CSD:
+            DEBUG("SD_INIT_READ_CSD\n");
+            if (_read_csd(card) == SD_RW_OK) {
+                if (card->csd_structure == SD_CSD_V1) {
+                    DEBUG("csd_structure is version 1\n");
+                }
+                else if (card->csd_structure == SD_CSD_V2) {
+                    DEBUG("csd_structure is version 2\n");
+                }
+                return SD_INIT_SET_MAX_SPI_SPEED;
+            }
+            else {
+                DEBUG("reading csd register failed!\n");
+                return SD_INIT_CARD_UNKNOWN;
+            }
+
+        case SD_INIT_SET_MAX_SPI_SPEED:
+            DEBUG("SD_INIT_SET_MAX_SPI_SPEED\n");
+            if (spi_init_master(card->params.spi_dev, SD_CARD_SPI_MODE,
+                                SD_CARD_SPI_SPEED_POSTINIT) == 0) {
+                DEBUG("SD_INIT_SET_MAX_SPI_SPEED: [OK]\n");
+                return SD_INIT_FINISH;
+            }
+            else {
+                DEBUG("SD_INIT_SET_MAX_SPI_SPEED: [ERROR]\n");
+                return SD_INIT_CARD_UNKNOWN;
+            }
+
+        default:
+            DEBUG("SD-INIT-FSM REACHED INVALID STATE!\n");
+            return SD_INIT_CARD_UNKNOWN;
+
+    }
+}
+
+static inline bool _wait_for_token(sdcard_spi_t *card, char token, int32_t max_retries)
+{
+    int tried = 0;
+
+    do {
+        char read_byte = 0;
+        if (spi_transfer_byte(card->params.spi_dev, SD_CARD_DUMMY_BYTE, &read_byte) == 1) {
+            if (read_byte == token) {
+                DEBUG("_wait_for_token: [MATCH]\n");
+                return true;
+            }
+            else {
+                DEBUG("_wait_for_token: [NO MATCH] (0x%02x)\n", read_byte);
+            }
+        }
+        else {
+            DEBUG("spi_transfer_byte: [FAILED]\n");
+            return false;
+        }
+
+        tried++;
+    } while ((max_retries < 0) || (tried <= max_retries));
+
+    return false;
+}
+
+static inline void _send_dummy_byte(sdcard_spi_t *card)
+{
+    char read_byte;
+
+    if (_dyn_spi_rxtx_byte(card, SD_CARD_DUMMY_BYTE, &read_byte) == 1) {
+        DEBUG("_send_dummy_byte:echo: 0x%02x\n", read_byte);
+    }
+    else {
+        DEBUG("_send_dummy_byte:_dyn_spi_rxtx_byte: [FAILED]\n");
+    }
+}
+
+static inline bool _wait_for_not_busy(sdcard_spi_t *card, int32_t max_retries)
+{
+    char read_byte;
+    int tried = 0;
+
+    do {
+        if (_dyn_spi_rxtx_byte(card, SD_CARD_DUMMY_BYTE, &read_byte) == 1) {
+            if (read_byte == 0xFF) {
+                DEBUG("_wait_for_not_busy: [OK]\n");
+                return true;
+            }
+            else {
+                DEBUG("_wait_for_not_busy: [BUSY]\n");
+            }
+        }
+        else {
+            DEBUG("_wait_for_not_busy:_dyn_spi_rxtx_byte: [FAILED]\n");
+            return false;
+        }
+
+        tried++;
+    } while ((max_retries < 0) || (tried <= max_retries));
+
+    DEBUG("_wait_for_not_busy: [FAILED]\n");
+    return false;
+}
+
+static char _crc_7(const char *data, int n)
+{
+    char crc = 0;
+
+    for (int i = 0; i < n; i++) {
+        char d = data[i];
+        for (int j = 0; j < 8; j++) {
+            crc <<= 1;
+            if ((d & 0x80) ^ (crc & 0x80)) {
+                crc ^= 0x09;
+            }
+            d <<= 1;
+        }
+    }
+    return (crc << 1) | 1;
+}
+
+static uint16_t _crc_16(const char *data, size_t n)
+{
+    uint16_t crc = 0;
+
+    for (size_t i = 0; i < n; i++) {
+        crc = (uint8_t)(crc >> 8) | (crc << 8);
+        crc ^= data[i];
+        crc ^= (uint8_t)(crc & 0xFF) >> 4;
+        crc ^= crc << 12;
+        crc ^= (crc & 0xFF) << 5;
+    }
+    return crc;
+}
+
+char sdcard_spi_send_cmd(sdcard_spi_t *card, char sd_cmd_idx, uint32_t argument, int32_t max_retry)
+{
+    int try_cnt = 0;
+    char r1_resu;
+    char cmd_data[6];
+
+    cmd_data[0] = SD_CMD_PREFIX_MASK | sd_cmd_idx;
+    cmd_data[1] = argument >> (3 * 8);
+    cmd_data[2] = (argument >> (2 * 8)) & 0xFF;
+    cmd_data[3] = (argument >> 8) & 0xFF;
+    cmd_data[4] = argument & 0xFF;
+    cmd_data[5] = _crc_7(cmd_data, sizeof(cmd_data) - 1);
+
+    char echo[sizeof(cmd_data)];
+
+    do {
+        DEBUG("sdcard_spi_send_cmd: CMD%02d (0x%08lx) (retry %d)\n", sd_cmd_idx, argument, try_cnt);
+
+        if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_CNT)) {
+            DEBUG("sdcard_spi_send_cmd: timeout while waiting for bus to be not busy!\n");
+            r1_resu = SD_INVALID_R1_RESPONSE;
+            try_cnt++;
+            continue;
+        }
+
+        if (_transfer_bytes(card, cmd_data, echo, sizeof(cmd_data)) != sizeof(cmd_data)) {
+            DEBUG("sdcard_spi_send_cmd: _transfer_bytes: send cmd [%d]: [ERROR]\n", sd_cmd_idx);
+            r1_resu = SD_INVALID_R1_RESPONSE;
+            try_cnt++;
+            continue;
+        }
+
+        DEBUG("CMD%02d echo: ", sd_cmd_idx);
+        for (int i = 0; i < sizeof(echo); i++) {
+            DEBUG("0x%02X ", echo[i]);
+        }
+        DEBUG("\n");
+
+        /* received byte after cmd12 is a dummy byte and should be ignored */
+        if (sd_cmd_idx == SD_CMD_12) {
+            _send_dummy_byte(card);
+        }
+
+        r1_resu = _wait_for_r1(card, R1_POLLING_RETRY_CNT);
+
+        if (R1_VALID(r1_resu)) {
+            break;
+        }
+        else {
+            DEBUG("sdcard_spi_send_cmd: R1_TIMEOUT (0x%02x)\n", r1_resu);
+            r1_resu = SD_INVALID_R1_RESPONSE;
+        }
+        try_cnt++;
+
+    } while ((max_retry < 0) || (try_cnt <= max_retry));
+
+    return r1_resu;
+}
+
+char sdcard_spi_send_acmd(sdcard_spi_t *card, char sd_cmd_idx, uint32_t argument, int32_t max_retry)
+{
+    int err_cnt = 0;
+    char r1_resu;
+
+    do {
+        DEBUG("sdcard_spi_send_acmd: CMD%02d (0x%08lx)(retry %d)\n", sd_cmd_idx, argument, err_cnt);
+        r1_resu = sdcard_spi_send_cmd(card, SD_CMD_55, SD_CMD_NO_ARG, 0);
+        if (R1_VALID(r1_resu) && !R1_ERROR(r1_resu)) {
+            r1_resu = sdcard_spi_send_cmd(card, sd_cmd_idx, argument, 0);
+
+            if (R1_VALID(r1_resu) && !R1_ERROR(r1_resu)) {
+                return r1_resu;
+            }
+            else {
+                DEBUG("ACMD%02d: [ERROR / NO RESPONSE]\n", sd_cmd_idx);
+                err_cnt++;
+            }
+        }
+        else {
+            DEBUG("CMD55: [ERROR / NO RESPONSE]\n");
+            err_cnt++;
+        }
+
+    } while ((max_retry < 0) || (err_cnt <= max_retry));
+
+    DEBUG("sdcard_spi_send_acmd: [TIMEOUT]\n");
+    return r1_resu;
+}
+
+static inline char _wait_for_r1(sdcard_spi_t *card, int32_t max_retries)
+{
+    int tried = 0;
+    char r1;
+
+    do {
+        if (_dyn_spi_rxtx_byte(card, SD_CARD_DUMMY_BYTE, &r1) != 1) {
+            DEBUG("_wait_for_r1: _dyn_spi_rxtx_byte:[ERROR]\n");
+            tried++;
+            continue;
+        }
+        else {
+            DEBUG("_wait_for_r1: r1=0x%02x\n", r1);
+        }
+
+        if (R1_VALID(r1)) {
+            DEBUG("_wait_for_r1: R1_VALID\n");
+            return r1;
+        }
+        tried++;
+    } while ((max_retries < 0) || (tried <= max_retries));
+
+    DEBUG("_wait_for_r1: [TIMEOUT]\n");
+    return r1;
+}
+
+void _select_card_spi(sdcard_spi_t *card)
+{
+    spi_acquire(card->params.spi_dev);
+    gpio_clear(card->params.cs);
+}
+
+void _unselect_card_spi(sdcard_spi_t *card)
+{
+    gpio_set(card->params.cs);
+    spi_release(card->params.spi_dev);
+}
+
+static inline int _sw_spi_rxtx_byte(sdcard_spi_t *card, char out, char *in){
+    char rx = 0;
+    int i = 7;
+    for(; i >= 0; i--){
+        if( ((out >> (i)) & 0x01) == 1){
+            gpio_set(card->params.mosi);
+        }else{
+            gpio_clear(card->params.mosi);
+        }
+        xtimer_usleep(SD_CARD_PREINIT_CLOCK_PERIOD_US/2);
+        gpio_set(card->params.clk);
+        rx = (rx | ((gpio_read(card->params.miso) > 0) << i));
+        xtimer_usleep(SD_CARD_PREINIT_CLOCK_PERIOD_US/2);
+        gpio_clear(card->params.clk);
+    }
+    *in = rx;
+    return 1;
+}
+
+static inline int _hw_spi_rxtx_byte(sdcard_spi_t *card, char out, char *in){
+    return spi_transfer_byte(card->params.spi_dev, out, in);
+}
+
+static inline int _transfer_bytes(sdcard_spi_t *card, char *out, char *in, unsigned int length){
+    int trans_ret;
+    unsigned trans_bytes = 0;
+    char in_temp;
+
+    for (trans_bytes = 0; trans_bytes < length; trans_bytes++) {
+        if (out != NULL) {
+            trans_ret = _dyn_spi_rxtx_byte(card, out[trans_bytes], &in_temp);
+        }
+        else {
+            trans_ret = _dyn_spi_rxtx_byte(card, SD_CARD_DUMMY_BYTE, &in_temp);
+        }
+        if (trans_ret < 0) {
+            return trans_ret;
+        }
+        if (in != NULL) {
+            in[trans_bytes] = in_temp;
+        }
+    }
+
+    return trans_bytes;
+}
+
+static sd_rw_response_t _read_data_packet(sdcard_spi_t *card, char token, char *data, int size)
+{
+    DEBUG("_read_data_packet: size: %d\n", size);
+    if (_wait_for_token(card, token, SD_DATA_TOKEN_RETRY_CNT) == true) {
+        DEBUG("_read_data_packet: [GOT TOKEN]\n");
+    }
+    else {
+        DEBUG("_read_data_packet: [GOT NO TOKEN]\n");
+        return SD_RW_NO_TOKEN;
+    }
+
+    if (_transfer_bytes(card, NULL, data, size) == size) {
+
+        DEBUG("_read_data_packet: data: ");
+        for (int i = 0; i < size; i++) {
+            DEBUG("0x%02X ", data[i]);
+        }
+        DEBUG("\n");
+
+        char crc_bytes[2];
+        if (_transfer_bytes(card, 0, crc_bytes, sizeof(crc_bytes)) == sizeof(crc_bytes)) {
+            uint16_t data__crc_16 = (crc_bytes[0] << 8) | crc_bytes[1];
+
+            if (_crc_16(data, size) == data__crc_16) {
+                DEBUG("_read_data_packet: [OK]\n");
+                return SD_RW_OK;
+            }
+            else {
+                DEBUG("_read_data_packet: [CRC_MISMATCH]\n");
+                return SD_RW_CRC_MISMATCH;
+            }
+        }
+
+        DEBUG("_read_data_packet: _transfer_bytes [RX_TX_ERROR] (while transmitting crc)\n");
+        return SD_RW_RX_TX_ERROR;
+    }
+
+    DEBUG("_read_data_packet: _transfer_bytes [RX_TX_ERROR] (while transmitting payload)\n");
+    return SD_RW_RX_TX_ERROR;
+}
+
+static inline int _read_blocks(sdcard_spi_t *card, int cmd_idx, int bladdr, char *data, int blsz,
+                               int nbl, sd_rw_response_t *state)
+{
+    _select_card_spi(card);
+    int reads = 0;
+
+    uint32_t addr = card->use_block_addr ? bladdr : (bladdr * SD_HC_BLOCK_SIZE);
+    char cmd_r1_resu = sdcard_spi_send_cmd(card, cmd_idx, addr, SD_BLOCK_READ_CMD_RETRIES);
+
+    if (R1_VALID(cmd_r1_resu) && !R1_ERROR(cmd_r1_resu)) {
+        DEBUG("_read_blocks: send CMD%d: [OK]\n", cmd_idx);
+
+        for (int i = 0; i < nbl; i++) {
+            *state = _read_data_packet(card, SD_DATA_TOKEN_CMD_17_18_24, &(data[i * blsz]), blsz);
+
+            if (*state != SD_RW_OK) {
+                DEBUG("_read_blocks: _read_data_packet: [FAILED]\n");
+                _unselect_card_spi(card);
+                return reads;
+            }
+            else {
+                reads++;
+            }
+        }
+
+        /* if this was a multi-block read */
+        if (cmd_idx == SD_CMD_18) {
+            cmd_r1_resu = sdcard_spi_send_cmd(card, SD_CMD_12, 0, 1);
+
+            if (R1_VALID(cmd_r1_resu) && !R1_ERROR(cmd_r1_resu)) {
+                DEBUG("_read_blocks: read multi (%d) blocks [OK]\n", nbl);
+                *state = SD_RW_OK;
+            }
+            else {
+                DEBUG("_read_blocks: send CMD12: [RX_TX_ERROR]\n");
+                *state =  SD_RW_RX_TX_ERROR;
+            }
+        }
+        else {
+            DEBUG("_read_blocks: read single block [OK]\n");
+            *state = SD_RW_OK;
+        }
+    }
+    else {
+        DEBUG("_read_blocks: send CMD%d: [RX_TX_ERROR]\n", cmd_idx);
+        *state = SD_RW_RX_TX_ERROR;
+    }
+
+    _unselect_card_spi(card);
+    return reads;
+}
+
+int sdcard_spi_read_blocks(sdcard_spi_t *card, int blockaddr, char *data, int blocksize,
+                           int nblocks, sd_rw_response_t *state)
+{
+    if (nblocks > 1) {
+        return _read_blocks(card, SD_CMD_18, blockaddr, data, blocksize, nblocks, state);
+    }
+    else {
+        return _read_blocks(card, SD_CMD_17, blockaddr, data, blocksize, nblocks, state);
+    }
+}
+
+static sd_rw_response_t _write_data_packet(sdcard_spi_t *card, char token, char *data, int size)
+{
+
+    if (spi_transfer_byte(card->params.spi_dev, token, 0) == 1) {
+
+        if (_transfer_bytes(card, data, 0, size) == size) {
+
+            uint16_t data__crc_16 = _crc_16(data, size);
+            char crc[sizeof(uint16_t)] = { data__crc_16 >> 8, data__crc_16 & 0xFF };
+
+            if (_transfer_bytes(card, crc, 0, sizeof(crc)) == sizeof(crc)) {
+
+                char data_response;
+
+                if (spi_transfer_byte(card->params.spi_dev, SD_CARD_DUMMY_BYTE,
+                                      &data_response) == 1) {
+
+                    DEBUG("_write_data_packet: DATA_RESPONSE: 0x%02x\n", data_response);
+
+                    if (DATA_RESPONSE_IS_VALID(data_response)) {
+
+                        if (DATA_RESPONSE_ACCEPTED(data_response)) {
+                            DEBUG("_write_data_packet: DATA_RESPONSE: [OK]\n");
+                            return SD_RW_OK;
+                        }
+                        else {
+
+                            if (DATA_RESPONSE_WRITE_ERR(data_response)) {
+                                DEBUG("_write_data_packet: DATA_RESPONSE: [WRITE_ERROR]\n");
+                            }
+
+                            if (DATA_RESPONSE_CRC_ERR(data_response)) {
+                                DEBUG("_write_data_packet: DATA_RESPONSE: [CRC_ERROR]\n");
+                            }
+                            return SD_RW_WRITE_ERROR;
+                        }
+
+                    }
+                    else {
+                        DEBUG("_write_data_packet: DATA_RESPONSE invalid\n");
+                        return SD_RW_RX_TX_ERROR;
+                    }
+
+
+                }
+                else {
+                    DEBUG("_write_data_packet: [RX_TX_ERROR] (while transmitting data response)\n");
+                    return SD_RW_RX_TX_ERROR;
+                }
+            }
+            else {
+                DEBUG("_write_data_packet: [RX_TX_ERROR] (while transmitting CRC16)\n");
+                return SD_RW_RX_TX_ERROR;
+            }
+        }
+        else {
+            DEBUG("_write_data_packet: [RX_TX_ERROR] (while transmitting payload)\n");
+            return SD_RW_RX_TX_ERROR;
+        }
+
+    }
+    else {
+        DEBUG("_write_data_packet: [RX_TX_ERROR] (while transmitting token)\n");
+        return SD_RW_RX_TX_ERROR;
+    }
+}
+
+static inline int _write_blocks(sdcard_spi_t *card, char cmd_idx, int bladdr, char *data, int blsz,
+                                int nbl, sd_rw_response_t *state)
+{
+    _select_card_spi(card);
+    int written = 0;
+
+    uint32_t addr = card->use_block_addr ? bladdr : (bladdr * SD_HC_BLOCK_SIZE);
+    char cmd_r1_resu = sdcard_spi_send_cmd(card, cmd_idx, addr, SD_BLOCK_WRITE_CMD_RETRIES);
+
+    if (R1_VALID(cmd_r1_resu) && !R1_ERROR(cmd_r1_resu)) {
+        DEBUG("_write_blocks: send CMD%d: [OK]\n", cmd_idx);
+
+        int token;
+        if (cmd_idx == SD_CMD_25) {
+            token = SD_DATA_TOKEN_CMD_25;
+        }
+        else {
+            token = SD_DATA_TOKEN_CMD_17_18_24;
+        }
+
+        for (int i = 0; i < nbl; i++) {
+            sd_rw_response_t write_resu = _write_data_packet(card, token, &(data[i * blsz]), blsz);
+            if (write_resu != SD_RW_OK) {
+                DEBUG("_write_blocks: _write_data_packet: [FAILED]\n");
+                _unselect_card_spi(card);
+                *state = write_resu;
+                return written;
+            }
+            if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_CNT)) {
+                DEBUG("_write_blocks: _wait_for_not_busy: [FAILED]\n");
+                _unselect_card_spi(card);
+                *state = SD_RW_TIMEOUT;
+                return written;
+            }
+            written++;
+        }
+
+        /* if this is a multi-block write it is needed to issue a stop command*/
+        if (cmd_idx == SD_CMD_25) {
+            char data_response;
+            if (spi_transfer_byte(card->params.spi_dev, SD_DATA_TOKEN_CMD_25_STOP,
+                                  &data_response) == 1) {
+                DEBUG("_write_blocks: write multi (%d) blocks: [OK]\n", nbl);
+            }
+            else {
+                DEBUG("_write_blocks: spi_transfer_byte: [FAILED] (SD_DATA_TOKEN_CMD_25_STOP)\n");
+                _unselect_card_spi(card);
+                *state = SD_RW_RX_TX_ERROR;
+            }
+            _send_dummy_byte(card); //sd card needs dummy byte before we can wait for not-busy state
+            if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_CNT)) {
+                _unselect_card_spi(card);
+                *state = SD_RW_TIMEOUT;
+            }
+        }
+        else {
+            DEBUG("_write_blocks: write single block: [OK]\n");
+            *state = SD_RW_OK;
+        }
+
+        _unselect_card_spi(card);
+        return written;
+    }
+    else {
+        DEBUG("_write_blocks: sdcard_spi_send_cmd: SD_CMD_ERROR_NO_RESP\n");
+        _unselect_card_spi(card);
+        *state = SD_RW_RX_TX_ERROR;
+        return written;
+    }
+}
+
+int sdcard_spi_write_blocks(sdcard_spi_t *card, int blockaddr, char *data, int blocksize,
+                            int nblocks, sd_rw_response_t *state)
+{
+    if (nblocks > 1) {
+        return _write_blocks(card, SD_CMD_25, blockaddr, data, blocksize, nblocks, state);
+    }
+    else {
+        return _write_blocks(card, SD_CMD_24, blockaddr, data, blocksize, nblocks, state);
+    }
+}
+
+sd_rw_response_t _read_cid(sdcard_spi_t *card)
+{
+    char cid_raw_data[SD_SIZE_OF_CID_AND_CSD_REG];
+    sd_rw_response_t state;
+    int nbl = _read_blocks(card, SD_CMD_10, 0, cid_raw_data, SD_SIZE_OF_CID_AND_CSD_REG,
+                           SD_BLOCKS_FOR_REG_READ, &state);
+
+    DEBUG("_read_cid: _read_blocks: nbl=%d state=%d\n", nbl, state);
+    DEBUG("_read_cid: cid_raw_data: ");
+    for (int i = 0; i < sizeof(cid_raw_data); i++) {
+        DEBUG("0x%02X ", cid_raw_data[i]);
+    }
+    DEBUG("\n");
+
+    char crc7 = _crc_7(&(cid_raw_data[0]), SD_SIZE_OF_CID_AND_CSD_REG - 1);
+    if (nbl == SD_BLOCKS_FOR_REG_READ) {
+        if (crc7 == cid_raw_data[SD_SIZE_OF_CID_AND_CSD_REG - 1]) {
+            card->cid.MID = cid_raw_data[0];
+            memcpy(&card->cid.OID[0], &cid_raw_data[1], SD_SIZE_OF_OID);
+            memcpy(&card->cid.PNM[0], &cid_raw_data[2], SD_SIZE_OF_PNM);
+            card->cid.PRV = cid_raw_data[8];
+            memcpy((char *)&card->cid.PSN, &cid_raw_data[9], 4);
+            card->cid.MDT = (cid_raw_data[13]<<4) | cid_raw_data[14];
+            card->cid.CID_CRC = cid_raw_data[15];
+            DEBUG("_read_cid: [OK]\n");
+            return SD_RW_OK;
+        }
+        else {
+            DEBUG("_read_cid: [SD_RW_CRC_MISMATCH] (data-crc: 0x%02x | calc-crc: 0x%02x)\n",
+                  cid_raw_data[SD_SIZE_OF_CID_AND_CSD_REG - 1], crc7);
+            return SD_RW_CRC_MISMATCH;
+        }
+    }
+    return state;
+}
+
+sd_rw_response_t _read_csd(sdcard_spi_t *card)
+{
+    char c[SD_SIZE_OF_CID_AND_CSD_REG];
+    sd_rw_response_t state;
+    int read_resu = _read_blocks(card, SD_CMD_9, 0, c, SD_SIZE_OF_CID_AND_CSD_REG,
+                                 SD_BLOCKS_FOR_REG_READ, &state);
+
+    DEBUG("_read_csd: _read_blocks: read_resu=%d state=%d\n", read_resu, state);
+    DEBUG("_read_csd: raw data: ");
+    for (int i = 0; i < sizeof(c); i++) {
+        DEBUG("0x%02X ", c[i]);
+    }
+    DEBUG("\n");
+
+    if (read_resu == SD_BLOCKS_FOR_REG_READ) {
+        if (_crc_7(c, SD_SIZE_OF_CID_AND_CSD_REG - 1) == c[SD_SIZE_OF_CID_AND_CSD_REG - 1]) {
+            if (SD_GET_CSD_STRUCTURE(c) == SD_CSD_V1) {
+                card->csd.v1.CSD_STRUCTURE = c[0]>>6;
+                card->csd.v1.TAAC          = c[1];
+                card->csd.v1.NSAC          = c[2];
+                card->csd.v1.TRAN_SPEED    = c[3];
+                card->csd.v1.CCC           = (c[4]<<4) | ((c[5] & 0xF0)>>4);
+                card->csd.v1.READ_BL_LEN   = (c[5] & 0x0F);
+                card->csd.v1.READ_BL_PARTIAL    = (c[6] &  (1<<7))>>7;
+                card->csd.v1.WRITE_BLK_MISALIGN = (c[6] &  (1<<6))>>6;
+                card->csd.v1.READ_BLK_MISALIGN  = (c[6] &  (1<<5))>>5;
+                card->csd.v1.DSR_IMP            = (c[6] &  (1<<4))>>4;
+                card->csd.v1.C_SIZE         = ((c[6]  & 0x03)<<10) | (c[7]<<2) | (c[8]>>6);
+                card->csd.v1.VDD_R_CURR_MIN = (c[8]   & 0x38)>>3;
+                card->csd.v1.VDD_R_CURR_MAX = (c[8]   & 0x07);
+                card->csd.v1.VDD_W_CURR_MIN = (c[9]   & 0xE0)>>5;
+                card->csd.v1.VDD_W_CURR_MAX = (c[9]   & 0x1C)>>2;
+                card->csd.v1.C_SIZE_MULT    = ((c[9]  & 0x03)<<1) | (c[10]>>7);
+                card->csd.v1.ERASE_BLK_EN   = (c[10]  & (1<<6))>>6;
+                card->csd.v1.SECTOR_SIZE    = ((c[10] & 0x3F)<<1) | (c[11]>>7);
+                card->csd.v1.WP_GRP_SIZE    = (c[11]  & 0x7F);
+                card->csd.v1.WP_GRP_ENABLE  = c[12]>>7;
+                card->csd.v1.R2W_FACTOR     = (c[12]   & 0x1C)>>2;
+                card->csd.v1.WRITE_BL_LEN   = (c[12]   & 0x03)<<2 | (c[13]>>6);
+                card->csd.v1.WRITE_BL_PARTIAL   = (c[13] & (1<<5))>>5;
+                card->csd.v1.FILE_FORMAT_GRP    = (c[14] & (1<<7))>>7;
+                card->csd.v1.COPY               = (c[14] & (1<<6))>>6;
+                card->csd.v1.PERM_WRITE_PROTECT = (c[14] & (1<<5))>>5;
+                card->csd.v1.TMP_WRITE_PROTECT  = (c[14] & (1<<4))>>4;
+                card->csd.v1.FILE_FORMAT        = (c[14] & 0x0C)>>2;
+                card->csd.v1.CSD_CRC                = c[15];
+                card->csd_structure = SD_CSD_V1;
+                return SD_RW_OK;
+            }
+            else if (SD_GET_CSD_STRUCTURE(c) == SD_CSD_V2) {
+                card->csd.v2.CSD_STRUCTURE = c[0]>>6;
+                card->csd.v2.TAAC          = c[1];
+                card->csd.v2.NSAC          = c[2];
+                card->csd.v2.TRAN_SPEED    = c[3];
+                card->csd.v2.CCC           = (c[4]<<4) | ((c[5] & 0xF0)>>4);
+                card->csd.v2.READ_BL_LEN   = (c[5] & 0x0F);
+                card->csd.v2.READ_BL_PARTIAL    = (c[6] & (1<<7))>>7;
+                card->csd.v2.WRITE_BLK_MISALIGN = (c[6] & (1<<6))>>6;
+                card->csd.v2.READ_BLK_MISALIGN  = (c[6] & (1<<5))>>5;
+                card->csd.v2.DSR_IMP            = (c[6] & (1<<4))>>4;
+                card->csd.v2.C_SIZE             = (((uint32_t)c[7] & 0x3F)<<16)
+                                                   | (c[8]<<8) | c[9];
+                card->csd.v2.ERASE_BLK_EN       = (c[10] & (1<<6))>>6;
+                card->csd.v2.SECTOR_SIZE        = (c[10] & 0x3F)<<1 | (c[11]>>7);
+                card->csd.v2.WP_GRP_SIZE        = (c[11] & 0x7F);
+                card->csd.v2.WP_GRP_ENABLE      = (c[12] & (1<<7))>> 7;
+                card->csd.v2.R2W_FACTOR         = (c[12] & 0x1C)>> 2;
+                card->csd.v2.WRITE_BL_LEN       = ((c[12] & 0x03)<<2) | (c[13]>>6);
+                card->csd.v2.WRITE_BL_PARTIAL   = (c[13] & (1<<5))>>5;
+                card->csd.v2.FILE_FORMAT_GRP    = (c[14] & (1<<7))>>7;
+                card->csd.v2.COPY               = (c[14] & (1<<6))>>6;
+                card->csd.v2.PERM_WRITE_PROTECT = (c[14] & (1<<5))>>5;
+                card->csd.v2.TMP_WRITE_PROTECT  = (c[14] & (1<<4))>>4;
+                card->csd.v2.FILE_FORMAT        = (c[14] & 0x0C)>>2;
+                card->csd.v2.CSD_CRC                = c[15];
+                card->csd_structure = SD_CSD_V2;
+                return SD_RW_OK;
+            }
+            else {
+                return SD_RW_NOT_SUPPORTED;
+            }
+
+        }
+        else {
+            return SD_RW_CRC_MISMATCH;
+        }
+    }
+    return state;
+}
+
+sd_rw_response_t sdcard_spi_read_sds(sdcard_spi_t *card, sd_status_t *sd_status){
+    _select_card_spi(card);
+    char sds_raw_data[SD_SIZE_OF_SD_STATUS];
+    char r1_resu = sdcard_spi_send_cmd(card, SD_CMD_55, SD_CMD_NO_ARG, 0);
+    _unselect_card_spi(card);
+    if (R1_VALID(r1_resu)) {
+        if(!R1_ERROR(r1_resu)){
+
+            sd_rw_response_t state;
+            int nbl = _read_blocks(card, SD_CMD_13, 0, sds_raw_data, SD_SIZE_OF_SD_STATUS,
+                                   SD_BLOCKS_FOR_REG_READ, &state);
+
+            DEBUG("sdcard_spi_read_sds: _read_blocks: nbl=%d state=%d\n", nbl, state);
+            DEBUG("sdcard_spi_read_sds: sds_raw_data: ");
+            for (int i = 0; i < sizeof(sds_raw_data); i++) {
+                DEBUG("0x%02X ", sds_raw_data[i]);
+            }
+            DEBUG("\n");
+
+            if (nbl == SD_BLOCKS_FOR_REG_READ) {
+                sd_status->DAT_BUS_WIDTH =  sds_raw_data[0] >> 6;
+                sd_status->SECURED_MODE  = (sds_raw_data[0] & (1<<5)) >> 5;
+                sd_status->SD_CARD_TYPE  = (sds_raw_data[2] << 8) | sds_raw_data[3];
+                sd_status->SIZE_OF_PROTECTED_AREA = ((uint32_t)sds_raw_data[4] << (3*8)) |
+                                                    ((uint32_t)sds_raw_data[5] << (2*8)) |
+                                                    (sds_raw_data[6] << 8    ) |
+                                                     sds_raw_data[7];
+                sd_status->SPEED_CLASS       = sds_raw_data[8];
+                sd_status->PERFORMANCE_MOVE  = sds_raw_data[9];
+                sd_status->AU_SIZE           = sds_raw_data[10] >> 4;
+                sd_status->ERASE_SIZE        = (sds_raw_data[11] << 8) | sds_raw_data[12];
+                sd_status->ERASE_TIMEOUT     = sds_raw_data[13] >> 2;
+                sd_status->ERASE_OFFSET      = sds_raw_data[13] & 0x03;
+                sd_status->UHS_SPEED_GRADE   = sds_raw_data[14] >> 4;
+                sd_status->UHS_AU_SIZE       = sds_raw_data[14] & 0x0F;
+                sd_status->VIDEO_SPEED_CLASS = sds_raw_data[15];
+                sd_status->VSC_AU_SIZE       = ((sds_raw_data[16] & 0x03) << 8)
+                                               | sds_raw_data[17];
+                sd_status->SUS_ADDR          = (sds_raw_data[18] << 14) |
+                                               (sds_raw_data[19] << 6 ) |
+                                               (sds_raw_data[20] >> 2 );
+                DEBUG("sdcard_spi_read_sds: [OK]\n");
+                return SD_RW_OK;
+            }
+            return state;
+        }
+        return SD_RW_RX_TX_ERROR;
+    }
+    return SD_RW_TIMEOUT;
+}
+
+uint64_t sdcard_spi_get_capacity(sdcard_spi_t *card)
+{
+    if (card->csd_structure == SD_CSD_V1) {
+        uint32_t block_len = (1 << card->csd.v1.READ_BL_LEN);
+        uint32_t mult = 1 << (card->csd.v1.C_SIZE_MULT + 2);
+        uint32_t blocknr = (card->csd.v1.C_SIZE + 1) * mult;
+        return blocknr * block_len;
+    }
+    else if (card->csd_structure == SD_CSD_V2) {
+        return (card->csd.v2.C_SIZE + 1) * (uint64_t)(SD_HC_BLOCK_SIZE << 10);
+    }
+    return 0;
+}
+
+uint32_t sdcard_spi_get_sector_count(sdcard_spi_t *card)
+{
+    return sdcard_spi_get_capacity(card) / SD_HC_BLOCK_SIZE;
+}
+
+uint32_t sdcard_spi_get_au_size(sdcard_spi_t *card)
+{
+    sd_status_t sds;
+    if(sdcard_spi_read_sds(card, &sds) == SD_RW_OK) {
+        if (sds.AU_SIZE < 0xB) {
+            return 1 << (13 + sds.AU_SIZE); /* sds->AU_SIZE = 1 maps to 16KB; 2 to 32KB etc.*/
+        }
+        else if (sds.AU_SIZE == 0xB) {
+            return 12 * SDCARD_SPI_IEC_KIBI * SDCARD_SPI_IEC_KIBI; /* 12 MB */
+        }
+        else if (sds.AU_SIZE == 0xC) {
+            return 1 << (12 + sds.AU_SIZE); /* 16 MB */
+        }
+        else if (sds.AU_SIZE == 0xD) {
+            return 24 * SDCARD_SPI_IEC_KIBI * SDCARD_SPI_IEC_KIBI; /* 24 MB */
+        }
+        else if (sds.AU_SIZE > 0xD) {
+            return 1 << (11 + sds.AU_SIZE); /* 32 MB or 64 MB */
+        }
+    }
+    return 0; /* AU_SIZE is not defined by the card */
+}
diff --git a/sys/auto_init/Makefile b/sys/auto_init/Makefile
index ad7b0b1b7c84eafba29f2faa3c390952eb280642..3328c8443800550cf4ba9aa5ea1fcbd611604f47 100644
--- a/sys/auto_init/Makefile
+++ b/sys/auto_init/Makefile
@@ -8,4 +8,8 @@ ifneq (,$(filter auto_init_saul,$(USEMODULE)))
 DIRS += saul
 endif
 
+ifneq (,$(filter auto_init_storage,$(USEMODULE)))
+DIRS += storage
+endif
+
 include $(RIOTBASE)/Makefile.base
diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c
index b4ac6909d582fd5d288db090d13c66a975bfafa8..dccd57f5d5b1ecac2de5f60a59606e6720f92c22 100644
--- a/sys/auto_init/auto_init.c
+++ b/sys/auto_init/auto_init.c
@@ -325,4 +325,15 @@ void auto_init(void)
 #endif
 
 #endif /* MODULE_AUTO_INIT_GNRC_RPL */
+
+/* initialize storage devices */
+#ifdef MODULE_AUTO_INIT_STORAGE
+    DEBUG("auto_init STORAGE\n");
+
+#ifdef MODULE_SDCARD_SPI
+    extern void auto_init_sdcard_spi(void);
+    auto_init_sdcard_spi();
+#endif
+
+#endif /* MODULE_AUTO_INIT_STORAGE */
 }
diff --git a/sys/auto_init/storage/Makefile b/sys/auto_init/storage/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..80951d6fb3acd78e7c4543a53cc05bfb74b75aa8
--- /dev/null
+++ b/sys/auto_init/storage/Makefile
@@ -0,0 +1,3 @@
+MODULE = auto_init_storage
+
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/auto_init/storage/auto_init_sdcard_spi.c b/sys/auto_init/storage/auto_init_sdcard_spi.c
new file mode 100644
index 0000000000000000000000000000000000000000..8c7dc3c25f0af2dbd86d08c28e0b252982dc8cf5
--- /dev/null
+++ b/sys/auto_init/storage/auto_init_sdcard_spi.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     auto_init_sdcard_spi
+ * @{
+ *
+ * @file
+ * @brief       Auto initialization for sd-cards connected over spi
+ *
+ * @author      Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ */
+
+#ifdef MODULE_SDCARD_SPI
+
+#include "sdcard_spi.h"
+#include "sdcard_spi_params.h"
+
+#define ENABLE_DEBUG    (0)
+#include "debug.h"
+
+/**
+ * @brief   number of used sd cards
+ * @{
+ */
+#define SDCARD_SPI_NUM (sizeof(sdcard_spi_params) / sizeof(sdcard_spi_params[0]))
+/** @} */
+
+/**
+ * @brief   Allocate memory for the device descriptors
+ * @{
+ */
+sdcard_spi_t sdcard_spi_devs[SDCARD_SPI_NUM];
+/** @} */
+
+void auto_init_sdcard_spi(void)
+{
+    for (int i = 0; i < SDCARD_SPI_NUM; i++) {
+        DEBUG("sdcard_spi_auto_init(): initializing device [%i]...\n", i);
+        int resu = sdcard_spi_init(&sdcard_spi_devs[i], &sdcard_spi_params[i]);
+        if(resu != 0){
+            DEBUG("error initializing device [%i]\n", i);
+        }
+    }
+}
+
+#else
+typedef int dont_be_pedantic;
+#endif /* MODULE_SDCARD_SPI */
+/** @} */
diff --git a/tests/driver_sdcard_spi/Makefile b/tests/driver_sdcard_spi/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b9cfac34add633d6a9b11dfbd03e2b953f41d6c7
--- /dev/null
+++ b/tests/driver_sdcard_spi/Makefile
@@ -0,0 +1,9 @@
+APPLICATION = driver_sdcard_spi
+include ../Makefile.tests_common
+
+USEMODULE += sdcard_spi
+USEMODULE += auto_init_storage
+USEMODULE += fmt
+USEMODULE += shell
+
+include $(RIOTBASE)/Makefile.include
diff --git a/tests/driver_sdcard_spi/main.c b/tests/driver_sdcard_spi/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..24f5272b5c7c48ec80dacb3fd258462d0edb93a8
--- /dev/null
+++ b/tests/driver_sdcard_spi/main.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2016 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     tests
+ * @{
+ *
+ * @file
+ * @brief       Test application for the sd-card spi driver
+ *
+ * @author      Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
+ *
+ * @}
+ */
+#include "shell.h"
+#include "sdcard_spi.h"
+#include "sdcard_spi_internal.h"
+#include "sdcard_spi_params.h"
+#include "fmt.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+/* independent of what you specify in a r/w cmd this is the maximum number of blocks read at once.
+   If you call read with a bigger blockcount the read is performed in chunks*/
+#define MAX_BLOCKS_IN_BUFFER 4
+#define BLOCK_PRINT_BYTES_PER_LINE 16
+#define FIRST_PRINTABLE_ASCII_CHAR 0x20
+#define ASCII_UNPRINTABLE_REPLACEMENT "."
+
+/* this is provided by the sdcard_spi driver
+ * see sys/auto_init/storage/auto_init_sdcard_spi.c */
+extern sdcard_spi_t sdcard_spi_devs[sizeof(sdcard_spi_params) / sizeof(sdcard_spi_params[0])];
+sdcard_spi_t *card = &sdcard_spi_devs[0];
+
+char buffer[SD_HC_BLOCK_SIZE * MAX_BLOCKS_IN_BUFFER];
+
+static int _init(int argc, char **argv)
+{
+    printf("Initializing SD-card at SPI_%i...", sdcard_spi_params[0].spi_dev);
+
+    if (sdcard_spi_init(card, &sdcard_spi_params[0]) != 0) {
+        puts("[FAILED]");
+        #if ENABLE_DEBUG != 1
+        puts("enable debugging in sdcard_spi.c for further information!");
+        #endif
+        return -2;
+    }
+    puts("[OK]");
+    return 0;
+}
+
+static int _cid(int argc, char **argv)
+{
+    puts("----------------------------------------");
+    printf("MID: %d\n", card->cid.MID);
+    printf("OID: %c%c\n", card->cid.OID[0], card->cid.OID[1]);
+    printf("PNM: %c%c%c%c%c\n", card->cid.PNM[0], card->cid.PNM[1], card->cid.PNM[2],
+                                card->cid.PNM[3], card->cid.PNM[4]);
+    printf("PRV: %d\n", card->cid.PRV);
+    printf("PSN: %lu\n", card->cid.PSN);
+    printf("MDT: %d\n", card->cid.MDT);
+    printf("CRC: %d\n", card->cid.CID_CRC);
+    puts("----------------------------------------");
+    return 0;
+}
+
+static int _csd(int argc, char **argv)
+{
+    if (card->csd_structure == SD_CSD_V1) {
+        puts("CSD V1\n----------------------------------------");
+        printf("CSD_STRUCTURE: 0x%0lx\n", (unsigned long)card->csd.v1.CSD_STRUCTURE);
+        printf("TAAC: 0x%0lx\n", (unsigned long)card->csd.v1.TAAC);
+        printf("NSAC: 0x%0lx\n", (unsigned long)card->csd.v1.NSAC);
+        printf("TRAN_SPEED: 0x%0lx\n", (unsigned long)card->csd.v1.TRAN_SPEED);
+        printf("CCC: 0x%0lx\n", (unsigned long)card->csd.v1.CCC);
+        printf("READ_BL_LEN: 0x%0lx\n", (unsigned long)card->csd.v1.READ_BL_LEN);
+        printf("READ_BL_PARTIAL: 0x%0lx\n", (unsigned long)card->csd.v1.READ_BL_PARTIAL);
+        printf("WRITE_BLK_MISALIGN: 0x%0lx\n", (unsigned long)card->csd.v1.WRITE_BLK_MISALIGN);
+        printf("READ_BLK_MISALIGN: 0x%0lx\n", (unsigned long)card->csd.v1.READ_BLK_MISALIGN);
+        printf("DSR_IMP: 0x%0lx\n", (unsigned long)card->csd.v1.DSR_IMP);
+        printf("C_SIZE: 0x%0lx\n", (unsigned long)card->csd.v1.C_SIZE);
+        printf("VDD_R_CURR_MIN: 0x%0lx\n", (unsigned long)card->csd.v1.VDD_R_CURR_MIN);
+        printf("VDD_R_CURR_MAX: 0x%0lx\n", (unsigned long)card->csd.v1.VDD_R_CURR_MAX);
+        printf("VDD_W_CURR_MIN: 0x%0lx\n", (unsigned long)card->csd.v1.VDD_W_CURR_MIN);
+        printf("VDD_W_CURR_MAX: 0x%0lx\n", (unsigned long)card->csd.v1.VDD_W_CURR_MAX);
+        printf("C_SIZE_MULT: 0x%0lx\n", (unsigned long)card->csd.v1.C_SIZE_MULT);
+        printf("ERASE_BLK_EN: 0x%0lx\n", (unsigned long)card->csd.v1.ERASE_BLK_EN);
+        printf("SECTOR_SIZE: 0x%0lx\n", (unsigned long)card->csd.v1.SECTOR_SIZE);
+        printf("WP_GRP_SIZE: 0x%0lx\n", (unsigned long)card->csd.v1.WP_GRP_SIZE);
+        printf("WP_GRP_ENABLE: 0x%0lx\n", (unsigned long)card->csd.v1.WP_GRP_ENABLE);
+        printf("R2W_FACTOR: 0x%0lx\n", (unsigned long)card->csd.v1.R2W_FACTOR);
+        printf("WRITE_BL_LEN: 0x%0lx\n", (unsigned long)card->csd.v1.WRITE_BL_LEN);
+        printf("WRITE_BL_PARTIAL: 0x%0lx\n", (unsigned long)card->csd.v1.WRITE_BL_PARTIAL);
+        printf("FILE_FORMAT_GRP: 0x%0lx\n", (unsigned long)card->csd.v1.FILE_FORMAT_GRP);
+        printf("COPY: 0x%0lx\n", (unsigned long)card->csd.v1.COPY);
+        printf("PERM_WRITE_PROTECT: 0x%0lx\n", (unsigned long)card->csd.v1.PERM_WRITE_PROTECT);
+        printf("TMP_WRITE_PROTECT: 0x%0lx\n", (unsigned long)card->csd.v1.TMP_WRITE_PROTECT);
+        printf("FILE_FORMAT: 0x%0lx\n", (unsigned long)card->csd.v1.FILE_FORMAT);
+        printf("CRC: 0x%0lx\n", (unsigned long)card->csd.v1.CSD_CRC);
+    }
+    else if (card->csd_structure == SD_CSD_V2) {
+        puts("CSD V2:\n----------------------------------------");
+        printf("CSD_STRUCTURE: 0x%0lx\n", (unsigned long)card->csd.v2.CSD_STRUCTURE);
+        printf("TAAC: 0x%0lx\n", (unsigned long)card->csd.v2.TAAC);
+        printf("NSAC: 0x%0lx\n", (unsigned long)card->csd.v2.NSAC);
+        printf("TRAN_SPEED: 0x%0lx\n", (unsigned long)card->csd.v2.TRAN_SPEED);
+        printf("CCC: 0x%0lx\n", (unsigned long)card->csd.v2.CCC);
+        printf("READ_BL_LEN: 0x%0lx\n", (unsigned long)card->csd.v2.READ_BL_LEN);
+        printf("READ_BL_PARTIAL: 0x%0lx\n", (unsigned long)card->csd.v2.READ_BL_PARTIAL);
+        printf("WRITE_BLK_MISALIGN: 0x%0lx\n", (unsigned long)card->csd.v2.WRITE_BLK_MISALIGN);
+        printf("READ_BLK_MISALIGN: 0x%0lx\n", (unsigned long)card->csd.v2.READ_BLK_MISALIGN);
+        printf("DSR_IMP: 0x%0lx\n", (unsigned long)card->csd.v2.DSR_IMP);
+        printf("C_SIZE: 0x%0lx\n", (unsigned long)card->csd.v2.C_SIZE);
+        printf("ERASE_BLK_EN: 0x%0lx\n", (unsigned long)card->csd.v2.ERASE_BLK_EN);
+        printf("SECTOR_SIZE: 0x%0lx\n", (unsigned long)card->csd.v2.SECTOR_SIZE);
+        printf("WP_GRP_SIZE: 0x%0lx\n", (unsigned long)card->csd.v2.WP_GRP_SIZE);
+        printf("WP_GRP_ENABLE: 0x%0lx\n", (unsigned long)card->csd.v2.WP_GRP_ENABLE);
+        printf("R2W_FACTOR: 0x%0lx\n", (unsigned long)card->csd.v2.R2W_FACTOR);
+        printf("WRITE_BL_LEN: 0x%0lx\n", (unsigned long)card->csd.v2.WRITE_BL_LEN);
+        printf("WRITE_BL_PARTIAL: 0x%0lx\n", (unsigned long)card->csd.v2.WRITE_BL_PARTIAL);
+        printf("FILE_FORMAT_GRP: 0x%0lx\n", (unsigned long)card->csd.v2.FILE_FORMAT_GRP);
+        printf("COPY: 0x%0lx\n", (unsigned long)card->csd.v2.COPY);
+        printf("PERM_WRITE_PROTECT: 0x%0lx\n", (unsigned long)card->csd.v2.PERM_WRITE_PROTECT);
+        printf("TMP_WRITE_PROTECT: 0x%0lx\n", (unsigned long)card->csd.v2.TMP_WRITE_PROTECT);
+        printf("FILE_FORMAT: 0x%0lx\n", (unsigned long)card->csd.v2.FILE_FORMAT);
+        printf("CRC: 0x%0lx\n", (unsigned long)card->csd.v2.CSD_CRC);
+    }
+    puts("----------------------------------------");
+    return 0;
+}
+
+static int _sds(int argc, char **argv)
+{
+    sd_status_t sds;
+
+    if (sdcard_spi_read_sds(card, &sds) == SD_RW_OK) {
+        puts("SD status:\n----------------------------------------");
+        printf("SIZE_OF_PROTECTED_AREA: 0x%0lx\n", (unsigned long)sds.SIZE_OF_PROTECTED_AREA);
+        printf("SUS_ADDR: 0x%0lx\n", (unsigned long)sds.SUS_ADDR);
+        printf("VSC_AU_SIZE: 0x%0lx\n", (unsigned long)sds.VSC_AU_SIZE);
+        printf("SD_CARD_TYPE: 0x%0lx\n", (unsigned long)sds.SD_CARD_TYPE);
+        printf("ERASE_SIZE: 0x%0lx\n", (unsigned long)sds.ERASE_SIZE);
+        printf("SPEED_CLASS: 0x%0lx\n", (unsigned long)sds.SPEED_CLASS);
+        printf("PERFORMANCE_MOVE: 0x%0lx\n", (unsigned long)sds.PERFORMANCE_MOVE);
+        printf("VIDEO_SPEED_CLASS: 0x%0lx\n", (unsigned long)sds.VIDEO_SPEED_CLASS);
+        printf("ERASE_TIMEOUT: 0x%0lx\n", (unsigned long)sds.ERASE_TIMEOUT);
+        printf("ERASE_OFFSET: 0x%0lx\n", (unsigned long)sds.ERASE_OFFSET);
+        printf("UHS_SPEED_GRADE: 0x%0lx\n", (unsigned long)sds.UHS_SPEED_GRADE);
+        printf("UHS_AU_SIZE: 0x%0lx\n", (unsigned long)sds.UHS_AU_SIZE);
+        printf("AU_SIZE: 0x%0lx\n", (unsigned long)sds.AU_SIZE);
+        printf("DAT_BUS_WIDTH: 0x%0lx\n", (unsigned long)sds.DAT_BUS_WIDTH);
+        printf("SECURED_MODE: 0x%0lx\n", (unsigned long)sds.SECURED_MODE);
+        puts("----------------------------------------");
+        return 0;
+    }
+    return -1;
+}
+
+static int _size(int argc, char **argv)
+{
+    uint64_t bytes = sdcard_spi_get_capacity(card);
+
+    uint32_t gib_int = bytes / (SDCARD_SPI_IEC_KIBI * SDCARD_SPI_IEC_KIBI * SDCARD_SPI_IEC_KIBI);
+    uint32_t gib_frac = ( (((bytes/(SDCARD_SPI_IEC_KIBI * SDCARD_SPI_IEC_KIBI))
+                            - gib_int * SDCARD_SPI_IEC_KIBI) * SDCARD_SPI_SI_KILO)
+                          / SDCARD_SPI_IEC_KIBI);
+
+    uint32_t gb_int = bytes / (SDCARD_SPI_SI_KILO * SDCARD_SPI_SI_KILO * SDCARD_SPI_SI_KILO);
+    uint32_t gb_frac = (bytes / (SDCARD_SPI_SI_KILO * SDCARD_SPI_SI_KILO))
+                       - (gb_int * SDCARD_SPI_SI_KILO); //[MB]
+
+    puts("\nCard size: ");
+    //fflush(stdout);
+    print_u64_dec( bytes );
+    printf(" bytes (%lu,%03lu GiB | %lu,%03lu GB)\n", gib_int, gib_frac, gb_int, gb_frac);
+    return 0;
+}
+
+static int _read(int argc, char **argv)
+{
+    int blockaddr;
+    int cnt;
+    bool print_as_char = false;
+
+    if ((argc == 3) || (argc == 4)) {
+        blockaddr = (uint32_t)atoi(argv[1]);
+        cnt = (uint32_t)atoi(argv[2]);
+        if (argc == 4 && (strcmp("-c", argv[3]) == 0)) {
+            print_as_char = true;
+        }
+    }
+    else {
+        printf("usage: %s blockaddr cnt [-c]\n", argv[0]);
+        return -1;
+    }
+
+    int total_read = 0;
+    while (total_read < cnt) {
+        int chunk_blocks = cnt - total_read;
+        if (chunk_blocks > MAX_BLOCKS_IN_BUFFER) {
+            chunk_blocks = MAX_BLOCKS_IN_BUFFER;
+        }
+        sd_rw_response_t state;
+        int chunks_read = sdcard_spi_read_blocks(card, blockaddr + total_read, buffer,
+                                                 SD_HC_BLOCK_SIZE, chunk_blocks, &state);
+
+        if (state != SD_RW_OK) {
+            printf("read error %d (block %d/%d)\n", state, total_read + chunks_read, cnt);
+            return -1;
+        }
+
+        for (int i = 0; i < chunk_blocks * SD_HC_BLOCK_SIZE; i++) {
+
+            if ((i % SD_HC_BLOCK_SIZE) == 0) {
+                printf("BLOCK %d:\n", blockaddr + total_read + i / SD_HC_BLOCK_SIZE);
+            }
+
+            if (print_as_char) {
+                if (buffer[i] >= FIRST_PRINTABLE_ASCII_CHAR) {
+                    printf("%c", buffer[i]);
+                }
+                else {
+                    printf(ASCII_UNPRINTABLE_REPLACEMENT);
+                }
+            }
+            else {
+                printf("%02x ", buffer[i]);
+            }
+
+            if ((i % BLOCK_PRINT_BYTES_PER_LINE) == (BLOCK_PRINT_BYTES_PER_LINE - 1)) {
+                puts(""); /* line break after BLOCK_PRINT_BYTES_PER_LINE bytes */
+            }
+
+            if ((i % SD_HC_BLOCK_SIZE) == (SD_HC_BLOCK_SIZE - 1)) {
+                puts(""); /* empty line after each printed block */
+            }
+        }
+        total_read += chunks_read;
+    }
+    return 0;
+}
+
+static int _write(int argc, char **argv)
+{
+    int bladdr;
+    char *data;
+    int size;
+    bool repeat_data = false;
+
+    if (argc == 3 || argc == 4) {
+        bladdr = (int)atoi(argv[1]);
+        data = argv[2];
+        size = strlen(argv[2]);
+        printf("will write '%s' (%d chars) at start of block %d\n", data, size, bladdr);
+        if (argc == 4 && (strcmp("-r", argv[3]) == 0)) {
+            repeat_data = true;
+            puts("the rest of the block will be filled with copies of that string");
+        }
+        else {
+            puts("the rest of the block will be filled with zeros");
+        }
+    }
+    else {
+        printf("usage: %s blockaddr string [-r]\n", argv[0]);
+        return -1;
+    }
+
+    if (size > SD_HC_BLOCK_SIZE) {
+        printf("maximum stringsize to write at once is %d ...aborting\n", SD_HC_BLOCK_SIZE);
+        return -1;
+    }
+
+    /* copy data to a full-block-sized buffer an fill remaining block space according to -r param*/
+    char buffer[SD_HC_BLOCK_SIZE];
+    for (int i = 0; i < sizeof(buffer); i++) {
+        if (repeat_data || i < size) {
+            buffer[i] = data[i % size];
+        }
+        else {
+            buffer[i] = 0;
+        }
+    }
+
+    sd_rw_response_t state;
+    int chunks_written = sdcard_spi_write_blocks(card, bladdr, buffer, SD_HC_BLOCK_SIZE, 1, &state);
+
+    if (state != SD_RW_OK) {
+        printf("write error %d (wrote %d/%d blocks)\n", state, chunks_written, 1);
+        return -1;
+    }
+
+    printf("write block %d [OK]\n", bladdr);
+    return 0;
+}
+
+static int _copy(int argc, char **argv)
+{
+    int src_block;
+    int dst_block;
+    char tmp_copy[SD_HC_BLOCK_SIZE];
+
+    if (argc != 3) {
+        printf("usage: %s src_block dst_block\n", argv[0]);
+        return -1;
+    }
+
+    src_block = (uint32_t)atoi(argv[1]);
+    dst_block = (uint32_t)atoi(argv[2]);
+
+    sd_rw_response_t rd_state;
+    sdcard_spi_read_blocks(card, src_block, tmp_copy, SD_HC_BLOCK_SIZE, 1, &rd_state);
+
+    if (rd_state != SD_RW_OK) {
+        printf("read error %d (block %d)\n", rd_state, src_block);
+        return -1;
+    }
+
+    sd_rw_response_t wr_state;
+    sdcard_spi_write_blocks(card, dst_block, tmp_copy, SD_HC_BLOCK_SIZE, 1, &wr_state);
+
+    if (wr_state != SD_RW_OK) {
+        printf("write error %d (block %d)\n", wr_state, dst_block);
+        return -2;
+    }
+
+    printf("copy block %d to %d [OK]\n", src_block, dst_block);
+    return 0;
+}
+
+static int _sector_count(int argc, char **argv)
+{
+    printf("available sectors on card: %li\n", sdcard_spi_get_sector_count(card));
+    return 0;
+}
+
+static const shell_command_t shell_commands[] = {
+    { "init", "initializes default card", _init },
+    { "cid",  "print content of CID (Card IDentification) register", _cid },
+    { "csd", "print content of CSD (Card-Specific Data) register", _csd },
+    { "sds", "print SD status", _sds },
+    { "size", "print card size", _size },
+    { "sectors", "print sector count of card", _sector_count },
+    { "read", "'read n m' reads m blocks beginning at block address n and prints the result. "
+              "Append -c option to print data readable chars", _read },
+    { "write", "'write n data' writes data to block n. Append -r option to "
+               "repeatedly write data to coplete block", _write },
+    { "copy", "'copy src dst' copies block src to block dst", _copy },
+    { NULL, NULL, NULL }
+};
+
+int main(void)
+{
+    puts("SD-card spi driver test application");
+
+    card->init_done = false;
+
+    puts("insert SD-card and use 'init' command to set card to spi mode");
+    puts("WARNING: using 'write' or 'copy' commands WILL overwrite data on your sd-card and");
+    puts("almost for sure corrupt existing filesystems, partitions and contained data!");
+    char line_buf[SHELL_DEFAULT_BUFSIZE];
+    shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
+    return 0;
+}