aboutsummaryrefslogtreecommitdiff
path: root/drivers
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
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')
-rw-r--r--drivers/eeprom/eeprom_custom.c-template46
-rw-r--r--drivers/eeprom/eeprom_driver.c73
-rw-r--r--drivers/eeprom/eeprom_driver.h22
-rw-r--r--drivers/eeprom/eeprom_i2c.c120
-rw-r--r--drivers/eeprom/eeprom_i2c.h115
-rw-r--r--drivers/eeprom/eeprom_transient.c52
-rw-r--r--drivers/eeprom/eeprom_transient.h25
7 files changed, 453 insertions, 0 deletions
diff --git a/drivers/eeprom/eeprom_custom.c-template b/drivers/eeprom/eeprom_custom.c-template
new file mode 100644
index 000000000..5f915f7fa
--- /dev/null
+++ b/drivers/eeprom/eeprom_custom.c-template
@@ -0,0 +1,46 @@
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#include "eeprom_driver.h"
21
22void eeprom_driver_init(void) {
23 /* Any initialisation code */
24 }
25
26void eeprom_driver_erase(void) {
27 /* Wipe out the EEPROM, setting values to zero */
28}
29
30void eeprom_read_block(void *buf, const void *addr, size_t len) {
31 /*
32 Read a block of data:
33 buf: target buffer
34 addr: 0-based offset within the EEPROM
35 len: length to read
36 */
37}
38
39void eeprom_write_block(const void *buf, void *addr, size_t len) {
40 /*
41 Write a block of data:
42 buf: target buffer
43 addr: 0-based offset within the EEPROM
44 len: length to write
45 */
46}
diff --git a/drivers/eeprom/eeprom_driver.c b/drivers/eeprom/eeprom_driver.c
new file mode 100644
index 000000000..3835e5e9d
--- /dev/null
+++ b/drivers/eeprom/eeprom_driver.c
@@ -0,0 +1,73 @@
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#include "eeprom_driver.h"
21
22uint8_t eeprom_read_byte(const uint8_t *addr) {
23 uint8_t ret;
24 eeprom_read_block(&ret, addr, 1);
25 return ret;
26}
27
28uint16_t eeprom_read_word(const uint16_t *addr) {
29 uint16_t ret;
30 eeprom_read_block(&ret, addr, 2);
31 return ret;
32}
33
34uint32_t eeprom_read_dword(const uint32_t *addr) {
35 uint32_t ret;
36 eeprom_read_block(&ret, addr, 4);
37 return ret;
38}
39
40void eeprom_write_byte(uint8_t *addr, uint8_t value) { eeprom_write_block(&value, addr, 1); }
41
42void eeprom_write_word(uint16_t *addr, uint16_t value) { eeprom_write_block(&value, addr, 2); }
43
44void eeprom_write_dword(uint32_t *addr, uint32_t value) { eeprom_write_block(&value, addr, 4); }
45
46void eeprom_update_block(const void *buf, void *addr, size_t len) {
47 uint8_t read_buf[len];
48 eeprom_read_block(read_buf, addr, len);
49 if (memcmp(buf, read_buf, len) != 0) {
50 eeprom_write_block(buf, addr, len);
51 }
52}
53
54void eeprom_update_byte(uint8_t *addr, uint8_t value) {
55 uint8_t orig = eeprom_read_byte(addr);
56 if (orig != value) {
57 eeprom_write_byte(addr, value);
58 }
59}
60
61void eeprom_update_word(uint16_t *addr, uint16_t value) {
62 uint16_t orig = eeprom_read_word(addr);
63 if (orig != value) {
64 eeprom_write_word(addr, value);
65 }
66}
67
68void eeprom_update_dword(uint32_t *addr, uint32_t value) {
69 uint32_t orig = eeprom_read_dword(addr);
70 if (orig != value) {
71 eeprom_write_dword(addr, value);
72 }
73}
diff --git a/drivers/eeprom/eeprom_driver.h b/drivers/eeprom/eeprom_driver.h
new file mode 100644
index 000000000..74592bc8f
--- /dev/null
+++ b/drivers/eeprom/eeprom_driver.h
@@ -0,0 +1,22 @@
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#pragma once
18
19#include "eeprom.h"
20
21void eeprom_driver_init(void);
22void eeprom_driver_erase(void);
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}
diff --git a/drivers/eeprom/eeprom_i2c.h b/drivers/eeprom/eeprom_i2c.h
new file mode 100644
index 000000000..51bce825b
--- /dev/null
+++ b/drivers/eeprom/eeprom_i2c.h
@@ -0,0 +1,115 @@
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#pragma once
18
19/*
20 Default device configurations:
21
22 For the Sparkfun Qwiic I2C EEPROM module: https://www.sparkfun.com/products/14764
23 #define EEPROM_I2C_CAT24C512 // (part number 24512A)
24 #define EEPROM_I2C_RM24C512C // (part number 24512C)
25
26 For the Sparkfun I2C EEPROM chip: https://www.sparkfun.com/products/525
27 #define EEPROM_I2C_24LC256
28
29 For the Adafruit I2C FRAM chip: https://www.adafruit.com/product/1895
30 #define EEPROM_I2C_MB85RC256V
31*/
32#if defined(EEPROM_I2C_CAT24C512)
33# define EXTERNAL_EEPROM_BYTE_COUNT 65536
34# define EXTERNAL_EEPROM_PAGE_SIZE 128
35# define EXTERNAL_EEPROM_ADDRESS_SIZE 2
36# define EXTERNAL_EEPROM_WRITE_TIME 5
37#elif defined(EEPROM_I2C_RM24C512C)
38# define EXTERNAL_EEPROM_BYTE_COUNT 65536
39# define EXTERNAL_EEPROM_PAGE_SIZE 128
40# define EXTERNAL_EEPROM_ADDRESS_SIZE 2
41# define EXTERNAL_EEPROM_WRITE_TIME 3
42#elif defined(EEPROM_I2C_24LC256)
43# define EXTERNAL_EEPROM_BYTE_COUNT 32768
44# define EXTERNAL_EEPROM_PAGE_SIZE 64
45# define EXTERNAL_EEPROM_ADDRESS_SIZE 2
46# define EXTERNAL_EEPROM_WRITE_TIME 5
47#elif defined(EEPROM_I2C_24LC128)
48# define EXTERNAL_EEPROM_BYTE_COUNT 16384
49# define EXTERNAL_EEPROM_PAGE_SIZE 64
50# define EXTERNAL_EEPROM_ADDRESS_SIZE 2
51# define EXTERNAL_EEPROM_WRITE_TIME 5
52#elif defined(EEPROM_I2C_MB85RC256V)
53# define EXTERNAL_EEPROM_BYTE_COUNT 32768
54# define EXTERNAL_EEPROM_PAGE_SIZE 128
55# define EXTERNAL_EEPROM_ADDRESS_SIZE 2
56# define EXTERNAL_EEPROM_WRITE_TIME 0
57#endif
58
59/*
60 The base I2C address of the EEPROM.
61 This needs to be shifted up by 1, to match i2c_master requirements.
62*/
63#ifndef EXTERNAL_EEPROM_I2C_BASE_ADDRESS
64# define EXTERNAL_EEPROM_I2C_BASE_ADDRESS 0b10100000
65#endif
66
67/*
68 The calculated I2C address based on the input memory location.
69
70 For EEPROM chips that embed part of the memory location in the I2C address
71 such as AT24M02 you can use something similar to the following (ensuring the
72 result is shifted by left by 1):
73
74 #define EXTERNAL_EEPROM_I2C_ADDRESS(loc) \
75 (EXTERNAL_EEPROM_I2C_BASE_ADDRESS | ((((loc) >> 16) & 0x07) << 1))
76
77*/
78#ifndef EXTERNAL_EEPROM_I2C_ADDRESS
79# define EXTERNAL_EEPROM_I2C_ADDRESS(loc) (EXTERNAL_EEPROM_I2C_BASE_ADDRESS)
80#endif
81
82/*
83 The total size of the EEPROM, in bytes. The EEPROM datasheet will usually
84 specify this value in kbits, and will require conversion to bytes.
85*/
86#ifndef EXTERNAL_EEPROM_BYTE_COUNT
87# define EXTERNAL_EEPROM_BYTE_COUNT 8192
88#endif
89
90/*
91 The page size in bytes of the EEPROM, as specified in the datasheet.
92*/
93#ifndef EXTERNAL_EEPROM_PAGE_SIZE
94# define EXTERNAL_EEPROM_PAGE_SIZE 32
95#endif
96
97/*
98 The address size in bytes of the EEPROM. For EEPROMs with <=256 bytes, this
99 will likely be 1. For EEPROMs >256 and <=65536, this will be 2. For EEPROMs
100 >65536, this will likely need to be 2 with the modified variant of
101 EXTERNAL_EEPROM_I2C_ADDRESS above.
102
103 As expected, consult the datasheet for specifics of your EEPROM.
104*/
105#ifndef EXTERNAL_EEPROM_ADDRESS_SIZE
106# define EXTERNAL_EEPROM_ADDRESS_SIZE 2
107#endif
108
109/*
110 The write cycle time of the EEPROM in milliseconds, as specified in the
111 datasheet.
112*/
113#ifndef EXTERNAL_EEPROM_WRITE_TIME
114# define EXTERNAL_EEPROM_WRITE_TIME 5
115#endif
diff --git a/drivers/eeprom/eeprom_transient.c b/drivers/eeprom/eeprom_transient.c
new file mode 100644
index 000000000..b4c78c6f4
--- /dev/null
+++ b/drivers/eeprom/eeprom_transient.c
@@ -0,0 +1,52 @@
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#include "eeprom_driver.h"
21#include "eeprom_transient.h"
22
23__attribute__((aligned(4))) static uint8_t transientBuffer[TRANSIENT_EEPROM_SIZE] = {0};
24
25size_t clamp_length(intptr_t offset, size_t len) {
26 if (offset + len > TRANSIENT_EEPROM_SIZE) {
27 len = TRANSIENT_EEPROM_SIZE - offset;
28 }
29
30 return len;
31}
32
33void eeprom_driver_init(void) { eeprom_driver_erase(); }
34
35void eeprom_driver_erase(void) { memset(transientBuffer, 0x00, TRANSIENT_EEPROM_SIZE); }
36
37void eeprom_read_block(void *buf, const void *addr, size_t len) {
38 intptr_t offset = (intptr_t)addr;
39 memset(buf, 0x00, len);
40 len = clamp_length(offset, len);
41 if (len > 0) {
42 memcpy(buf, &transientBuffer[offset], len);
43 }
44}
45
46void eeprom_write_block(const void *buf, void *addr, size_t len) {
47 intptr_t offset = (intptr_t)addr;
48 len = clamp_length(offset, len);
49 if (len > 0) {
50 memcpy(&transientBuffer[offset], buf, len);
51 }
52}
diff --git a/drivers/eeprom/eeprom_transient.h b/drivers/eeprom/eeprom_transient.h
new file mode 100644
index 000000000..ca9d634f0
--- /dev/null
+++ b/drivers/eeprom/eeprom_transient.h
@@ -0,0 +1,25 @@
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#pragma once
18
19/*
20 The size of the transient EEPROM buffer size.
21*/
22#ifndef TRANSIENT_EEPROM_SIZE
23# include "eeconfig.h"
24# define TRANSIENT_EEPROM_SIZE (((EECONFIG_SIZE+3)/4)*4) // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO
25#endif