From bd40f75d370cca1933fdae164751c71e197d363b Mon Sep 17 00:00:00 2001
From: UncleRus <unclerus@gmail.com>
Date: Fri, 4 Mar 2016 01:10:06 +0500
Subject: [PATCH 1/7] Hardware SPI driver

---
 core/esp_spi.c              | 206 ++++++++++++++++++++++++++++++++++++
 core/include/esp/spi.h      | 197 ++++++++++++++++++++++++++++++++++
 core/include/esp/spi_regs.h |   2 +
 3 files changed, 405 insertions(+)
 create mode 100644 core/esp_spi.c
 create mode 100644 core/include/esp/spi.h

diff --git a/core/esp_spi.c b/core/esp_spi.c
new file mode 100644
index 0000000..ac9b0b1
--- /dev/null
+++ b/core/esp_spi.c
@@ -0,0 +1,206 @@
+/*
+ * ESP hardware SPI master driver
+ *
+ * Part of esp-open-rtos
+ * Copyright (c) Ruslan V. Uss, 2016
+ * BSD Licensed as described in the file LICENSE
+ */
+#include "esp/spi.h"
+
+#include "esp/iomux.h"
+#include "esp/gpio.h"
+#include <string.h>
+
+//#include <stdio.h>
+
+#define _SPI0_SCK_GPIO  6
+#define _SPI0_MISO_GPIO 7
+#define _SPI0_MOSI_GPIO 8
+#define _SPI0_HD_GPIO   9
+#define _SPI0_WP_GPIO   10
+#define _SPI0_CS0_GPIO  11
+
+#define _SPI1_MISO_GPIO 12
+#define _SPI1_MOSI_GPIO 13
+#define _SPI1_SCK_GPIO  14
+#define _SPI1_CS0_GPIO  15
+
+#define _SPI0_FUNC 1
+#define _SPI1_FUNC 2
+
+#define _SPI_BUF_SIZE 64
+
+inline static void _set_pin_function(uint8_t pin, uint32_t function)
+{
+    iomux_set_function(gpio_to_iomux(pin), function);
+}
+
+bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi_endianness_t endianness, bool minimal_pins)
+{
+    switch (bus)
+    {
+        case 0:
+            _set_pin_function(_SPI0_MISO_GPIO, _SPI0_FUNC);
+            _set_pin_function(_SPI0_MOSI_GPIO, _SPI0_FUNC);
+            _set_pin_function(_SPI0_SCK_GPIO, _SPI0_FUNC);
+            if (!minimal_pins)
+            {
+                _set_pin_function(_SPI0_HD_GPIO, _SPI0_FUNC);
+                _set_pin_function(_SPI0_WP_GPIO, _SPI0_FUNC);
+                _set_pin_function(_SPI0_CS0_GPIO, _SPI0_FUNC);
+            }
+            break;
+        case 1:
+            _set_pin_function(_SPI1_MISO_GPIO, _SPI1_FUNC);
+            _set_pin_function(_SPI1_MOSI_GPIO, _SPI1_FUNC);
+            _set_pin_function(_SPI1_SCK_GPIO, _SPI1_FUNC);
+            if (!minimal_pins)
+                _set_pin_function(_SPI1_CS0_GPIO, _SPI1_FUNC);
+            break;
+        default:
+            return false;
+    }
+
+    SPI(bus).USER0 = SPI_USER0_MOSI | SPI_USER0_CLOCK_IN_EDGE | SPI_USER0_DUPLEX |
+        (minimal_pins ? 0 : (SPI_USER0_CS_HOLD | SPI_USER0_CS_SETUP));
+
+    spi_set_frequency_div(bus, freq_divider);
+    spi_set_mode(bus, mode);
+    spi_set_msb(bus, msb);
+    spi_set_endianness(bus, endianness);
+
+    return true;
+}
+
+void spi_set_mode(uint8_t bus, spi_mode_t mode)
+{
+    // CPHA
+    if ((uint8_t)mode & 1)
+        SPI(bus).USER0 |= SPI_USER0_CLOCK_OUT_EDGE;
+    else
+        SPI(bus).USER0 &= ~SPI_USER0_CLOCK_OUT_EDGE;
+
+    // CPOL - see http://bbs.espressif.com/viewtopic.php?t=342#p5384
+    if ((uint8_t)mode & 2)
+        SPI(bus).PIN |= SPI_PIN_IDLE_EDGE;
+    else
+        SPI(bus).PIN &= ~SPI_PIN_IDLE_EDGE;
+}
+
+void spi_set_msb(uint8_t bus, bool msb)
+{
+    if (msb)
+        SPI(bus).CTRL0 &= ~(SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER);
+    else
+        SPI(bus).CTRL0 |= (SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER);
+}
+
+void spi_set_endianness(uint8_t bus, spi_endianness_t endianness)
+{
+    if (endianness == SPI_BIG_ENDIAN)
+        SPI(bus).USER0 |= (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER);
+    else
+        SPI(bus).USER0 &= ~(SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER);
+}
+
+void spi_set_frequency_div(uint8_t bus, uint32_t divider)
+{
+    uint32_t predivider = divider & 0xffff;
+    uint32_t count = divider >> 16;
+    if (count > 1 && divider > 1)
+    {
+        predivider = predivider > SPI_CLOCK_DIV_PRE_M + 1 ? SPI_CLOCK_DIV_PRE_M + 1 : predivider;
+        count = count > SPI_CLOCK_COUNT_NUM_M + 1 ? SPI_CLOCK_COUNT_NUM_M + 1 : count;
+        IOMUX.CONF &= ~(bus == 0 ? IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK : IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK);
+        SPI(bus).CLOCK = (((predivider - 1) & SPI_CLOCK_DIV_PRE_M) << SPI_CLOCK_DIV_PRE_S) |
+            (((count - 1)     & SPI_CLOCK_COUNT_NUM_M)  << SPI_CLOCK_COUNT_NUM_S) |
+            (((count / 2 - 1) & SPI_CLOCK_COUNT_HIGH_M) << SPI_CLOCK_COUNT_HIGH_S) |
+            (((count - 1)     & SPI_CLOCK_COUNT_LOW_M)  << SPI_CLOCK_COUNT_LOW_S);
+    }
+    else
+    {
+        IOMUX.CONF |= bus == 0 ? IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK : IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK;
+        SPI(bus).CLOCK = SPI_CLOCK_EQU_SYS_CLOCK;
+    }
+}
+
+inline static void _set_size(uint8_t bus, uint8_t bytes)
+{
+    uint16_t bits = ((uint16_t)bytes << 3) - 1;
+    const uint32_t mask = ~((SPI_USER1_MOSI_BITLEN_M << SPI_USER1_MOSI_BITLEN_S) |
+        (SPI_USER1_MISO_BITLEN_M << SPI_USER1_MISO_BITLEN_S));
+    SPI(bus).USER1 = (SPI(bus).USER1 & mask) | (bits << SPI_USER1_MOSI_BITLEN_S) |
+        (bits << SPI_USER1_MISO_BITLEN_S);
+}
+
+inline static void _wait(uint8_t bus)
+{
+    while (SPI(bus).CMD & SPI_CMD_USR)
+        ;
+}
+
+inline static void _start(uint8_t bus)
+{
+    SPI(bus).CMD |= SPI_CMD_USR;
+}
+
+inline static uint32_t _reverse_bytes(uint32_t value)
+{
+    return (value << 24) | ((value << 8) & 0x00ff0000) | ((value >> 8) & 0x0000ff00) | (value >> 24);
+}
+
+static uint32_t _spi_single_transfer (uint8_t bus, uint32_t data, uint8_t len)
+{
+    _wait(bus);
+    _set_size(bus, len);
+    spi_endianness_t e = spi_get_endianness(bus);
+    SPI(bus).W0 = e == SPI_BIG_ENDIAN ? _reverse_bytes(data) : data;
+    _start(bus);
+    _wait(bus);
+    return e == SPI_BIG_ENDIAN ? _reverse_bytes(SPI(bus).W0) : SPI(bus).W0;
+}
+
+// works properly only with little endian byte order
+static void _spi_buf_transfer (uint8_t bus, uint8_t *data, size_t len)
+{
+    _wait(bus);
+    _set_size(bus, len);
+    memcpy((void *)&SPI(bus).W0, data, len);
+    _start(bus);
+    _wait(bus);
+    memcpy(data, (void *)&SPI(bus).W0, len);
+}
+
+uint8_t spi_transfer_8(uint8_t bus, uint8_t data)
+{
+    return _spi_single_transfer(bus, data, sizeof(data));
+}
+
+uint16_t spi_transfer_16(uint8_t bus, uint16_t data)
+{
+    return _spi_single_transfer(bus, data, sizeof(data));
+}
+
+uint32_t spi_transfer_32(uint8_t bus, uint32_t data)
+{
+    return _spi_single_transfer(bus, data, sizeof(data));
+}
+
+void spi_transfer(uint8_t bus, void *data, size_t len)
+{
+    if (!data || !len) return;
+
+    _wait(bus);
+    spi_endianness_t e = spi_get_endianness(bus);
+    spi_set_endianness(bus, SPI_LITTLE_ENDIAN);
+
+    size_t counts = len / _SPI_BUF_SIZE;
+    for (uint8_t i = 0; i < counts; i++)
+        _spi_buf_transfer(bus, data + i * _SPI_BUF_SIZE, _SPI_BUF_SIZE);
+
+    uint8_t tail = len % _SPI_BUF_SIZE;
+    if (tail)
+        _spi_buf_transfer(bus, data + counts * _SPI_BUF_SIZE, tail);
+
+    spi_set_endianness(bus, e);
+}
diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h
new file mode 100644
index 0000000..6b2b0e2
--- /dev/null
+++ b/core/include/esp/spi.h
@@ -0,0 +1,197 @@
+/**
+ * \file Hardware SPI master driver
+ *
+ * Part of esp-open-rtos
+ *
+ * \copyright Ruslan V. Uss, 2016
+ * BSD Licensed as described in the file LICENSE
+ */
+#ifndef _ESP_SPI_H_
+#define _ESP_SPI_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "esp/spi_regs.h"
+
+// FIXME Better to define it somewhere else. This is not the CPU frequency!
+#define SYSTEM_BUS_FREQ 80000000UL
+
+/**
+ * Macro for use with spi_init and spi_set_frequency_div.
+ * SPI frequency = 80000000 / divider / count
+ * dvider must be in 1..8192 and count in 1..64
+ */
+#define SPI_GET_FREQ_DIV(divider, count) (((count) << 16) | (divider))
+
+/**
+ * Predefinded SPI frequency dividers
+ */
+#define SPI_FREQ_DIV_125K SPI_GET_FREQ_DIV(64, 10) ///< 125kHz
+#define SPI_FREQ_DIV_250K SPI_GET_FREQ_DIV(32, 10) ///< 250kHz
+#define SPI_FREQ_DIV_500K SPI_GET_FREQ_DIV(16, 10) ///< 500kHz
+#define SPI_FREQ_DIV_1M   SPI_GET_FREQ_DIV(8, 10)  ///< 1MHz
+#define SPI_FREQ_DIV_2M   SPI_GET_FREQ_DIV(4, 10)  ///< 2MHz
+#define SPI_FREQ_DIV_4M   SPI_GET_FREQ_DIV(2, 10)  ///< 4MHz
+#define SPI_FREQ_DIV_8M   SPI_GET_FREQ_DIV(5,  2)  ///< 8MHz
+#define SPI_FREQ_DIV_10M  SPI_GET_FREQ_DIV(4,  2)  ///< 10MHz
+#define SPI_FREQ_DIV_20M  SPI_GET_FREQ_DIV(2,  2)  ///< 20MHz
+#define SPI_FREQ_DIV_40M  SPI_GET_FREQ_DIV(1,  2)  ///< 40MHz
+#define SPI_FREQ_DIV_80M  SPI_GET_FREQ_DIV(1,  1)  ///< 80MHz
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef enum _spi_mode_t {
+    SPI_MODE0 = 0,  ///< CPOL = 0, CPHA = 0
+    SPI_MODE1,      ///< CPOL = 0, CPHA = 1
+    SPI_MODE2,      ///< CPOL = 1, CPHA = 0
+    SPI_MODE3       ///< CPOL = 1, CPHA = 1
+} spi_mode_t;
+
+typedef enum _spi_endianness_t {
+    SPI_LITTLE_ENDIAN,
+    SPI_BIG_ENDIAN
+} spi_endianness_t;
+
+/**
+ * \brief Initalize SPI bus
+ * Initalize specified SPI bus and setup appropriate pins:
+ * Bus 0:
+ *   - MISO = GPIO 7
+ *   - MOSI = GPIO 8
+ *   - SCK  = GPIO 6
+ *   - CS0  = GPIO 11 (if minimal_pins is false)
+ *   - HD   = GPIO 9  (if minimal_pins is false)
+ *   - WP   = GPIO 10 (if minimal_pins is false)
+ * Bus 1:
+ *   - MISO = GPIO 12
+ *   - MOSI = GPIO 13
+ *   - SCK  = GPIO 14
+ *   - CS0  = GPIO 15 (if minimal_pins is false)
+ * Note that system flash memory is on the bus 0!
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \param mode Bus mode
+ * \param freq_divider SPI bus frequency divider, use SPI_GET_FREQ_DIV() or predefined value
+ * \param msb Bit order, MSB first if true
+ * \param endianness Byte order
+ * \param minimal_pins If true use the minimal set of pins: MISO, MOSI and SCK.
+ * \return false when error
+ */
+bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi_endianness_t endianness, bool minimal_pins);
+
+/**
+ * \brief Set SPI bus mode
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \param mode Bus mode.
+ */
+void spi_set_mode(uint8_t bus, spi_mode_t mode);
+/**
+ * \brief Get mode of the SPI bus
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \return Bus mode
+ */
+inline spi_mode_t spi_get_mode(uint8_t bus)
+{
+    return (spi_mode_t)((SPI(bus).PIN & SPI_PIN_IDLE_EDGE ? 2 : 0) | (SPI(bus).USER0 & SPI_USER0_CLOCK_OUT_EDGE ? 1 : 0));
+}
+
+/**
+ * \brief Set SPI bus frequency
+ * Examples:
+ *
+ *     spi_set_frequency_div(1, SPI_FREQ_DIV_8M); // 8 MHz, predefined value
+ *     ...
+ *     spi_set_frequency_div(1, SPI_GET_FREQ_DIV(8, 10)); // divider = 8, count = 10,
+ *                                                        // frequency = 80000000 Hz / 8 / 10 = 1000000 Hz
+ *
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \param divider Predivider of the system bus frequency (80MHz) in the 2 low
+ *     bytes and period pulses count in the third byte. Please note that
+ *     divider must be be in range 1..8192 and count in range 2..64. Use the
+ *     macro SPI_GET_FREQ_DIV(divider, count) to get the correct parameter value.
+ */
+void spi_set_frequency_div(uint8_t bus, uint32_t divider);
+/**
+ * \brief Get SPI bus frequency
+ * Note the result is in Hz.
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \return SPI frequency, Hz
+ */
+inline uint32_t spi_get_frequency_hz(uint8_t bus)
+{
+    return SYSTEM_BUS_FREQ /
+        (((SPI(bus).CLOCK >> SPI_CLOCK_DIV_PRE_S) & SPI_CLOCK_DIV_PRE_M) + 1) /
+        (((SPI(bus).CLOCK >> SPI_CLOCK_COUNT_NUM_S) & SPI_CLOCK_COUNT_NUM_M) + 1);
+}
+
+/**
+ * \brief Set SPI bus bit order
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \param msb Bit order, MSB first if true
+ */
+void spi_set_msb(uint8_t bus, bool msb);
+/**
+ * \brief Get SPI bus bit order
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \return msb Bit order, MSB first if true
+ */
+inline bool spi_get_msb(uint8_t bus)
+{
+    return !(SPI(bus).CTRL0 & (SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER));
+}
+
+/**
+ * \brief Set SPI bus byte order
+ * This value is ignored when transferring buffer with spi_transfer()
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \param endianness Byte order
+ */
+void spi_set_endianness(uint8_t bus, spi_endianness_t endianness);
+/**
+ * \brief Get SPI bus byte order
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \return endianness Byte order
+ */
+inline spi_endianness_t spi_get_endianness(uint8_t bus)
+{
+    return SPI(bus).USER0 & (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER)
+    	? SPI_BIG_ENDIAN
+    	: SPI_LITTLE_ENDIAN;
+}
+
+/**
+ * \brief Transfer byte over SPI
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \param data Byte to send
+ * \return Received byte
+ */
+uint8_t spi_transfer_8(uint8_t bus, uint8_t data);
+/**
+ * \brief Transfer word over SPI
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \param data Word to send
+ * \return Received word
+ */
+uint16_t spi_transfer_16(uint8_t bus, uint16_t data);
+/**
+ * \brief Transfer dword over SPI
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \param data dword to send
+ * \return Received dword
+ */
+uint32_t spi_transfer_32(uint8_t bus, uint32_t data);
+/**
+ * \brief Transfer buffer over SPI
+ * \param bus Bus ID: 0 - system, 1 - user
+ * \param data Data to send. Buffer contents will be replaced with received data
+ * \param len Buffer size
+ */
+void spi_transfer(uint8_t bus, void *data, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ESP_SPI_H_ */
diff --git a/core/include/esp/spi_regs.h b/core/include/esp/spi_regs.h
index dfcf998..37eb113 100644
--- a/core/include/esp/spi_regs.h
+++ b/core/include/esp/spi_regs.h
@@ -152,6 +152,7 @@ _Static_assert(sizeof(struct SPI_REGS) == 0x100, "SPI_REGS is the wrong size");
 #define SPI_USER0_CS_SETUP                 BIT(5)
 #define SPI_USER0_CS_HOLD                  BIT(4)
 #define SPI_USER0_FLASH_MODE               BIT(2)
+#define SPI_USER0_DUPLEX                   BIT(0)
 
 /* Details for USER1 register */
 
@@ -173,6 +174,7 @@ _Static_assert(sizeof(struct SPI_REGS) == 0x100, "SPI_REGS is the wrong size");
 
 /* Details for PIN register */
 
+#define SPI_PIN_IDLE_EDGE                  BIT(29)  ///< CPOL
 #define SPI_PIN_CS2_DISABLE                BIT(2)
 #define SPI_PIN_CS1_DISABLE                BIT(1)
 #define SPI_PIN_CS0_DISABLE                BIT(0)

From 0e3f3bb2ca36766bc3e759fcce5348d8d6073a58 Mon Sep 17 00:00:00 2001
From: UncleRus <unclerus@gmail.com>
Date: Sat, 5 Mar 2016 03:07:57 +0500
Subject: [PATCH 2/7] Frequency divider bug

---
 core/esp_spi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/esp_spi.c b/core/esp_spi.c
index ac9b0b1..56ed196 100644
--- a/core/esp_spi.c
+++ b/core/esp_spi.c
@@ -107,7 +107,7 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider)
 {
     uint32_t predivider = divider & 0xffff;
     uint32_t count = divider >> 16;
-    if (count > 1 && divider > 1)
+    if (count > 1 || divider > 1)
     {
         predivider = predivider > SPI_CLOCK_DIV_PRE_M + 1 ? SPI_CLOCK_DIV_PRE_M + 1 : predivider;
         count = count > SPI_CLOCK_COUNT_NUM_M + 1 ? SPI_CLOCK_COUNT_NUM_M + 1 : count;

From b5c2120efce06895fb0dcba53f7b4f2dd67b7231 Mon Sep 17 00:00:00 2001
From: UncleRus <unclerus@gmail.com>
Date: Sun, 6 Mar 2016 01:24:59 +0500
Subject: [PATCH 3/7] Removed redeclaration of peripheral bus freq

---
 core/esp_spi.c         | 6 +++---
 core/include/esp/spi.h | 6 ++----
 2 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/core/esp_spi.c b/core/esp_spi.c
index 56ed196..02dae76 100644
--- a/core/esp_spi.c
+++ b/core/esp_spi.c
@@ -194,13 +194,13 @@ void spi_transfer(uint8_t bus, void *data, size_t len)
     spi_endianness_t e = spi_get_endianness(bus);
     spi_set_endianness(bus, SPI_LITTLE_ENDIAN);
 
-    size_t counts = len / _SPI_BUF_SIZE;
-    for (uint8_t i = 0; i < counts; i++)
+    size_t blocks = len / _SPI_BUF_SIZE;
+    for (uint8_t i = 0; i < blocks; i++)
         _spi_buf_transfer(bus, data + i * _SPI_BUF_SIZE, _SPI_BUF_SIZE);
 
     uint8_t tail = len % _SPI_BUF_SIZE;
     if (tail)
-        _spi_buf_transfer(bus, data + counts * _SPI_BUF_SIZE, tail);
+        _spi_buf_transfer(bus, data + blocks * _SPI_BUF_SIZE, tail);
 
     spi_set_endianness(bus, e);
 }
diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h
index 6b2b0e2..55b9d14 100644
--- a/core/include/esp/spi.h
+++ b/core/include/esp/spi.h
@@ -12,9 +12,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include "esp/spi_regs.h"
-
-// FIXME Better to define it somewhere else. This is not the CPU frequency!
-#define SYSTEM_BUS_FREQ 80000000UL
+#include "esp/clocks.h"
 
 /**
  * Macro for use with spi_init and spi_set_frequency_div.
@@ -121,7 +119,7 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider);
  */
 inline uint32_t spi_get_frequency_hz(uint8_t bus)
 {
-    return SYSTEM_BUS_FREQ /
+    return APB_CLK_FREQ /
         (((SPI(bus).CLOCK >> SPI_CLOCK_DIV_PRE_S) & SPI_CLOCK_DIV_PRE_M) + 1) /
         (((SPI(bus).CLOCK >> SPI_CLOCK_COUNT_NUM_S) & SPI_CLOCK_COUNT_NUM_M) + 1);
 }

From 9dc565ff7cc613363a6ea69d8b7e1e234455bafc Mon Sep 17 00:00:00 2001
From: UncleRus <unclerus@gmail.com>
Date: Tue, 8 Mar 2016 04:00:11 +0500
Subject: [PATCH 4/7] Separate send/receive buffers

---
 core/esp_spi.c         | 26 ++++++++++++++++----------
 core/include/esp/spi.h |  5 +++--
 2 files changed, 19 insertions(+), 12 deletions(-)

diff --git a/core/esp_spi.c b/core/esp_spi.c
index 02dae76..320716c 100644
--- a/core/esp_spi.c
+++ b/core/esp_spi.c
@@ -11,8 +11,6 @@
 #include "esp/gpio.h"
 #include <string.h>
 
-//#include <stdio.h>
-
 #define _SPI0_SCK_GPIO  6
 #define _SPI0_MISO_GPIO 7
 #define _SPI0_MOSI_GPIO 8
@@ -161,14 +159,15 @@ static uint32_t _spi_single_transfer (uint8_t bus, uint32_t data, uint8_t len)
 }
 
 // works properly only with little endian byte order
