diff --git a/boards/cc2538dk/Makefile.features b/boards/cc2538dk/Makefile.features
index 328367ec499111f0f1122e70fdf4ba452ca2d86d..c2f4c92a7961e634345da8bc59907546e2f1df06 100644
--- a/boards/cc2538dk/Makefile.features
+++ b/boards/cc2538dk/Makefile.features
@@ -3,6 +3,7 @@ FEATURES_PROVIDED += periph_cpuid
 FEATURES_PROVIDED += periph_gpio
 FEATURES_PROVIDED += periph_hwrng
 FEATURES_PROVIDED += periph_i2c
+FEATURES_PROVIDED += periph_spi
 FEATURES_PROVIDED += periph_timer
 FEATURES_PROVIDED += periph_uart
 
diff --git a/boards/cc2538dk/include/periph_conf.h b/boards/cc2538dk/include/periph_conf.h
index 16a706cfd98e6aae0f8dc321a90e5e807d28f232..3cc9ad366fe215b84cbc0666fd13c39272c7ef26 100644
--- a/boards/cc2538dk/include/periph_conf.h
+++ b/boards/cc2538dk/include/periph_conf.h
@@ -129,6 +129,27 @@ static const i2c_conf_t i2c_config[I2C_NUMOF] = {
 };
 /** @} */
 
+/**
+ * @name SPI configuration
+ * @{
+ */
+#define SPI_NUMOF           1
+#define SPI_0_EN            1
+
+#ifdef HAVE_PERIPH_SPI_CONF_T
+static const periph_spi_conf_t spi_config[SPI_NUMOF] = {
+    {
+        .dev      = SSI0,
+        .mosi_pin = GPIO_PA4,
+        .miso_pin = GPIO_PA5,
+        .sck_pin  = GPIO_PA2,
+        .cs_pin   = GPIO_PD0,
+    },
+};
+#endif
+
+/** @} */
+
 /**
  * @name GPIO configuration
  * @{
diff --git a/boards/openmote-cc2538/Makefile.features b/boards/openmote-cc2538/Makefile.features
index b6dc26a6f027ba7f620aec73d9fa8a0dba032bf3..819901ddc85175a1a5edc80a62d2cd05fcebb2ec 100644
--- a/boards/openmote-cc2538/Makefile.features
+++ b/boards/openmote-cc2538/Makefile.features
@@ -3,6 +3,7 @@ FEATURES_PROVIDED += periph_cpuid
 FEATURES_PROVIDED += periph_gpio
 FEATURES_PROVIDED += periph_hwrng
 FEATURES_PROVIDED += periph_i2c
+FEATURES_PROVIDED += periph_spi
 FEATURES_PROVIDED += periph_timer
 FEATURES_PROVIDED += periph_uart
 
diff --git a/boards/openmote-cc2538/include/periph_conf.h b/boards/openmote-cc2538/include/periph_conf.h
index f7c7afa0c708560cecb9f8d2fe01894ddfefd432..a2d490908f7f82d1ce4a2c30f180ec8530d6b313 100644
--- a/boards/openmote-cc2538/include/periph_conf.h
+++ b/boards/openmote-cc2538/include/periph_conf.h
@@ -121,6 +121,25 @@ static const i2c_conf_t i2c_config[I2C_NUMOF] = {
 };
 /** @} */
 
+/**
+ * @name SPI configuration
+ * @{
+ */
+#define SPI_NUMOF           1
+#define SPI_0_EN            1
+
+static const periph_spi_conf_t spi_config[SPI_NUMOF] = {
+    {
+        .dev      = SSI0,
+        .mosi_pin = GPIO_PA5,
+        .miso_pin = GPIO_PA4,
+        .sck_pin  = GPIO_PA2,
+        .cs_pin   = GPIO_PA3,
+    },
+};
+
+/** @} */
+
 /**
  * @name GPIO configuration
  * @{
diff --git a/boards/remote/Makefile.features b/boards/remote/Makefile.features
index b6dc26a6f027ba7f620aec73d9fa8a0dba032bf3..819901ddc85175a1a5edc80a62d2cd05fcebb2ec 100644
--- a/boards/remote/Makefile.features
+++ b/boards/remote/Makefile.features
@@ -3,6 +3,7 @@ FEATURES_PROVIDED += periph_cpuid
 FEATURES_PROVIDED += periph_gpio
 FEATURES_PROVIDED += periph_hwrng
 FEATURES_PROVIDED += periph_i2c
+FEATURES_PROVIDED += periph_spi
 FEATURES_PROVIDED += periph_timer
 FEATURES_PROVIDED += periph_uart
 
diff --git a/boards/remote/include/periph_conf.h b/boards/remote/include/periph_conf.h
index ece54877c19708ce048bf2bafb25487a816c33af..7323b0ec384c1aa4373d81dc842e25671cbe4f38 100644
--- a/boards/remote/include/periph_conf.h
+++ b/boards/remote/include/periph_conf.h
@@ -124,6 +124,34 @@ static const i2c_conf_t i2c_config[I2C_NUMOF] = {
 };
 /** @} */
 
