From dd16d3cc7e427c5256f5d2089d0cc2b6bbc776e1 Mon Sep 17 00:00:00 2001
From: Purdea Andrei <andrei@purdea.ro>
Date: Tue, 22 Jun 2021 05:34:28 +0300
Subject: eeprom_i2c driver: added EXTERNAL_EEPROM_WP_PIN configuration option.
 (#12617)

---
 docs/eeprom_driver.md       |  3 +++
 drivers/eeprom/eeprom_i2c.c | 23 ++++++++++++++++++++++-
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/docs/eeprom_driver.md b/docs/eeprom_driver.md
index e2c262546d..6dcf10c04d 100644
--- a/docs/eeprom_driver.md
+++ b/docs/eeprom_driver.md
@@ -31,6 +31,9 @@ Currently QMK supports 24xx-series chips over I2C. As such, requires a working i
 `#define EXTERNAL_EEPROM_PAGE_SIZE`         | Page size of the EEPROM in bytes, as specified in the datasheet                     | 32
 `#define EXTERNAL_EEPROM_ADDRESS_SIZE`      | The number of bytes to transmit for the memory location within the EEPROM           | 2
 `#define EXTERNAL_EEPROM_WRITE_TIME`        | Write cycle time of the EEPROM, as specified in the datasheet                       | 5
+`#define EXTERNAL_EEPROM_WP_PIN`            | If defined the WP pin will be toggled appropriately when writing to the EEPROM.     | _none_
+
+Some I2C EEPROM manufacturers explicitly recommend against hardcoding the WP pin to ground. This is in order to protect the eeprom memory content during power-up/power-down/brown-out conditions at low voltage where the eeprom is still operational, but the i2c master output might be unpredictable. If a WP pin is configured, then having an external pull-up on the WP pin is recommended.
 
 Default values and extended descriptions can be found in `drivers/eeprom/eeprom_i2c.h`.
 
diff --git a/drivers/eeprom/eeprom_i2c.c b/drivers/eeprom/eeprom_i2c.c
index 4210f06f9f..8e80ff544f 100644
--- a/drivers/eeprom/eeprom_i2c.c
+++ b/drivers/eeprom/eeprom_i2c.c
@@ -16,6 +16,9 @@
 
 #include <stdint.h>
 #include <string.h>
+#if defined(EXTERNAL_EEPROM_WP_PIN)
+#    include "gpio.h"
+#endif
 
 /*
     Note that the implementations of eeprom_XXXX_YYYY on AVR are normally
@@ -50,7 +53,14 @@ static inline void fill_target_address(uint8_t *buffer, const void *addr) {
     }
 }
 
-void eeprom_driver_init(void) { i2c_init(); }
+void eeprom_driver_init(void) {
+    i2c_init();
+#if defined(EXTERNAL_EEPROM_WP_PIN)
+    /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
+    writePin(EXTERNAL_EEPROM_WP_PIN, 1);
+    setPinInputHigh(EXTERNAL_EEPROM_WP_PIN);
+#endif
+}
 
 void eeprom_driver_erase(void) {
 #if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)
@@ -89,6 +99,11 @@ void eeprom_write_block(const void *buf, void *addr, size_t len) {
     uint8_t * read_buf    = (uint8_t *)buf;
     uintptr_t target_addr = (uintptr_t)addr;
 
+#if defined(EXTERNAL_EEPROM_WP_PIN)
+    setPinOutput(EXTERNAL_EEPROM_WP_PIN);
+    writePin(EXTERNAL_EEPROM_WP_PIN, 0);
+#endif
+
     while (len > 0) {
         uintptr_t page_offset  = target_addr % EXTERNAL_EEPROM_PAGE_SIZE;
         int       write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset;
@@ -116,4 +131,10 @@ void eeprom_write_block(const void *buf, void *addr, size_t len) {
         target_addr += write_length;
         len -= write_length;
     }
+
+#if defined(EXTERNAL_EEPROM_WP_PIN)
+    /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
+    writePin(EXTERNAL_EEPROM_WP_PIN, 1);
+    setPinInputHigh(EXTERNAL_EEPROM_WP_PIN);
+#endif
 }
-- 
cgit v1.2.3