-static void _spi_buf_transfer (uint8_t bus, uint8_t *data, size_t len)
+static void _spi_buf_transfer (uint8_t bus, const uint8_t *out_data, uint8_t *in_data, size_t len)
 {
     _wait(bus);
     _set_size(bus, len);
-    memcpy((void *)&SPI(bus).W0, data, len);
+    memcpy((void *)&SPI(bus).W0, out_data, len);
     _start(bus);
     _wait(bus);
-    memcpy(data, (void *)&SPI(bus).W0, len);
+    if (in_data)
+        memcpy(in_data, (void *)&SPI(bus).W0, len);
 }
 
 uint8_t spi_transfer_8(uint8_t bus, uint8_t data)
@@ -186,21 +185,28 @@ uint32_t spi_transfer_32(uint8_t bus, uint32_t data)
     return _spi_single_transfer(bus, data, sizeof(data));
 }
 
-void spi_transfer(uint8_t bus, void *data, size_t len)
+void spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len)
 {
-    if (!data || !len) return;
+    if (!out_data || !len) return;
 
     _wait(bus);
     spi_endianness_t e = spi_get_endianness(bus);
     spi_set_endianness(bus, SPI_LITTLE_ENDIAN);
 
     size_t blocks = len / _SPI_BUF_SIZE;
-    for (uint8_t i = 0; i < blocks; i++)
-        _spi_buf_transfer(bus, data + i * _SPI_BUF_SIZE, _SPI_BUF_SIZE);
+    for (size_t i = 0; i < blocks; i++)
+    {
+        size_t offset = i * _SPI_BUF_SIZE;
+        _spi_buf_transfer(bus, (const uint8_t *)out_data + offset,
+            in_data ? (uint8_t *)in_data + offset : NULL, _SPI_BUF_SIZE);
+    }
 
     uint8_t tail = len % _SPI_BUF_SIZE;
     if (tail)
-        _spi_buf_transfer(bus, data + blocks * _SPI_BUF_SIZE, tail);
+    {
+        _spi_buf_transfer(bus, (const uint8_t *)out_data + blocks * _SPI_BUF_SIZE,
+            in_data ? (uint8_t *)in_data + blocks * _SPI_BUF_SIZE : NULL, tail);
+    }
 
     spi_set_endianness(bus, e);
 }
diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h
index 55b9d14..5de966c 100644
--- a/core/include/esp/spi.h
+++ b/core/include/esp/spi.h
@@ -183,10 +183,11 @@ uint32_t spi_transfer_32(uint8_t bus, uint32_t data);
 /**
  * \brief Transfer buffer over SPI
  * \param bus Bus ID: 0 - system, 1 - user
- * \param data Data to send. Buffer contents will be replaced with received data
+ * \param out_data Data to send.
+ * \param in_data Receive buffer. If NULL, received data will be lost.
  * \param len Buffer size
  */
-void spi_transfer(uint8_t bus, void *data, size_t len);
+void spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len);
 
 #ifdef __cplusplus
 }

From b4554b5806a0af098ee60175ba7e3cf9607f2f35 Mon Sep 17 00:00:00 2001
From: UncleRus <unclerus@gmail.com>
Date: Sun, 13 Mar 2016 01:59:52 +0500
Subject: [PATCH 5/7] SPI mode bug fixed, SPI endianness bugs fixed, new
 spi_transfer()

---
 core/esp_spi.c         | 117 ++++++++++++++++++++++++++---------------
 core/include/esp/spi.h |  42 +++++++++------
 2 files changed, 102 insertions(+), 57 deletions(-)

diff --git a/core/esp_spi.c b/core/esp_spi.c
index 320716c..d91e1b2 100644
--- a/core/esp_spi.c
+++ b/core/esp_spi.c
@@ -72,19 +72,32 @@ bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi
 
 void spi_set_mode(uint8_t bus, spi_mode_t mode)
 {
+    bool cpha = (uint8_t)mode & 1;
+    bool cpol = (uint8_t)mode & 2;
+    if (cpol)
+        cpha = !cpha;  // CPHA must be inverted when CPOL = 1, I have no idea why
+
     // CPHA
-    if ((uint8_t)mode & 1)
+    if (cpha)
         SPI(bus).USER0 |= SPI_USER0_CLOCK_OUT_EDGE;
     else
         SPI(bus).USER0 &= ~SPI_USER0_CLOCK_OUT_EDGE;
 
     // CPOL - see http://bbs.espressif.com/viewtopic.php?t=342#p5384
-    if ((uint8_t)mode & 2)
+    if (cpol)
         SPI(bus).PIN |= SPI_PIN_IDLE_EDGE;
     else
         SPI(bus).PIN &= ~SPI_PIN_IDLE_EDGE;
 }
 
