diff --git a/cpu/sam0_common/Makefile.features b/cpu/sam0_common/Makefile.features
index 430ceb42af7110d1e98806b6bdb4bb4fcdba4540..cd5a20b58413c87bc25de09e2826be7f0d0ede75 100644
--- a/cpu/sam0_common/Makefile.features
+++ b/cpu/sam0_common/Makefile.features
@@ -1,4 +1,5 @@
 FEATURES_PROVIDED += periph_cpuid
 FEATURES_PROVIDED += periph_flashpage
+FEATURES_PROVIDED += periph_flashpage_raw
 
 -include $(RIOTCPU)/cortexm_common/Makefile.features
diff --git a/cpu/sam0_common/include/cpu_conf.h b/cpu/sam0_common/include/cpu_conf.h
index e954eb139a913c943824a41cc14cbb085130a891..48c1658bcbce68283f43808cbb3ee0d84f6aaa51 100644
--- a/cpu/sam0_common/include/cpu_conf.h
+++ b/cpu/sam0_common/include/cpu_conf.h
@@ -42,9 +42,15 @@ extern "C" {
  * @{
  */
 /* a flashpage in RIOT is mapped to a flash row on the SAM0s */
-#define FLASHPAGE_SIZE      (256U)
+#define FLASHPAGE_SIZE             (256U)
 /* one SAM0 row contains 4 SAM0 pages -> 4x the amount of RIOT flashpages */
-#define FLASHPAGE_NUMOF     (FLASH_NB_OF_PAGES / 4)
+#define FLASHPAGE_NUMOF            (FLASH_NB_OF_PAGES / 4)
+/* The minimum block size which can be written is 16B. However, the erase
+ * block is always FLASHPAGE_SIZE (SAM0 row).
+ */
+#define FLASHPAGE_RAW_BLOCKSIZE    (16)
+/* Writing should be always 4 byte aligned */
+#define FLASHPAGE_RAW_ALIGNMENT    (4)
 /** @} */
 
 #ifdef __cplusplus
diff --git a/cpu/sam0_common/periph/flashpage.c b/cpu/sam0_common/periph/flashpage.c
index 1090bcd286de179fb99becfa87e4bed4e1e286eb..ee499d6ba84cd616cdc0e6abe9c7cc11484dc4d4 100644
--- a/cpu/sam0_common/periph/flashpage.c
+++ b/cpu/sam0_common/periph/flashpage.c
@@ -17,7 +17,7 @@
  * The sam0 has its flash memory organized in pages and rows, where each row
  * consists of 4 pages. While pages are writable one at a time, it is only
  * possible to delete a complete row. This implementation abstracts this
- * behavior by only writing complete rows at a time, so the FLASH_PAGE_SIZE we
+ * behavior by only writing complete rows at a time, so the FLASHPAGE_SIZE we
  * use in RIOT is actually the row size as specified in the datasheet.
  *
  * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
@@ -25,18 +25,15 @@
  * @}
  */
 
+#include <assert.h>
+
 #include "cpu.h"
-#include "assert.h"
 #include "periph/flashpage.h"
 
 #define NVMCTRL_PAC_BIT     (0x00000002)
 
-void flashpage_write(int page, void *data)
+static void _unlock(void)
 {
-    assert(page < FLASHPAGE_NUMOF);
-
-    uint32_t *page_addr = (uint32_t *)flashpage_addr(page);
-
     /* remove peripheral access lock for the NVMCTRL peripheral */
 #ifdef CPU_FAM_SAML21
     PAC->WRCTRL.reg = (PAC_WRCTRL_KEY_CLR | ID_NVMCTRL);
@@ -45,19 +42,68 @@ void flashpage_write(int page, void *data)
         PAC1->WPCLR.reg = NVMCTRL_PAC_BIT;
     }
 #endif
+}
+
+static void _lock(void)
+{
+    /* put peripheral access lock for the NVMCTRL peripheral */
+#ifdef CPU_FAM_SAML21
+    PAC->WRCTRL.reg = (PAC_WRCTRL_KEY_SET | ID_NVMCTRL);
+#else
+    if (PAC1->WPCLR.reg & NVMCTRL_PAC_BIT) {
+        PAC1->WPSET.reg = NVMCTRL_PAC_BIT;
+    }
+#endif
+}
+
+void flashpage_write_raw(void *target_addr, void *data, size_t len)
+{
+    /* The actual minimal block size for writing is 16B, thus we
+     * assert we write on multiples and no less of that length.
+     */
+    assert(!(len % FLASHPAGE_RAW_BLOCKSIZE));
+
+    /* ensure 4 byte aligned writes */
+    assert(!(((unsigned)target_addr % FLASHPAGE_RAW_ALIGNMENT) ||
+            ((unsigned)data % FLASHPAGE_RAW_ALIGNMENT)));
+
+    /* ensure the length doesn't exceed the actual flash size */
+    assert(((uint8_t*)target_addr + len) <
+           (CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF)));
+
+    uint32_t *dst = (uint32_t *)target_addr;
+    uint32_t *data_addr = (uint32_t *)data;
+
+    /* write 4 bytes in one go */
+    len /= 4;
+
+    _unlock();
+
+    NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC);
+    for (unsigned i = 0; i < len; i++) {
+        *dst++ = *data_addr++;
+    }
+    NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP);
+
+    _lock();
+}
+
+void flashpage_write(int page, void *data)
+{
+    assert(page < FLASHPAGE_NUMOF);
+
+    uint32_t *page_addr = (uint32_t *)flashpage_addr(page);
 
     /* erase given page (the ADDR register uses 16-bit addresses) */
+    _unlock();
     NVMCTRL->ADDR.reg = (((uint32_t)page_addr) >> 1);
     NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER);
     while (!NVMCTRL->INTFLAG.bit.READY) {}
