aboutsummaryrefslogtreecommitdiff
path: root/drivers/eeprom/eeprom_i2c.c
diff options
context:
space:
mode:
authorNick Brassel <nick@tzarc.org>2019-11-06 08:04:50 +1100
committerNick Brassel <nick@tzarc.org>2020-01-24 12:45:58 +1100
commitd13ada11622977bcc0b530212b4405229805016d (patch)
tree3f8874ac3c9b5950b1fed6ac4d0081a268d9f487 /drivers/eeprom/eeprom_i2c.c
parent6ff093efbee21d3f64f5b4bfdbc66d4648490523 (diff)
downloadqmk_firmware-d13ada11622977bcc0b530212b4405229805016d.tar.gz
qmk_firmware-d13ada11622977bcc0b530212b4405229805016d.zip
Add customisable EEPROM driver selection (#7274)
- uprintf -> dprintf - Fix atsam "vendor" eeprom. - Bump Kinetis K20x to 64 bytes, too. - Rollback Kinetis to 32 bytes as partitioning can only be done once. Add warning about changing the value. - Change RAM-backed "fake" EEPROM implementations to match eeconfig's current usage. - Add 24LC128 by request.
Diffstat (limited to 'drivers/eeprom/eeprom_i2c.c')
-rw-r--r--drivers/eeprom/eeprom_i2c.c120
1 files changed, 120 insertions, 0 deletions
diff --git a/drivers/eeprom/eeprom_i2c.c b/drivers/eeprom/eeprom_i2c.c
new file mode 100644
index 000000000..03dbc5e51
--- /dev/null
+++ b/drivers/eeprom/eeprom_i2c.c
@@ -0,0 +1,120 @@
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
20/*
21 Note that the implementations of eeprom_XXXX_YYYY on AVR are normally
22 provided by avr-libc. The same functions are reimplemented below and are
23 rerouted to the external i2c equivalent.
24
25 Seemingly, as this is compiled from within QMK, the object file generated
26 during the build overrides the avr-libc implementation during the linking
27 stage.
28
29 On other platforms such as ARM, there are no provided implementations, so
30 there is nothing to override during linkage.
31*/
32
33#include "wait.h"
34#include "i2c_master.h"
35#include "eeprom.h"
36#include "eeprom_i2c.h"
37
38// #define DEBUG_EEPROM_OUTPUT
39
40#ifdef DEBUG_EEPROM_OUTPUT
41# include "print.h"
42#endif // DEBUG_EEPROM_OUTPUT
43
44static inline void init_i2c_if_required(void) {
45 static int done = 0;
46 if (!done) {
47 i2c_init();
48 done = 1;
49 }
50}
51
52static inline void fill_target_address(uint8_t *buffer, const void *addr) {
53 intptr_t p = (intptr_t)addr;
54 for (int i = 0; i < EXTERNAL_EEPROM_ADDRESS_SIZE; ++i) {
55 buffer[EXTERNAL_EEPROM_ADDRESS_SIZE - 1 - i] = p & 0xFF;
56 p >>= 8;
57 }
58}
59
60void eeprom_driver_init(void) {}
61
62void eeprom_driver_erase(void) {
63 uint8_t buf[EXTERNAL_EEPROM_PAGE_SIZE];
64 memset(buf, 0x00, EXTERNAL_EEPROM_PAGE_SIZE);
65 for (intptr_t addr = 0; addr < EXTERNAL_EEPROM_BYTE_COUNT; addr += EXTERNAL_EEPROM_PAGE_SIZE) {
66 eeprom_write_block(buf, (void *)addr, EXTERNAL_EEPROM_PAGE_SIZE);
67 }
68}
69
70void eeprom_read_block(void *buf, const void *addr, size_t len) {
71 uint8_t complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE];
72 fill_target_address(complete_packet, addr);
73
74 init_i2c_if_required();
75 i2c_transmit(EXTERNAL_EEPROM_I2C_ADDRESS((intptr_t)addr), complete_packet, EXTERNAL_EEPROM_ADDRESS_SIZE, 100);
76 i2c_receive(EXTERNAL_EEPROM_I2C_ADDRESS((intptr_t)addr), buf, len, 100);
77
78#ifdef DEBUG_EEPROM_OUTPUT
79 dprintf("[EEPROM R] 0x%04X: ", ((int)addr));
80 for (size_t i = 0; i < len; ++i) {
81 dprintf(" %02X", (int)(((uint8_t *)buf)[i]));
82 }
83 dprintf("\n");
84#endif // DEBUG_EEPROM_OUTPUT
85}
86
87void eeprom_write_block(const void *buf, void *addr, size_t len) {
88 uint8_t complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE + EXTERNAL_EEPROM_PAGE_SIZE];
89 uint8_t *read_buf = (uint8_t *)buf;
90 intptr_t target_addr = (intptr_t)addr;
91
92 init_i2c_if_required();
93 while (len > 0) {
94 intptr_t page_offset = target_addr % EXTERNAL_EEPROM_PAGE_SIZE;
95 int write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset;
96 if (write_length > len) {
97 write_length = len;
98 }
99
100 fill_target_address(complete_packet, (const void *)target_addr);
101 for (uint8_t i = 0; i < write_length; i++) {
102 complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE + i] = read_buf[i];
103 }
104
105#ifdef DEBUG_EEPROM_OUTPUT
106 dprintf("[EEPROM W] 0x%04X: ", ((int)target_addr));
107 for (uint8_t i = 0; i < write_length; i++) {
108 dprintf(" %02X", (int)(read_buf[i]));
109 }
110 dprintf("\n");
111#endif // DEBUG_EEPROM_OUTPUT
112
113 i2c_transmit(EXTERNAL_EEPROM_I2C_ADDRESS((intptr_t)addr), complete_packet, EXTERNAL_EEPROM_ADDRESS_SIZE + write_length, 100);
114 wait_ms(EXTERNAL_EEPROM_WRITE_TIME);
115
116 read_buf += write_length;
117 target_addr += write_length;
118 len -= write_length;
119 }
120}