+spi_mode_t spi_get_mode(uint8_t bus)
+{
+    uint8_t cpha = SPI(bus).USER0 & SPI_USER0_CLOCK_OUT_EDGE ? 1 : 0;
+    uint8_t cpol = SPI(bus).PIN & SPI_PIN_IDLE_EDGE ? 2 : 0;
+
+    return (spi_mode_t)(cpol | (cpol ? 1 - cpha : cpha)); // see spi_set_mode
+}
+
 void spi_set_msb(uint8_t bus, bool msb)
 {
     if (msb)
@@ -103,17 +116,15 @@ void spi_set_endianness(uint8_t bus, spi_endianness_t endianness)
 
 void spi_set_frequency_div(uint8_t bus, uint32_t divider)
 {
-    uint32_t predivider = divider & 0xffff;
-    uint32_t count = divider >> 16;
-    if (count > 1 || divider > 1)
+    uint32_t predivider = (divider & 0xffff) - 1;
+    uint32_t count = (divider >> 16) - 1;
+    if (count || predivider)
     {
-        predivider = predivider > SPI_CLOCK_DIV_PRE_M + 1 ? SPI_CLOCK_DIV_PRE_M + 1 : predivider;
-        count = count > SPI_CLOCK_COUNT_NUM_M + 1 ? SPI_CLOCK_COUNT_NUM_M + 1 : count;
         IOMUX.CONF &= ~(bus == 0 ? IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK : IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK);
-        SPI(bus).CLOCK = (((predivider - 1) & SPI_CLOCK_DIV_PRE_M) << SPI_CLOCK_DIV_PRE_S) |
-            (((count - 1)     & SPI_CLOCK_COUNT_NUM_M)  << SPI_CLOCK_COUNT_NUM_S) |
-            (((count / 2 - 1) & SPI_CLOCK_COUNT_HIGH_M) << SPI_CLOCK_COUNT_HIGH_S) |
-            (((count - 1)     & SPI_CLOCK_COUNT_LOW_M)  << SPI_CLOCK_COUNT_LOW_S);
+        SPI(bus).CLOCK = VAL2FIELD_M(SPI_CLOCK_DIV_PRE, predivider) |
+                         VAL2FIELD_M(SPI_CLOCK_COUNT_NUM, count) |
+                         VAL2FIELD_M(SPI_CLOCK_COUNT_HIGH, count / 2) |
+                         VAL2FIELD_M(SPI_CLOCK_COUNT_LOW, count);
     }
     else
     {
@@ -124,11 +135,9 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider)
 
 inline static void _set_size(uint8_t bus, uint8_t bytes)
 {
-    uint16_t bits = ((uint16_t)bytes << 3) - 1;
-    const uint32_t mask = ~((SPI_USER1_MOSI_BITLEN_M << SPI_USER1_MOSI_BITLEN_S) |
-        (SPI_USER1_MISO_BITLEN_M << SPI_USER1_MISO_BITLEN_S));
-    SPI(bus).USER1 = (SPI(bus).USER1 & mask) | (bits << SPI_USER1_MOSI_BITLEN_S) |
-        (bits << SPI_USER1_MISO_BITLEN_S);
+    uint32_t bits = ((uint32_t)bytes << 3) - 1;
+    SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_MISO_BITLEN, bits);
+    SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_MOSI_BITLEN, bits);
 }
 
 inline static void _wait(uint8_t bus)
