From c36feab84564a898483a9e431fb58b20f3d380de Mon Sep 17 00:00:00 2001
From: Alex Stewart <Alexander.Stewart@consensuscorp.com>
Date: Sun, 21 Feb 2016 18:34:11 -0800
Subject: [PATCH 1/2] Separate pullup config out of `gpio_enable()`

Created `gpio_set_pullup` to configure pullups independently of direction.
Removed GPIO_INPUT_PULLUP direction type.
Added misc other helper functions in iomux.h
---
 core/esp_gpio.c          | 42 +++++++++++++++++++++-------------------
 core/include/esp/gpio.h  | 26 ++++++++++++++++++++++---
 core/include/esp/iomux.h | 38 +++++++++++++++++++++++++-----------
 core/include/esp/types.h |  1 +
 4 files changed, 73 insertions(+), 34 deletions(-)

diff --git a/core/esp_gpio.c b/core/esp_gpio.c
index 23ddf22..8d12be1 100644
--- a/core/esp_gpio.c
+++ b/core/esp_gpio.c
@@ -8,32 +8,34 @@
 
 void gpio_enable(const uint8_t gpio_num, const gpio_direction_t direction)
 {
-    uint32_t iomux_flags;
-
-    switch(direction) {
+    switch (direction) {
     case GPIO_INPUT:
-        iomux_flags = 0;
+        GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num);
+        iomux_set_gpio_function(gpio_num, false);
         break;
     case GPIO_OUTPUT:
-        iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
+        GPIO.CONF[gpio_num] &= ~GPIO_CONF_OPEN_DRAIN;
+        GPIO.ENABLE_OUT_SET = BIT(gpio_num);
+        iomux_set_gpio_function(gpio_num, true);
         break;
     case GPIO_OUT_OPEN_DRAIN:
-        iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
-        break;
-    case GPIO_INPUT_PULLUP:
-        iomux_flags = IOMUX_PIN_PULLUP;
-        break;
-    default:
-        return; /* Invalid direction flag */
-    }
-    iomux_set_gpio_function(gpio_num, iomux_flags);
-    if(direction == GPIO_OUT_OPEN_DRAIN)
         GPIO.CONF[gpio_num] |= GPIO_CONF_OPEN_DRAIN;
-    else
-        GPIO.CONF[gpio_num] &= ~GPIO_CONF_OPEN_DRAIN;
-    if (iomux_flags & IOMUX_PIN_OUTPUT_ENABLE)
         GPIO.ENABLE_OUT_SET = BIT(gpio_num);
-    else
-        GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num);
+        iomux_set_gpio_function(gpio_num, true);
+        break;
+    }
+}
+
+void gpio_set_pullup(uint8_t gpio_num, bool enabled, bool enabled_during_sleep)
+{
+    uint32_t flags = 0;
+
+    if (enabled) {
+        flags |= IOMUX_PIN_PULLUP;
+    }
+    if (enabled_during_sleep) {
+        flags |= IOMUX_PIN_PULLUP_SLEEP;
+    }
+    iomux_set_pullup_flags(gpio_to_iomux(gpio_num), flags);
 }
 
diff --git a/core/include/esp/gpio.h b/core/include/esp/gpio.h
index b5efcaf..95b3c31 100644
--- a/core/include/esp/gpio.h
+++ b/core/include/esp/gpio.h
@@ -17,14 +17,21 @@ typedef enum {
     GPIO_INPUT,
     GPIO_OUTPUT,         /* "Standard" push-pull output */
     GPIO_OUT_OPEN_DRAIN, /* Open drain output */
-    GPIO_INPUT_PULLUP,
 } gpio_direction_t;
 
-/* Enable GPIO on the specified pin, and set it to input/output/ with
- *  pullup as needed
+/* Enable GPIO on the specified pin, and set it to input or output mode
  */
 void gpio_enable(const uint8_t gpio_num, const gpio_direction_t direction);
 
