qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

eeprom_i2c.c (5194B)


      1 /* Copyright 2019 Nick Brassel (tzarc)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 2 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15  */
     16 
     17 #include <stdint.h>
     18 #include <string.h>
     19 #if defined(EXTERNAL_EEPROM_WP_PIN)
     20 #    include "gpio.h"
     21 #endif
     22 
     23 /*
     24     Note that the implementations of eeprom_XXXX_YYYY on AVR are normally
     25     provided by avr-libc. The same functions are reimplemented below and are
     26     rerouted to the external i2c equivalent.
     27 
     28     Seemingly, as this is compiled from within QMK, the object file generated
     29     during the build overrides the avr-libc implementation during the linking
     30     stage.
     31 
     32     On other platforms such as ARM, there are no provided implementations, so
     33     there is nothing to override during linkage.
     34 */
     35 
     36 #include "wait.h"
     37 #include "i2c_master.h"
     38 #include "eeprom.h"
     39 #include "eeprom_driver.h"
     40 #include "eeprom_i2c.h"
     41 
     42 // #define DEBUG_EEPROM_OUTPUT
     43 
     44 #if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)
     45 #    include "timer.h"
     46 #    include "debug.h"
     47 #endif // DEBUG_EEPROM_OUTPUT
     48 
     49 static inline void fill_target_address(uint8_t *buffer, const void *addr) {
     50     uintptr_t p = (uintptr_t)addr;
     51     for (int i = 0; i < EXTERNAL_EEPROM_ADDRESS_SIZE; ++i) {
     52         buffer[EXTERNAL_EEPROM_ADDRESS_SIZE - 1 - i] = p & 0xFF;
     53         p >>= 8;
     54     }
     55 }
     56 
     57 void eeprom_driver_init(void) {
     58     i2c_init();
     59 #if defined(EXTERNAL_EEPROM_WP_PIN)
     60     /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
     61     gpio_write_pin(EXTERNAL_EEPROM_WP_PIN, 1);
     62     gpio_set_pin_input_high(EXTERNAL_EEPROM_WP_PIN);
     63 #endif
     64 }
     65 
     66 void eeprom_driver_format(bool erase) {
     67     /* i2c eeproms do not need to be formatted before use */
     68     if (erase) {
     69         eeprom_driver_erase();
     70     }
     71 }
     72 
     73 void eeprom_driver_erase(void) {
     74 #if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)
     75     uint32_t start = timer_read32();
     76 #endif
     77 
     78     uint8_t buf[EXTERNAL_EEPROM_PAGE_SIZE];
     79     memset(buf, 0x00, EXTERNAL_EEPROM_PAGE_SIZE);
     80     for (uint32_t addr = 0; addr < EXTERNAL_EEPROM_BYTE_COUNT; addr += EXTERNAL_EEPROM_PAGE_SIZE) {
     81         eeprom_write_block(buf, (void *)(uintptr_t)addr, EXTERNAL_EEPROM_PAGE_SIZE);
     82     }
     83 
     84 #if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)
     85     dprintf("EEPROM erase took %ldms to complete\n", ((long)(timer_read32() - start)));
     86 #endif
     87 }
     88 
     89 void eeprom_read_block(void *buf, const void *addr, size_t len) {
     90     uint8_t complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE];
     91     fill_target_address(complete_packet, addr);
     92 
     93     i2c_transmit(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), complete_packet, EXTERNAL_EEPROM_ADDRESS_SIZE, 100);
     94     i2c_receive(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), buf, len, 100);
     95 
     96 #if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)
     97     dprintf("[EEPROM R] 0x%04X: ", ((int)addr));
     98     for (size_t i = 0; i < len; ++i) {
     99         dprintf(" %02X", (int)(((uint8_t *)buf)[i]));
    100     }
    101     dprintf("\n");
    102 #endif // DEBUG_EEPROM_OUTPUT
    103 }
    104 
    105 void eeprom_write_block(const void *buf, void *addr, size_t len) {
    106     uint8_t   complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE + EXTERNAL_EEPROM_PAGE_SIZE];
    107     uint8_t  *read_buf    = (uint8_t *)buf;
    108     uintptr_t target_addr = (uintptr_t)addr;
    109 
    110 #if defined(EXTERNAL_EEPROM_WP_PIN)
    111     gpio_set_pin_output(EXTERNAL_EEPROM_WP_PIN);
    112     gpio_write_pin(EXTERNAL_EEPROM_WP_PIN, 0);
    113 #endif
    114 
    115     while (len > 0) {
    116         uintptr_t page_offset  = target_addr % EXTERNAL_EEPROM_PAGE_SIZE;
    117         int       write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset;
    118         if (write_length > len) {
    119             write_length = len;
    120         }
    121 
    122         fill_target_address(complete_packet, (const void *)target_addr);
    123         for (uint8_t i = 0; i < write_length; i++) {
    124             complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE + i] = read_buf[i];
    125         }
    126 
    127 #if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)
    128         dprintf("[EEPROM W] 0x%04X: ", ((int)target_addr));
    129         for (uint8_t i = 0; i < write_length; i++) {
    130             dprintf(" %02X", (int)(read_buf[i]));
    131         }
    132         dprintf("\n");
    133 #endif // DEBUG_EEPROM_OUTPUT
    134 
    135         i2c_transmit(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), complete_packet, EXTERNAL_EEPROM_ADDRESS_SIZE + write_length, 100);
    136         wait_ms(EXTERNAL_EEPROM_WRITE_TIME);
    137 
    138         read_buf += write_length;
    139         target_addr += write_length;
    140         len -= write_length;
    141     }
    142 
    143 #if defined(EXTERNAL_EEPROM_WP_PIN)
    144     /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
    145     gpio_write_pin(EXTERNAL_EEPROM_WP_PIN, 1);
    146     gpio_set_pin_input_high(EXTERNAL_EEPROM_WP_PIN);
    147 #endif
    148 }