@@ -142,71 +151,95 @@ inline static void _start(uint8_t bus)
     SPI(bus).CMD |= SPI_CMD_USR;
 }
 
-inline static uint32_t _reverse_bytes(uint32_t value)
+inline static uint32_t _swap_bytes(uint32_t value)
 {
     return (value << 24) | ((value << 8) & 0x00ff0000) | ((value >> 8) & 0x0000ff00) | (value >> 24);
 }
 
-static uint32_t _spi_single_transfer (uint8_t bus, uint32_t data, uint8_t len)
+inline static uint32_t _swap_words(uint32_t value)
 {
-    _wait(bus);
-    _set_size(bus, len);
-    spi_endianness_t e = spi_get_endianness(bus);
-    SPI(bus).W0 = e == SPI_BIG_ENDIAN ? _reverse_bytes(data) : data;
-    _start(bus);
-    _wait(bus);
-    return e == SPI_BIG_ENDIAN ? _reverse_bytes(SPI(bus).W0) : SPI(bus).W0;
+    return (value << 16) | (value >> 16);
 }
 
-// works properly only with little endian byte order
-static void _spi_buf_transfer (uint8_t bus, const uint8_t *out_data, uint8_t *in_data, size_t len)
+static void _prepare_buffer(uint8_t bus, size_t len, spi_endianness_t e, spi_word_size_t word_size)
+{
+    if (e == SPI_LITTLE_ENDIAN || word_size == SPI_32BIT) return;
+
+    if (word_size == SPI_16BIT)
+    {
+        if (len % 2)
+            len ++;
+        len /= 2;
+    }
+
+    uint32_t *data = (uint32_t *)&SPI(bus).W0;
+    for (size_t i = 0; i < len; i ++)
+    {
+        data[i] = word_size == SPI_16BIT
+            ? _swap_words(data[i])
+            : _swap_bytes(data[i]);
+    }
+}
+
+static void _spi_buf_transfer(uint8_t bus, const void *out_data, void *in_data,
+    size_t len, spi_endianness_t e, spi_word_size_t word_size)
 {
     _wait(bus);
-    _set_size(bus, len);
-    memcpy((void *)&SPI(bus).W0, out_data, len);
+    size_t bytes = len * (uint8_t)word_size;
+    _set_size(bus, bytes);
+    memcpy((void *)&SPI(bus).W0, out_data, bytes);
+    _prepare_buffer(bus, len, e, word_size);
     _start(bus);
     _wait(bus);
     if (in_data)
-        memcpy(in_data, (void *)&SPI(bus).W0, len);
+    {
+        _prepare_buffer(bus, len, e, word_size);
+        memcpy(in_data, (void *)&SPI(bus).W0, bytes);
+    }
 }
 
 uint8_t spi_transfer_8(uint8_t bus, uint8_t data)
 {
-    return _spi_single_transfer(bus, data, sizeof(data));
+    uint8_t res;
+    _spi_buf_transfer(bus, &data, &res, 1, spi_get_endianness(bus), SPI_8BIT);
+    return res;
 }
 
 uint16_t spi_transfer_16(uint8_t bus, uint16_t data)
 {
-    return _spi_single_transfer(bus, data, sizeof(data));
+    uint16_t res;
+    _spi_buf_transfer(bus, &data, &res, 1, spi_get_endianness(bus), SPI_16BIT);
+    return res;
 }
 
 uint32_t spi_transfer_32(uint8_t bus, uint32_t data)
 {
-    return _spi_single_transfer(bus, data, sizeof(data));
+    uint32_t res;
+    _spi_buf_transfer(bus, &data, &res, 1, spi_get_endianness(bus), SPI_32BIT);
+    return res;
 }
 