+    _lock();
 
     /* write data to page */
     if (data != NULL) {
-        uint32_t *data_addr = (uint32_t *)data;
-        NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC);
-        for (unsigned i = 0; i < (FLASHPAGE_SIZE / 4); i++) {
-            *page_addr++ = data_addr[i];
-        }
-        NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP);
+        flashpage_write_raw(page_addr, data, FLASHPAGE_SIZE);
     }
+
 }
diff --git a/drivers/include/periph/flashpage.h b/drivers/include/periph/flashpage.h
index 5fce095333b1b11f3d5a9ff21e4179b1873c4933..c585ddf8c06b407e4591529316a9f3b96d35139e 100644
--- a/drivers/include/periph/flashpage.h
+++ b/drivers/include/periph/flashpage.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2016 Freie Universität Berlin
+ *               2017 Inria
  *
  * 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
@@ -28,6 +29,9 @@
  * @brief       Low-level flash page peripheral driver interface
  *
  * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ * @author      Francisco Acosta <francisco.acosta@inria.fr>
+ *
  */
 
 #ifndef PERIPH_FLASHPAGE_H
@@ -47,8 +51,30 @@ extern "C" {
 #ifndef CPU_FLASH_BASE
 #define CPU_FLASH_BASE      (0)
 #endif
+/**
+ * @def     FLASHPAGE_RAW_BLOCKSIZE
+ *
+ * @brief   For raw writings to the flash, this constant must define the
+ *          minimum write length allowed by the MCU.
+ */
+#ifdef DOXYGEN
+#define FLASHPAGE_RAW_BLOCKSIZE
+#endif
 
 /**
+ * @def     FLASHPAGE_RAW_ALIGNMENT
+ *
+ * @brief   The buffers to be written to the flash MUST be aligned, as well as
+ *          the address on which the buffer is written to the flash. This variable
+ *          must be defined for that purpose, according to the MCU align
+ *          requirements.
+ */
+#ifdef DOXYGEN
+#define FLASHPAGE_RAW_ALIGNMENT
+#endif
+/**
+ * @def FLASHPAGE_SIZE
+ *
  * @brief   Make sure the page size and the number of pages is defined
  */
 #ifndef FLASHPAGE_SIZE
@@ -101,16 +127,39 @@ static inline int flashpage_page(void *addr)
  * @brief   Write the given page with the given data
  *
  * @param[in] page      page to write
- * @param[in] data      data to write to the page, MUST be @p FLASHPAGE_SIZE
+ * @param[in] data      data to write to the page, MUST be FLASHPAGE_SIZE
  *                      byte. Set to NULL for page erase only.
  */
 void flashpage_write(int page, void *data);
 
+/**
+ * @brief   Write any number of data bytes to a given location in the
+ *          flash memory
+ *
+ * @warning Make sure the targeted memory area is erased before calling
+ *          this function
+ *
+ * Both target address and data address must be aligned to
+ * FLASHPAGE_RAW_ALIGN. @p len must be a multiple of FLASHPAGE_RAW_BLOCKSIZE.
+ * This function doesn't erase any area in flash, thus be sure the targeted
+ * memory area is erased before writing on it (using the flashpage_write function).
+ *
+ * @param[in] target_addr   address in flash to write to. MUST be aligned
+ *                          to FLASHPAGE_RAW_ALIGNMENT.
+ * @param[in] data          data to write to the address. MUST be aligned
+ *                          to FLASHPAGE_RAW_ALIGNMENT.
+ * @param[in] len           length of the data to be written. It MUST be
+ *                          multiple of FLASHPAGE_RAW_BLOCKSIZE. Also,
+ *                          ensure it doesn't exceed the actual flash
+ *                          memory size.
+ */
+void flashpage_write_raw(void *target_addr, void *data, size_t len);
+
 /**
  * @brief   Read the given page into the given memory location
  *
  * @param[in]  page     page to read
- * @param[out] data     memory to write the page to, MUST be @p FLASHPAGE_SIZE
+ * @param[out] data     memory to write the page to, MUST be FLASHPAGE_SIZE
  *                      byte
  */
 void flashpage_read(int page, void *data);
@@ -119,7 +168,7 @@ void flashpage_read(int page, void *data);
  * @brief   Verify the given page against the given data
  *
  * @param[in] page      page to verify
- * @param[in] data      data to compare page against, MUST be @p FLASHPAGE_SIZE
+ * @param[in] data      data to compare page against, MUST be FLASHPAGE_SIZE
  *                      byte of data
  *
  * @return              FLASHPAGE_OK if data in the page is identical to @p data
@@ -133,7 +182,7 @@ int flashpage_verify(int page, void *data);
  * This is a convenience function wrapping flashpage_write and flashpage_verify.
  *
  * @param[in] page      page to write
- * @param[in] data      data to write to the page, MUST be @p FLASHPAGE_SIZE
+ * @param[in] data      data to write to the page, MUST be FLASHPAGE_SIZE
  *                      byte.
  *
  * @return              FLASHPAGE_OK on success
diff --git a/tests/periph_flashpage/Makefile b/tests/periph_flashpage/Makefile
index d9672f7e6f3e6f2a676bc1bcfffef7e273f1498c..b64e980f4d3bb5a56bd88e775bd56a44be7a1331 100644
--- a/tests/periph_flashpage/Makefile
+++ b/tests/periph_flashpage/Makefile
@@ -1,7 +1,8 @@
 BOARD ?= iotlab-m3
 include ../Makefile.tests_common
 
-FEATURES_REQUIRED = periph_flashpage
+FEATURES_REQUIRED += periph_flashpage
+FEATURES_OPTIONAL += periph_flashpage_raw
 
 USEMODULE += shell
 
diff --git a/tests/periph_flashpage/main.c b/tests/periph_flashpage/main.c
index 41df0f34e996c515c6e22d6125ab8e5a431516c4..e8661a4b006b45483f1819f27be2e84fb4415335 100644
--- a/tests/periph_flashpage/main.c
+++ b/tests/periph_flashpage/main.c
@@ -32,6 +32,20 @@
  */
 static uint8_t page_mem[FLASHPAGE_SIZE];
 
+#ifdef MODULE_PERIPH_FLASHPAGE_RAW
+/*
+ * @brief   Allocate an aligned buffer for raw writings
+ */
+static char raw_buf[64] __attribute__ ((aligned (FLASHPAGE_RAW_ALIGNMENT)));
+
+static uint32_t getaddr(const char *str)
+{
+    uint32_t addr = strtol(str, NULL, 16);
+
+    return addr;
+}
+#endif
+
 static int getpage(const char *str)
 {
     int page = atoi(str);
@@ -164,6 +178,29 @@ static int cmd_write(int argc, char **argv)
     return 0;
 }
 
+#ifdef MODULE_PERIPH_FLASHPAGE_RAW
+static int cmd_write_raw(int argc, char **argv)
+{
+    uint32_t addr;
+
+    if (argc < 3) {
+        printf("usage: %s <addr> <data>\n", argv[0]);
+        return 1;
+    }
+
+    addr = getaddr(argv[1]);
+
+    /* try to align */
+    memcpy(raw_buf, argv[2], strlen(argv[2]));
+
+    flashpage_write_raw((void*)addr, raw_buf, strlen(raw_buf));
+
+    printf("wrote local data to flash address %#lx of len %u\n",
+           addr, strlen(raw_buf));
+    return 0;
+}
+#endif
+
 static int cmd_erase(int argc, char **argv)
 {
     int page;
@@ -248,6 +285,9 @@ static const shell_command_t shell_commands[] = {
     { "dump_local", "Dump the local page buffer to STDOUT", cmd_dump_local },
     { "read", "Read and output the given page", cmd_read },
     { "write", "Write (ASCII) data to the given page", cmd_write },
+#ifdef MODULE_PERIPH_FLASHPAGE_RAW
+    { "write_raw", "Write (ASCII, max 64B) data to the given address", cmd_write_raw },
+#endif
     { "erase", "Erase the given page", cmd_erase },
     { "edit", "Write bytes to the local page", cmd_edit },
     { "test", "Write and verify test pattern", cmd_test },