+/* Enable/disable internal pullup resistor for a particular GPIO
+ *
+ * Note: According to Espressif, pullup resistor values are between 30K and
+ * 100K ohms (see http://bbs.espressif.com/viewtopic.php?t=1079#p4097)
+ * However, measured values suggest that the actual value is likely to be close
+ * to 47K in reality.
+ */
+void gpio_set_pullup(uint8_t gpio_num, bool enabled, bool enabled_during_sleep);
+
 /* Disable GPIO on the specified pin, and set it Hi-Z.
  *
  * If later muxing this pin to a different function, make sure to set
@@ -36,6 +43,19 @@ static inline void gpio_disable(const uint8_t gpio_num)
     *gpio_iomux_reg(gpio_num) &= ~IOMUX_PIN_OUTPUT_ENABLE;
 }
 
+/* Set whether the specified pin continues to drive its output when the ESP8266
+ * goes into sleep mode.  Note that this setting is reset to off whenever
+ * gpio_enable is called, so this must be called after calling that function.
+ */
+static inline void gpio_set_output_on_sleep(const uint8_t gpio_num, bool enabled)
+{
+    if (enabled) {
+        IOMUX.PIN[gpio_to_iomux(gpio_num)] |= IOMUX_PIN_OUTPUT_ENABLE_SLEEP;
+    } else {
+        IOMUX.PIN[gpio_to_iomux(gpio_num)] &= ~IOMUX_PIN_OUTPUT_ENABLE_SLEEP;
+    }
+}
+
 /* Set output of a pin high or low.
  *
  * Only works if pin has been set to GPIO_OUTPUT via gpio_enable()
diff --git a/core/include/esp/iomux.h b/core/include/esp/iomux.h
index 214c9e8..9948784 100644
--- a/core/include/esp/iomux.h
+++ b/core/include/esp/iomux.h
@@ -43,25 +43,41 @@ inline static esp_reg_t gpio_iomux_reg(const uint8_t gpio_number)
     return &(IOMUX.PIN[gpio_to_iomux(gpio_number)]);
 }
 
+inline static void iomux_set_function(uint8_t iomux_num, uint32_t func)
+{
+    uint32_t prev = IOMUX.PIN[iomux_num] & ~IOMUX_PIN_FUNC_MASK;
+    IOMUX.PIN[iomux_num] = IOMUX_FUNC(func) | prev;
+}
+
+inline static void iomux_set_direction_flags(uint8_t iomux_num, uint32_t dir_flags)
+{
+    uint32_t mask = IOMUX_PIN_OUTPUT_ENABLE | IOMUX_PIN_OUTPUT_ENABLE_SLEEP;
+    uint32_t prev = IOMUX.PIN[iomux_num] & ~mask;
+    IOMUX.PIN[iomux_num] = dir_flags | prev;
+}
+
+inline static void iomux_set_pullup_flags(uint8_t iomux_num, uint32_t pullup_flags)
+{
+    uint32_t mask = IOMUX_PIN_PULLUP | IOMUX_PIN_PULLDOWN | IOMUX_PIN_PULLUP_SLEEP | IOMUX_PIN_PULLDOWN_SLEEP;
+    uint32_t prev = IOMUX.PIN[iomux_num] & ~mask;
+    IOMUX.PIN[iomux_num] = pullup_flags | prev;
+}
+
 /**
  * Set a pin to the GPIO function.
  *
  * This allows you to set pins to GPIO without knowing in advance the
  * exact register masks to use.
  *
- * flags can be any of IOMUX_PIN_OUTPUT_ENABLE, IOMUX_PIN_PULLUP, IOMUX_PIN_PULLDOWN, etc. Any other flags will be cleared.
- *
- * Equivalent to a direct register operation if gpio_number is known at compile time.
- * ie the following are equivalent:
- *
- * iomux_set_gpio_function(12, IOMUX_PIN_OUTPUT_ENABLE);
- * IOMUX_GPIO12 = IOMUX_GPIO12_FUNC_GPIO | IOMUX_PIN_OUTPUT_ENABLE;
+ * Sets the function and direction, but leaves the pullup configuration the
+ * same as before.
  */
-inline static void iomux_set_gpio_function(const uint8_t gpio_number, const uint32_t flags)
+inline static void iomux_set_gpio_function(uint8_t gpio_number, bool output_enable)
 {
-    const uint8_t reg_idx = gpio_to_iomux(gpio_number);
-    const uint32_t func = (reg_idx > 11 ? IOMUX_FUNC(0) : IOMUX_FUNC(3)) | flags;
-    IOMUX.PIN[reg_idx] = func | flags;
+    const uint8_t iomux_num = gpio_to_iomux(gpio_number);
+    const uint32_t func = iomux_num > 11 ? 0 : 3;
+    iomux_set_function(iomux_num, func);
+    iomux_set_direction_flags(iomux_num, output_enable ? IOMUX_PIN_OUTPUT_ENABLE : 0);
 }
 
 #ifdef	__cplusplus
diff --git a/core/include/esp/types.h b/core/include/esp/types.h
index 3f0560a..cb816da 100644
--- a/core/include/esp/types.h
+++ b/core/include/esp/types.h
@@ -2,6 +2,7 @@
 #define _ESP_TYPES_H
 
 #include <stdint.h>
+#include <stdbool.h>
 
 typedef volatile uint32_t *esp_reg_t;
 