-void spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len)
+size_t spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len, spi_word_size_t word_size)
 {
-    if (!out_data || !len) return;
+    if (!out_data || !len) return 0;
 
-    _wait(bus);
     spi_endianness_t e = spi_get_endianness(bus);
-    spi_set_endianness(bus, SPI_LITTLE_ENDIAN);
+    uint8_t buf_size = _SPI_BUF_SIZE / (uint8_t)word_size;
 
-    size_t blocks = len / _SPI_BUF_SIZE;
+    size_t blocks = len / buf_size;
     for (size_t i = 0; i < blocks; i++)
     {
         size_t offset = i * _SPI_BUF_SIZE;
         _spi_buf_transfer(bus, (const uint8_t *)out_data + offset,
-            in_data ? (uint8_t *)in_data + offset : NULL, _SPI_BUF_SIZE);
+            in_data ? (uint8_t *)in_data + offset : NULL, buf_size, e, word_size);
     }
 
-    uint8_t tail = len % _SPI_BUF_SIZE;
+    uint8_t tail = len % buf_size;
     if (tail)
     {
         _spi_buf_transfer(bus, (const uint8_t *)out_data + blocks * _SPI_BUF_SIZE,
-            in_data ? (uint8_t *)in_data + blocks * _SPI_BUF_SIZE : NULL, tail);
+            in_data ? (uint8_t *)in_data + blocks * _SPI_BUF_SIZE : NULL, tail, e, word_size);
     }
 