+/**
+ * @name SPI configuration
+ * @{
+ */
+#define SPI_NUMOF           2
+#define SPI_0_EN            1
+#define SPI_1_EN            1
+
+#ifdef HAVE_PERIPH_SPI_CONF_T
+static const periph_spi_conf_t spi_config[SPI_NUMOF] = {
+    {
+        .dev      = SSI0,
+        .mosi_pin = GPIO_PD0,
+        .miso_pin = GPIO_PC4,
+        .sck_pin  = GPIO_PD1,
+        .cs_pin   = GPIO_PD3,
+    },
+    {
+        .dev      = SSI1,
+        .mosi_pin = GPIO_PC7,
+        .miso_pin = GPIO_PA4,
+        .sck_pin  = GPIO_PB5,
+    },
+};
+#endif
+
+/** @} */
+
 /**
  * @name GPIO configuration
  * @{
diff --git a/cpu/cc2538/Makefile.include b/cpu/cc2538/Makefile.include
index 507f47d21b0db8d52986d51ce0588500b83a8966..3c829bccf014286dc40a209526ea9e9c8058474d 100644
--- a/cpu/cc2538/Makefile.include
+++ b/cpu/cc2538/Makefile.include
@@ -1,3 +1,6 @@
 export CPU_ARCH := cortex-m3
 
+# include common SPI functions
+USEMODULE += periph_common
+
 include $(RIOTCPU)/Makefile.include.cortexm_common
diff --git a/cpu/cc2538/include/cc2538_ssi.h b/cpu/cc2538/include/cc2538_ssi.h
new file mode 100644
index 0000000000000000000000000000000000000000..2dc08cff02e43c385797cdfa5c182c624ee2621a
--- /dev/null
+++ b/cpu/cc2538/include/cc2538_ssi.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 Loci Controls Inc.
+ *
+ * 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.
+ */
+
+/**
+ * @addtogroup      cpu_cc2538
+ * @{
+ *
+ * @file
+ * @brief           CC2538 SSI interface
+ *
+ * @author          Ian Martin <ian@locicontrols.com>
+ */
+
+#ifndef CC2538_SSI_H
+#define CC2538_SSI_H
+
+#include "cc2538.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief SSI component registers
+ */
+typedef struct {
+    union {
+        cc2538_reg_t CR0;                  /**< SSI Control Register 0 */
+        struct {
+            cc2538_reg_t DSS        :  4;  /**< SSI data size select */
+            cc2538_reg_t FRF        :  2;  /**< SSI frame format select */
+            cc2538_reg_t SPO        :  1;  /**< SSI serial clock polarity */
+            cc2538_reg_t SPH        :  1;  /**< SSI serial clock phase */
+            cc2538_reg_t SCR        :  8;  /**< SSI serial clock rate */
+            cc2538_reg_t RESERVED   : 16;  /**< Reserved bits */
+        } CR0bits;
+    };
+
+    union {
+        cc2538_reg_t CR1;                  /**< SSI Control Register 1 */
+        struct {
+            cc2538_reg_t LBM        :  1;  /**< SSI loop-back mode */
+            cc2538_reg_t SSE        :  1;  /**< SSI synchronous serial port enable */
+            cc2538_reg_t MS         :  1;  /**< SSI master and slave select */
+            cc2538_reg_t SOD        :  1;  /**< SSI slave mode output disable */
+            cc2538_reg_t RESERVED   : 28;  /**< Reserved bits */
+        } CR1bits;
+    };
+
+    cc2538_reg_t DR;                       /**< SSI Data register */
+
+    union {
+        cc2538_reg_t SR;                   /**< SSI FIFO/busy Status Register */
+        struct {
+            cc2538_reg_t TFE        :  1;  /**< SSI transmit FIFO empty */
+            cc2538_reg_t TNF        :  1;  /**< SSI transmit FIFO not full */
+            cc2538_reg_t RNE        :  1;  /**< SSI receive FIFO not empty */
+            cc2538_reg_t RFF        :  1;  /**< SSI receive FIFO full */
+            cc2538_reg_t BSY        :  1;  /**< SSI busy bit */
+            cc2538_reg_t RESERVED   : 27;  /**< Reserved bits */
+        } SRbits;
+    };
+    cc2538_reg_t CPSR;                     /**< SSI Clock Register */
+    cc2538_reg_t IM;                       /**< SSI Interrupt Mask register */
+    cc2538_reg_t RIS;                      /**< SSI Raw Interrupt Status register */
+    cc2538_reg_t MIS;                      /**< SSI Masked Interrupt Status register */
+    cc2538_reg_t ICR;                      /**< SSI Interrupt Clear Register */
+    cc2538_reg_t DMACTL;                   /**< SSI uDMA Control Register. */
+    cc2538_reg_t CC;                       /**< SSI clock configuration */
+} cc2538_ssi_t;
+
+#define SSI0 ( (cc2538_ssi_t*)0x40008000 ) /**< SSI0 Instance */
+#define SSI1 ( (cc2538_ssi_t*)0x40009000 ) /**< SSI1 Instance */
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* CC2538_SSI_H */
+/** @} */
diff --git a/cpu/cc2538/include/periph_cpu.h b/cpu/cc2538/include/periph_cpu.h
index 6a108feab0819622e1ec7b9d32b321ca47d81095..6532429b307780341764ee5c2955266d5771e1a2 100644
--- a/cpu/cc2538/include/periph_cpu.h
+++ b/cpu/cc2538/include/periph_cpu.h
@@ -21,6 +21,8 @@
 
 #include <stdint.h>
 