From 8279b5cfd1cde0bc03d58ff2ee40f868e71b623f Mon Sep 17 00:00:00 2001
From: Alex Stewart <Alexander.Stewart@consensuscorp.com>
Date: Mon, 22 Feb 2016 09:32:12 -0800
Subject: [PATCH 2/2] Added some clarifications to comments in esp/gpio.h

---
 core/include/esp/gpio.h | 34 ++++++++++++++++++++++++++++------
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/core/include/esp/gpio.h b/core/include/esp/gpio.h
index 95b3c31..01fe144 100644
--- a/core/include/esp/gpio.h
+++ b/core/include/esp/gpio.h
@@ -29,6 +29,9 @@ void gpio_enable(const uint8_t gpio_num, const gpio_direction_t direction);
  * 100K ohms (see http://bbs.espressif.com/viewtopic.php?t=1079#p4097)
  * However, measured values suggest that the actual value is likely to be close
  * to 47K in reality.
+ *
+ * NOTE: The enabled_during_sleep setting is currently untested (please send
+ * feedback if you give it a try)
  */
 void gpio_set_pullup(uint8_t gpio_num, bool enabled, bool enabled_during_sleep);
 
@@ -46,6 +49,9 @@ static inline void gpio_disable(const uint8_t gpio_num)
 /* Set whether the specified pin continues to drive its output when the ESP8266
  * goes into sleep mode.  Note that this setting is reset to off whenever
  * gpio_enable is called, so this must be called after calling that function.
+ *
+ * NOTE: This functionality is currently untested (please send feedback if you
+ * give it a try)
  */
 static inline void gpio_set_output_on_sleep(const uint8_t gpio_num, bool enabled)
 {
@@ -58,11 +64,19 @@ static inline void gpio_set_output_on_sleep(const uint8_t gpio_num, bool enabled
 
 /* Set output of a pin high or low.
  *
- * Only works if pin has been set to GPIO_OUTPUT via gpio_enable()
+ * Only works if pin has been set to GPIO_OUTPUT or GPIO_OUT_OPEN_DRAIN via
+ * gpio_enable()
+ *
+ * If the mode is GPIO_OUT_OPEN_DRAIN, setting it low (false) will pull the pin
+ * down to ground, but setting it high (true) will allow it to float.  Note
+ * that even in GPIO_OUT_OPEN_DRAIN mode, the input gates are still physically
+ * connected to the pin, and can be damaged if the voltage is not in either the
+ * "low" or "high" range.  Make sure there is some sort of pull-up resistor on
+ * the line to avoid floating logic lines!
  */
 static inline void gpio_write(const uint8_t gpio_num, const bool set)
 {
-    if(set)
+    if (set)
         GPIO.OUT_SET = BIT(gpio_num);
     else
         GPIO.OUT_CLEAR = BIT(gpio_num);
@@ -70,7 +84,10 @@ static inline void gpio_write(const uint8_t gpio_num, const bool set)
 
 /* Toggle output of a pin
  *
- * Only works if pin has been set to GPIO_OUTPUT via gpio_enable()
+ * Only works if pin has been set to GPIO_OUTPUT or GPIO_OUT_OPEN_DRAIN via
+ * gpio_enable()
+ *
+ * See notes in gpio_write() about GPIO_OUT_OPEN_DRAIN mode.
  */
 static inline void gpio_toggle(const uint8_t gpio_num)
 {
@@ -88,8 +105,12 @@ static inline void gpio_toggle(const uint8_t gpio_num)
 
 /* Read input value of a GPIO pin.
  *
- * If pin is set as an input, this reads the value on the pin.
- * If pin is set as an output, this reads the last value written to the pin.
+ * If pin is set GPIO_INPUT, this reads the level on the pin.
+ * If pin is set GPIO_OUTPUT, this reads the level at which the pin is
+ * currently being driven (i.e. the last value written).
+ * If pin is set GPIO_OUT_OPEN_DRAIN, when the pin is written low, this will
+ * return low (false), when the pin is written high, this will behave like
+ * GPIO_INPUT.
  */
 static inline bool gpio_read(const uint8_t gpio_num)
 {
@@ -100,7 +121,8 @@ extern void gpio_interrupt_handler(void);
 
 /* Set the interrupt type for a given pin
  *
- * If int_type is not GPIO_INTTYPE_NONE, the gpio_interrupt_handler will be attached and unmasked.
+ * If int_type is not GPIO_INTTYPE_NONE, the gpio_interrupt_handler will be
+ * attached and unmasked.
  */
 static inline void gpio_set_interrupt(const uint8_t gpio_num, const gpio_inttype_t int_type)
 {