-    spi_set_endianness(bus, e);
+    return len;
 }
diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h
index 5de966c..ec86e9d 100644
--- a/core/include/esp/spi.h
+++ b/core/include/esp/spi.h
@@ -49,10 +49,16 @@ typedef enum _spi_mode_t {
 } spi_mode_t;
 
 typedef enum _spi_endianness_t {
-    SPI_LITTLE_ENDIAN,
+    SPI_LITTLE_ENDIAN = 0,
     SPI_BIG_ENDIAN
 } spi_endianness_t;
 
+typedef enum _spi_word_size_t {
+    SPI_8BIT  = 1,  ///< 1 byte, no endian swapping
+    SPI_16BIT = 2,  ///< 2 bytes, swap 16-bit values in SPI_BIG_ENDIAN mode
+    SPI_32BIT = 4   ///< 4 bytes, swap 32-bit values in SPI_BIG_ENDIAN mode
+} spi_word_size_t;
+
 /**
  * \brief Initalize SPI bus
  * Initalize specified SPI bus and setup appropriate pins:
@@ -90,10 +96,7 @@ void spi_set_mode(uint8_t bus, spi_mode_t mode);
  * \param bus Bus ID: 0 - system, 1 - user
  * \return Bus mode
  */
-inline spi_mode_t spi_get_mode(uint8_t bus)
-{
-    return (spi_mode_t)((SPI(bus).PIN & SPI_PIN_IDLE_EDGE ? 2 : 0) | (SPI(bus).USER0 & SPI_USER0_CLOCK_OUT_EDGE ? 1 : 0));
-}
+spi_mode_t spi_get_mode(uint8_t bus);
 
 /**
  * \brief Set SPI bus frequency
@@ -120,8 +123,8 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider);
 inline uint32_t spi_get_frequency_hz(uint8_t bus)
 {
     return APB_CLK_FREQ /
-        (((SPI(bus).CLOCK >> SPI_CLOCK_DIV_PRE_S) & SPI_CLOCK_DIV_PRE_M) + 1) /
-        (((SPI(bus).CLOCK >> SPI_CLOCK_COUNT_NUM_S) & SPI_CLOCK_COUNT_NUM_M) + 1);
+    	(FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) /
+    	(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
 }
 
 /**
@@ -155,39 +158,48 @@ void spi_set_endianness(uint8_t bus, spi_endianness_t endianness);
 inline spi_endianness_t spi_get_endianness(uint8_t bus)
 {
     return SPI(bus).USER0 & (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER)
-    	? SPI_BIG_ENDIAN
-    	: SPI_LITTLE_ENDIAN;
+        ? SPI_BIG_ENDIAN
+        : SPI_LITTLE_ENDIAN;
 }
 
 /**
- * \brief Transfer byte over SPI
+ * \brief Transfer 8 bits over SPI
  * \param bus Bus ID: 0 - system, 1 - user
  * \param data Byte to send
  * \return Received byte
  */
 uint8_t spi_transfer_8(uint8_t bus, uint8_t data);
 /**
- * \brief Transfer word over SPI
+ * \brief Transfer 16 bits over SPI
  * \param bus Bus ID: 0 - system, 1 - user
  * \param data Word to send
  * \return Received word
  */
 uint16_t spi_transfer_16(uint8_t bus, uint16_t data);
 /**
- * \brief Transfer dword over SPI
+ * \brief Transfer 32 bits over SPI
  * \param bus Bus ID: 0 - system, 1 - user
  * \param data dword to send
  * \return Received dword
  */
 uint32_t spi_transfer_32(uint8_t bus, uint32_t data);
 /**
- * \brief Transfer buffer over SPI
+ * \brief Transfer buffer of words over SPI
+ * Please note that the buffer size is in words, not in bytes!
+ * Example:
+ *    const uint16_t out_buf[4] = { 0xa0b0, 0xa1b1, 0xa2b2, 0xa3b3 };
+ *    uint16_t in_buf[4];
+ *    spi_init(1, SPI_MODE1, SPI_FREQ_DIV_4M, true, SPI_BIG_ENDIAN, true);
+ *    spi_transfer(1, buf, in_buf, 4, SPI_16BIT); // len = 4 words = 8 bytes
+ *
  * \param bus Bus ID: 0 - system, 1 - user
  * \param out_data Data to send.
  * \param in_data Receive buffer. If NULL, received data will be lost.
- * \param len Buffer size
+ * \param len Buffer size in words
+ * \param word_size Size of the word
+ * \return Transmitted/received words count
  */