+#include "cc2538_ssi.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -43,11 +45,31 @@ typedef uint32_t gpio_t;
  */
 #define GPIO_UNDEF          (0xffffffff)
 
+/**
+ * @brief declare needed generic SPI functions
+ * @{
+ */
+#define PERIPH_SPI_NEEDS_TRANSFER_REG
+#define PERIPH_SPI_NEEDS_TRANSFER_REGS
+/** @} */
+
 typedef struct {
     gpio_t scl_pin;         /**< pin used for SCL */
     gpio_t sda_pin;         /**< pin used for SDA */
 } i2c_conf_t;
 
+/**
+ * @brief   SPI configuration data structure
+ */
+#define HAVE_PERIPH_SPI_CONF_T
+typedef struct {
+    cc2538_ssi_t *dev;      /**< SSI device */
+    gpio_t mosi_pin;        /**< pin used for MOSI */
+    gpio_t miso_pin;        /**< pin used for MISO */
+    gpio_t sck_pin;         /**< pin used for SCK */
+    gpio_t cs_pin;          /**< pin used for CS */
+} periph_spi_conf_t;
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cpu/cc2538/periph/spi.c b/cpu/cc2538/periph/spi.c
new file mode 100644
index 0000000000000000000000000000000000000000..3656df7793e9cb838bf7081c92767a0bc12afa2f
--- /dev/null
+++ b/cpu/cc2538/periph/spi.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2015 Loci Controls Inc.
+ *
+ * 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.
+ */
+
+/**
+ * @addtogroup  driver_periph
+ * @{
+ *
+ * @file
+ * @brief       Low-level SPI driver implementation
+ *
+ * @author      Ian Martin <ian@locicontrols.com>
+ *
+ * @}
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "cc2538_ssi.h"
+
+#include "cpu.h"
+#include "mutex.h"
+#include "periph/spi.h"
+#include "periph_conf.h"
+#include "thread.h"
+#include "sched.h"
+
+/* guard file in case no SPI device is defined */
+#if SPI_NUMOF
+
+/* clock sources for the SSI_CC register */
+#define CS_SYS_DIV            0
+#define CS_IO_DIV             1
+
+#define SSI0_MASK             (1 << 0)
+#define SSI1_MASK             (1 << 1)
+
+#ifndef SPI_DATA_BITS_NUMOF
+#define SPI_DATA_BITS_NUMOF   8
+#endif
+
+#define spin_until(condition) while (!(condition)) thread_yield()
+
+/**
+ * @brief Array holding one pre-initialized mutex for each SPI device
+ */
+static mutex_t locks[SPI_NUMOF] = {MUTEX_INIT};
+
+int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed)
+{
+    cc2538_ssi_t* ssi = spi_config[dev].dev;
+
+    if (dev >= SPI_NUMOF) {
+        return -1;
+    }
+
+    /* power on the SPI device */
+    spi_poweron(dev);
+
+    /* configure SCK, MISO and MOSI pin */
+    spi_conf_pins(dev);
+
+    /* Disable the SSI and configure it for SPI master mode */
+    ssi->CR1 = 0;
+
+    /* 3. Configure the SSI clock source */
+    ssi->CC = CS_SYS_DIV;
+
+    /* 4. Configure the clock prescale divisor by writing the SSI_CPSR register.
+     * frequency of the SSIClk is defined by: SSIClk = SysClk / (CPSDVSR x (1 + SCR))
+     */
+
+    const int32_t speed_lut[] = {
+        [SPI_SPEED_100KHZ] =   100000 /* Hz */,
+        [SPI_SPEED_400KHZ] =   400000 /* Hz */,
+        [SPI_SPEED_1MHZ  ] =  1000000 /* Hz */,
+        [SPI_SPEED_5MHZ  ] =  5000000 /* Hz */,
+        [SPI_SPEED_10MHZ ] = 10000000 /* Hz */,
+    };
+
+    int32_t SysClk = sys_clock_freq();
+    int32_t f_desired = speed_lut[speed];
+    int32_t f_actual;
+    int32_t err;
+    int32_t best_err = INT32_MAX;
+    int32_t div1;
+    int32_t div2;
+    int32_t best_div1 = 2;
+    int32_t best_div2 = 1;
+
+    /* System clock is first divided by CPSDVSR, then by SCR */
+    for (div1 = 2; div1 <= 254; div1++) {
+        div2 = SysClk;
+        int32_t denom = div1 * f_desired;
+        div2 += denom / 2;
+        div2 /= denom;
+
+        if (div2 < 1) {
+            div2 = 1;
+        }
+        else if (div2 > 256) {
+            div2 = 256;
+        }
+
+        f_actual = SysClk / (div1 * div2);
+        err = f_actual - f_desired;
+        if (err < 0) {
+            err = -err;
+        }
+        if (err <= best_err) {
+            best_div1 = div1;
+            best_div2 = div2;
+            best_err = err;
+        }
+    }
+
+    ssi->CPSR        = best_div1;     /* CPSDVSR */
+    ssi->CR0bits.SCR = best_div2 - 1; /* Serial clock rate (SCR) */
+
+    switch (conf) {
+        case SPI_CONF_FIRST_RISING:
+            ssi->CR0bits.SPO = 0;
+            ssi->CR0bits.SPH = 0;
+            break;
+
+        case SPI_CONF_SECOND_RISING:
+            ssi->CR0bits.SPO = 0;
+            ssi->CR0bits.SPH = 1;
+            break;
+
+        case SPI_CONF_FIRST_FALLING:
+            ssi->CR0bits.SPO = 1;
+            ssi->CR0bits.SPH = 0;
+            break;
+
+        case SPI_CONF_SECOND_FALLING:
+            ssi->CR0bits.SPO = 1;
+            ssi->CR0bits.SPH = 1;
+            break;
+    }
+
+    ssi->CR0bits.FRF = 0;                       /* SPI protocol mode */
+    ssi->CR0bits.DSS = SPI_DATA_BITS_NUMOF - 1; /* The data size */
+
+    ssi->CR1bits.SSE = 1;
+
+    return 0;
+}
+
+int spi_init_slave(spi_t dev, spi_conf_t conf, char(*cb)(char data))
+{
+    /* slave mode is not (yet) supported */
+    return -1;
+}
+
+int spi_conf_pins(spi_t dev)
+{
+    if (dev >= SPI_NUMOF) {
+        return -1;
+    }
+
+    switch ((uintptr_t)spi_config[dev].dev) {
+        case (uintptr_t)SSI0:
+            IOC_PXX_SEL[spi_config[dev].mosi_pin] = SSI0_TXD;
+            IOC_PXX_SEL[spi_config[dev].sck_pin ] = SSI0_CLKOUT;
+            IOC_PXX_SEL[spi_config[dev].cs_pin  ] = SSI0_FSSOUT;
+
+            IOC_SSIRXD_SSI0 = spi_config[dev].miso_pin;
+            break;
+
+        case (uintptr_t)SSI1:
+            IOC_PXX_SEL[spi_config[dev].mosi_pin] = SSI1_TXD;
+            IOC_PXX_SEL[spi_config[dev].sck_pin ] = SSI1_CLKOUT;
+            IOC_PXX_SEL[spi_config[dev].cs_pin  ] = SSI1_FSSOUT;
+
+            IOC_SSIRXD_SSI1 = spi_config[dev].miso_pin;
+            break;
+    }
+
+    IOC_PXX_OVER[spi_config[dev].mosi_pin] = IOC_OVERRIDE_OE;
+    IOC_PXX_OVER[spi_config[dev].sck_pin ] = IOC_OVERRIDE_OE;
+    IOC_PXX_OVER[spi_config[dev].cs_pin  ] = IOC_OVERRIDE_OE;
+    IOC_PXX_OVER[spi_config[dev].miso_pin] = IOC_OVERRIDE_DIS;
+
+    gpio_hardware_control(spi_config[dev].mosi_pin);
+    gpio_hardware_control(spi_config[dev].miso_pin);
+    gpio_hardware_control(spi_config[dev].sck_pin);
+    gpio_hardware_control(spi_config[dev].cs_pin);
+
+    return 0;
+}
+
+int spi_acquire(spi_t dev)
+{
+    if (dev >= SPI_NUMOF) {
+        return -1;
+    }
+    mutex_lock(&locks[dev]);
+    return 0;
+}
+
+int spi_release(spi_t dev)
+{
+    if (dev >= SPI_NUMOF) {
+        return -1;
+    }
+    mutex_unlock(&locks[dev]);
+    return 0;
+}
+
+static char ssi_flush_input(cc2538_ssi_t *ssi)
+{
+    char tmp;
+
+    while (ssi->SRbits.RNE) {
+        tmp = ssi->DR;
+    }
+
+    return tmp;
+}
+
+int spi_transfer_byte(spi_t dev, char out, char *in)
+{
+    cc2538_ssi_t* ssi = spi_config[dev].dev;
+    char tmp;
+
+    ssi_flush_input(ssi);
+
+    /* transmit byte */
+    spin_until(ssi->SRbits.TNF);
+    ssi->DR = out;
+
+    /* receive byte */
+    spin_until(ssi->SRbits.RNE);
+    tmp = ssi->DR;
+
+    if (in) {
+        *in = tmp;
+    }
+
+    return 1;
+}
+
+int spi_transfer_bytes(spi_t dev, char *out, char *in, unsigned int length)
+{
+    cc2538_ssi_t* ssi = spi_config[dev].dev;
+    typeof(length) tx_n = 0, rx_n = 0;
+
+    if (dev >= SPI_NUMOF) {
+        return -1;
+    }
+
+    ssi_flush_input(ssi);
+
+    /* transmit and receive bytes */
+    while (tx_n < length) {
+        spin_until(ssi->SRbits.TNF || ssi->SRbits.RNE);
+
+        if (ssi->SRbits.TNF) {
+            ssi->DR = out[tx_n];
+            tx_n++;
+        }
+        else if (ssi->SRbits.RNE) {
+            assert(rx_n < length);
+            in[rx_n] = ssi->DR;
+            rx_n++;
+        }
+    }
+
+    /* receive remaining bytes */
+    while (rx_n < length) {
+        spin_until(ssi->SRbits.RNE);
+        assert(rx_n < length);
+        in[rx_n] = ssi->DR;
+        rx_n++;
+    }
+
+    return rx_n;
+}
+
+void spi_transmission_begin(spi_t dev, char reset_val)
+{
+    /* slave mode is not (yet) supported */
+}
+
+void spi_poweron(spi_t dev)
+{
+    switch ((uintptr_t)spi_config[dev].dev) {
+        case (uintptr_t)SSI0:
+            /* enable SSI0 in all three power modes */
+            SYS_CTRL_RCGCSSI |= SSI0_MASK;
+            SYS_CTRL_SCGCSSI |= SSI0_MASK;
+            SYS_CTRL_DCGCSSI |= SSI0_MASK;
+            break;
+
+        case (uintptr_t)SSI1:
+            /* enable SSI1 in all three power modes */
+            SYS_CTRL_RCGCSSI |= SSI1_MASK;
+            SYS_CTRL_SCGCSSI |= SSI1_MASK;
+            SYS_CTRL_DCGCSSI |= SSI1_MASK;
+            break;
+    }
+}
+
+void spi_poweroff(spi_t dev)
+{
+    switch ((uintptr_t)spi_config[dev].dev) {
+        case (uintptr_t)SSI0:
+            /* disable SSI0 in all three power modes */
+            SYS_CTRL_RCGCSSI &= ~SSI0_MASK;
+            SYS_CTRL_SCGCSSI &= ~SSI0_MASK;
+            SYS_CTRL_DCGCSSI &= ~SSI0_MASK;
+            break;
+
+        case (uintptr_t)SSI1:
+            /* disable SSI1 in all three power modes */
+            SYS_CTRL_RCGCSSI &= ~SSI1_MASK;
+            SYS_CTRL_SCGCSSI &= ~SSI1_MASK;
+            SYS_CTRL_DCGCSSI &= ~SSI1_MASK;
+            break;
+    }
+}
+
+#endif /* SPI_NUMOF */