Initial commit, code based on LPC1224 support and LPC812-MAX CMSIS-DAP interface...
[lpc11u3x] / extdrv / eeprom.c
1 /****************************************************************************
2  *   extdrv/eeprom.c
3  *
4  *
5  * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
6  *
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  *************************************************************************** */
23 #include <stdint.h>
25 #include "core/lpc_regs_11u3x.h"
26 #include "core/lpc_core_cm0.h"
27 #include "core/system.h"
28 #include "core/pio.h"
29 #include "lib/string.h"
30 #include "drivers/gpio.h"
31 #include "drivers/i2c.h"
32 #include "extdrv/eeprom.h"
35 /***************************************************************************** */
36 /*          Read and Write for eeprom                                          */
37 /***************************************************************************** */
39 /* NOTE : This code does automatic detection of the eeprom type/size and thus does not
40  *        support multiple eeproms on the same I2C bus
41  */
43 /* Config */
44 /* Small eeprom : up to 2k bytes. These use a segment address on the lower three bits
45  *   of the address byte, and thus reply on 8 consecutive addresses */
46 #define EEPROM_ID_SMALL_ADDR_1  0xA0
47 #define EEPROM_ID_SMALL_ADDR_2  0xA2
48 #define EEPROM_ID_SMALL_I2C_SIZE  1024
49 #define EEPROM_ID_SMALL_PAGE_SIZE 16
50 /* Big eeprom : from 4k bytes and above : These use two address bytes, and the three
51  *   physical address pins are used to set the chip address. */
52 #define EEPROM_ID_BIG_I2C_SIZE  16*1024
53 #define EEPROM_ID_BIG_PAGE_SIZE 64
56 enum i2c_eeprom_type {
57         EEPROM_TYPE_NONE = 0,
58         EEPROM_TYPE_SMALL,
59         EEPROM_TYPE_BIG,
60 };
62 /* Detect the eeprom size */
63 int eeprom_detect(uint8_t eeprom_addr)
64 {
65         int ret = 0;
66         char cmd_buf[1] = { EEPROM_ID_SMALL_ADDR_1, };
68         /* Look for small eeproms first, only these would answer on all addresses */
69         if (eeprom_addr == EEPROM_ID_SMALL_ADDR_1) {
70                 cmd_buf[0] = EEPROM_ID_SMALL_ADDR_2;
71         }
72         ret = i2c_read(cmd_buf, 1, NULL, NULL, 0);
73         if (ret == 0) {
74                 return EEPROM_TYPE_SMALL;
75         }
77         /* No small eeprom ... look for big ones */
78         cmd_buf[0] = eeprom_addr;
79         ret = i2c_read(cmd_buf, 1, NULL, NULL, 0);
80         if (ret == 0) {
81                 return EEPROM_TYPE_BIG;
82         }
84         if (ret > 0) {
85                 return -EIO;
86         } else if (ret == -EREMOTEIO) {
87                 return EEPROM_TYPE_NONE; /* No module */
88         }
89         return ret; /* Error or module size */
90 }
92 int get_eeprom_type(uint8_t eeprom_addr)
93 {
94         static int eeprom_type = -1;
96         if (eeprom_type >= 0) {
97                 return eeprom_type; /* No need to check again */
98         }
100         eeprom_type = eeprom_detect(eeprom_addr);
101         if (eeprom_type <= 0) {
102                 return -1;
103         }
104         return eeprom_type;
108 /* EEPROM Read
109  * Performs a non-blocking read on the eeprom.
110  *   address : data offset in eeprom.
111  * RETURN VALUE
112  *   Upon successfull completion, returns the number of bytes read. On error, returns a negative
113  *   integer equivalent to errors from glibc.
114  *   -EFAULT : address above eeprom size
115  *   -EBADFD : Device not initialized
116  *   -EBUSY : Device or ressource Busy or Arbitration lost
117  *   -EAGAIN : Device already in use
118  *   -EINVAL : Invalid argument (buf)
119  *   -EREMOTEIO : Device did not acknowledge
120  *   -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
121  */
122 #define CMD_BUF_SIZE 4
123 int eeprom_read(uint8_t eeprom_addr, uint32_t offset, void *buf, size_t count)
125         int ret = 0;
126         char cmd_buf[CMD_BUF_SIZE];
127         char ctrl_buf[CMD_BUF_SIZE] = { I2C_CONT, I2C_CONT, I2C_DO_REPEATED_START, I2C_CONT, };
128         int eeprom_type = 0;
130         eeprom_type = get_eeprom_type(eeprom_addr);
132         /* Read the requested data */
133         switch (eeprom_type) {
134                 case EEPROM_TYPE_SMALL:
135                         cmd_buf[0] = EEPROM_ID_SMALL_ADDR_1 | ((offset & 0x700) >> 7);
136                         cmd_buf[1] = offset & 0xFF;
137                         cmd_buf[2] = EEPROM_ID_SMALL_ADDR_1 | 0x01;
138                         ret = i2c_read(cmd_buf, CMD_BUF_SIZE - 1, ctrl_buf + 1, buf, count);
139                         break;
140                 case EEPROM_TYPE_BIG:
141                         cmd_buf[0] = eeprom_addr;
142                         cmd_buf[1] = ((offset & 0xFF00) >> 8);
143                         cmd_buf[2] = offset & 0xFF;
144                         cmd_buf[3] = (eeprom_addr | 0x01);
145                         ret = i2c_read(cmd_buf, CMD_BUF_SIZE, ctrl_buf, buf, count);
146                         break;
147                 default:
148                         ret = -1;
149                         break;
150         }
152         return ret;
156 /* EEPROM Write
157  * Performs a non-blocking write on the eeprom.
158  *   address : data offset in eeprom.
159  * RETURN VALUE
160  *   Upon successfull completion, returns the number of bytes written. On error, returns a negative
161  *   integer equivalent to errors from glibc.
162  *   -EFAULT : address above eeprom size
163  *   -EBADFD : Device not initialized
164  *   -EBUSY : Device or ressource Busy or Arbitration lost
165  *   -EAGAIN : Device already in use
166  *   -EINVAL : Invalid argument (buf)
167  *   -EREMOTEIO : Device did not acknowledge
168  *   -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
169  */
170 #define CMD_SIZE_SMALL 2
171 #define CMD_SIZE_BIG 3
172 #define MAX_CMD_SIZE CMD_SIZE_BIG
173 #define EEPROM_ID_MAX_PAGE_SIZE EEPROM_ID_BIG_PAGE_SIZE
174 int eeprom_write(uint8_t eeprom_addr, uint32_t offset, const void *buf, size_t count)
176         int ret = 0;
177         uint8_t cmd_size = CMD_SIZE_BIG, page_size = EEPROM_ID_BIG_PAGE_SIZE;
178         int write_count = 0, size = 0;
179         char cmd[MAX_CMD_SIZE];
180         char full_buff[(EEPROM_ID_MAX_PAGE_SIZE + MAX_CMD_SIZE)];
181         int eeprom_type = 0;
183         eeprom_type = get_eeprom_type(eeprom_addr);
185         switch (eeprom_type) {
186                 case EEPROM_TYPE_SMALL:
187                         cmd_size = CMD_SIZE_SMALL;
188                         page_size = EEPROM_ID_SMALL_PAGE_SIZE;
189                         break;
190                 case EEPROM_TYPE_BIG:
191                         /* already configured */
192                         /* cmd_size = CMD_SIZE_BIG; */
193                         /* page_size = EEPROM_ID_BIG_PAGE_SIZE; */
194                         break;
195                 default:
196                         ret = -1;
197                         write_count = count + 1; /* skip the while loop, but return error */
198                         break;
199         }
200         while (write_count < count) {
201                 switch (eeprom_type) {
202                         case EEPROM_TYPE_SMALL:
203                                 cmd[0] = EEPROM_ID_SMALL_ADDR_1 | ((offset & 0x700) >> 7);
204                                 cmd[1] = offset & 0xFF;
205                                 break;
206                         case EEPROM_TYPE_BIG:
207                                 cmd[0] = eeprom_addr;
208                                 cmd[1] = ((offset & 0xFF00) >> 8);
209                                 cmd[2] = offset & 0xFF;
210                                 break;
211                 }
212                 /* make partial first write to allign to page boundaries */
213                 if (offset & (page_size - 1)) {
214                         size = (page_size - (offset & (page_size - 1)));
215                 } else {
216                         size = page_size;
217                 }
218                 if (size > (count - write_count))
219                         size = (count - write_count);
220                 offset += size;
221                 memcpy(full_buff, cmd, cmd_size);
222                 memcpy(full_buff + cmd_size, buf + write_count, size);
223                 ret = i2c_write(full_buff, (cmd_size + size), NULL);
225                 if (ret != (cmd_size + size)) {
226                         break;
227                 }
228                 /* Wait for page write completion : The device does not acknoledge anything during
229                  * page write, perform page writes with no data, until it returns 1 */
230                 do {
231                         ret = i2c_write(full_buff, 1, NULL);
232                 } while (ret != 1);
234                 write_count += size;
235         }
237         if (write_count != count)
238                 return ret;
239         return write_count;