-void spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len);
+size_t spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len, spi_word_size_t word_size);
 
 #ifdef __cplusplus
 }

From 22ceb08b49b55ab2da408982a2aa062a7796e262 Mon Sep 17 00:00:00 2001
From: UncleRus <unclerus@gmail.com>
Date: Sun, 13 Mar 2016 02:25:15 +0500
Subject: [PATCH 6/7] Tabs to spaces. Again.

---
 core/include/esp/spi.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h
index ec86e9d..0b8e053 100644
--- a/core/include/esp/spi.h
+++ b/core/include/esp/spi.h
@@ -123,8 +123,8 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider);
 inline uint32_t spi_get_frequency_hz(uint8_t bus)
 {
     return APB_CLK_FREQ /
-    	(FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) /
-    	(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
+        (FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) /
+        (FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
 }
 
 /**

From b0fb8736a8a64c2f38f7eef875c965a1823f1011 Mon Sep 17 00:00:00 2001
From: UncleRus <unclerus@gmail.com>
Date: Sun, 13 Mar 2016 16:48:05 +0500
Subject: [PATCH 7/7] Comments updated

---
 core/include/esp/spi.h | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/core/include/esp/spi.h b/core/include/esp/spi.h
index 0b8e053..a2403bf 100644
--- a/core/include/esp/spi.h
+++ b/core/include/esp/spi.h
@@ -54,9 +54,9 @@ typedef enum _spi_endianness_t {
 } spi_endianness_t;
 
 typedef enum _spi_word_size_t {
-    SPI_8BIT  = 1,  ///< 1 byte, no endian swapping
-    SPI_16BIT = 2,  ///< 2 bytes, swap 16-bit values in SPI_BIG_ENDIAN mode
-    SPI_32BIT = 4   ///< 4 bytes, swap 32-bit values in SPI_BIG_ENDIAN mode
+    SPI_8BIT  = 1,  ///< 1 byte
+    SPI_16BIT = 2,  ///< 2 bytes
+    SPI_32BIT = 4   ///< 4 bytes
 } spi_word_size_t;
 
 /**
@@ -145,7 +145,6 @@ inline bool spi_get_msb(uint8_t bus)
 
 /**
  * \brief Set SPI bus byte order
- * This value is ignored when transferring buffer with spi_transfer()
  * \param bus Bus ID: 0 - system, 1 - user
  * \param endianness Byte order
  */
@@ -187,10 +186,10 @@ uint32_t spi_transfer_32(uint8_t bus, uint32_t data);
  * \brief Transfer buffer of words over SPI
  * Please note that the buffer size is in words, not in bytes!
  * Example:
- *    const uint16_t out_buf[4] = { 0xa0b0, 0xa1b1, 0xa2b2, 0xa3b3 };
- *    uint16_t in_buf[4];
+ *    const uint16_t out_buf[] = { 0xa0b0, 0xa1b1, 0xa2b2, 0xa3b3 };
+ *    uint16_t in_buf[sizeof(out_buf)];
  *    spi_init(1, SPI_MODE1, SPI_FREQ_DIV_4M, true, SPI_BIG_ENDIAN, true);
- *    spi_transfer(1, buf, in_buf, 4, SPI_16BIT); // len = 4 words = 8 bytes
+ *    spi_transfer(1, out_buf, in_buf, sizeof(out_buf), SPI_16BIT); // len = 4 words * 2 bytes = 8 bytes
  *
  * \param bus Bus ID: 0 - system, 1 - user
  * \param out_data Data to send.