]>
Commit | Line | Data |
---|---|---|
35edb766 L |
1 | /* |
2 | * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de> | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
61b0cfe9 | 6 | |
04a63b0d L |
7 | /* |
8 | * I2C (TWI) master interface. | |
61b0cfe9 L |
9 | */ |
10 | ||
11 | #include "common.h" | |
12 | #include <avr/interrupt.h> | |
61b0cfe9 L |
13 | #include <string.h> |
14 | ||
15 | #include "config.h" | |
16 | #include "timer.h" | |
17 | #include "debug.h" | |
18 | #include "i2c.h" | |
19 | ||
f14850db L |
20 | #define DEBUG_I2C 0 |
21 | ||
22 | #define debug_i2c(fmt, args...) \ | |
23 | debug_cond(DEBUG_I2C, fmt, ##args) | |
24 | ||
61b0cfe9 L |
25 | |
26 | /* General TWI Master status codes */ | |
27 | #define TWI_START 0x08 /* START has been transmitted */ | |
28 | #define TWI_REP_START 0x10 /* Repeated START has been transmitted */ | |
29 | #define TWI_ARB_LOST 0x38 /* Arbitration lost */ | |
30 | ||
31 | /* TWI Master Transmitter status codes */ | |
32 | #define TWI_MTX_ADR_ACK 0x18 /* SLA+W has been transmitted and ACK received */ | |
33 | #define TWI_MTX_ADR_NACK 0x20 /* SLA+W has been transmitted and NACK received */ | |
34 | #define TWI_MTX_DATA_ACK 0x28 /* Data byte has been transmitted and ACK received */ | |
35 | #define TWI_MTX_DATA_NACK 0x30 /* Data byte has been transmitted and NACK received */ | |
36 | ||
37 | /* TWI Master Receiver status codes */ | |
38 | #define TWI_MRX_ADR_ACK 0x40 /* SLA+R has been transmitted and ACK received */ | |
39 | #define TWI_MRX_ADR_NACK 0x48 /* SLA+R has been transmitted and NACK received */ | |
40 | #define TWI_MRX_DATA_ACK 0x50 /* Data byte has been received and ACK transmitted */ | |
41 | #define TWI_MRX_DATA_NACK 0x58 /* Data byte has been received and NACK transmitted */ | |
42 | ||
43 | /* TWI Miscellaneous status codes */ | |
44 | #define TWI_NO_STATE 0xF8 /* No relevant state information available */ | |
45 | #define TWI_BUS_ERROR 0x00 /* Bus error due to an illegal START or STOP condition */ | |
46 | ||
47 | ||
48 | /* | |
49 | * TWINT: TWI Interrupt Flag | |
50 | * TWEA: TWI Enable Acknowledge Bit | |
51 | * TWSTA: TWI START Condition Bit | |
52 | * TWSTO: TWI STOP Condition Bit | |
53 | * TWEN: TWI Enable Bit | |
54 | * TWIE: TWI Interrupt Enable | |
04a63b0d | 55 | * |
61b0cfe9 L |
56 | * (1<<TWEN)|(1<<TWIE)|(1<<TWINT) |
57 | * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)| (1<<TWEA) | |
58 | * (1<<TWEN)|(1<<TWIE)|(1<<TWINT) | |
04a63b0d | 59 | * |
61b0cfe9 L |
60 | * default: |
61 | * (1<<TWEN)| (1<<TWINT)| (1<<TWSTO) | |
04a63b0d | 62 | * |
61b0cfe9 L |
63 | * Init: |
64 | * (1<<TWEN) | |
04a63b0d | 65 | * |
61b0cfe9 L |
66 | * start read/write: |
67 | * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA) | |
68 | * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA) | |
69 | * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA) | |
70 | * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA) | |
04a63b0d | 71 | * |
61b0cfe9 L |
72 | * wait ready: |
73 | * (1<<TWIE)|(1<<TWSTO) | |
74 | * | |
75 | * | |
76 | * | |
04a63b0d | 77 | *i2c_result |
61b0cfe9 L |
78 | * |
79 | * 0b10000000 Busy (Transmission in progress) | |
80 | * 0b01000000 Timeout | |
04a63b0d | 81 | * 0b00001000 Start transmitted |
61b0cfe9 L |
82 | * 0b00000100 Slave acknowledged address |
83 | * 0b00000010 Data byte(s) transmitted/received | |
84 | * 0b00000001 Transmission completed | |
85 | * | |
86 | * | |
87 | *---------------------------------------------------------------------- | |
88 | */ | |
89 | ||
90 | #define TWI_C_DISABLE 0x00 | |
91 | #define TWI_C_ENABLE (1<<TWEN) | |
92 | ||
04a63b0d L |
93 | |
94 | ||
61b0cfe9 L |
95 | typedef struct i2c_msg_s { |
96 | uint8_t stat; | |
97 | #define XMIT_DONE (1<<0) | |
98 | #define DATA_ACK (1<<1) | |
99 | #define ADDR_ACK (1<<2) | |
100 | #define START (1<<3) | |
101 | #define TIMEOUT (1<<6) | |
102 | #define BUSY (1<<7) | |
103 | uint8_t idx; | |
104 | uint8_t len; | |
105 | uint8_t buf[CONFIG_SYS_I2C_BUFSIZE]; | |
106 | } i2c_msg_t; | |
107 | ||
35e9ec0c | 108 | static volatile i2c_msg_t xmit; |
61b0cfe9 L |
109 | |
110 | ISR(TWI_vect) | |
111 | { | |
35e9ec0c | 112 | uint8_t tmp_stat; |
61b0cfe9 | 113 | uint8_t tmp_idx; |
35e9ec0c L |
114 | uint8_t next_twcr; |
115 | uint8_t n; | |
116 | ||
117 | tmp_idx = xmit.idx; | |
118 | tmp_stat = xmit.stat; | |
61b0cfe9 L |
119 | |
120 | uint8_t twsr = TWSR; | |
04a63b0d | 121 | |
61b0cfe9 L |
122 | switch (twsr & 0xf8) { |
123 | ||
124 | case TWI_START: | |
125 | case TWI_REP_START: | |
35e9ec0c L |
126 | tmp_stat = BUSY | START; |
127 | tmp_idx = 0; /* reset xmit_buf index */ | |
61b0cfe9 | 128 | |
61b0cfe9 L |
129 | if (tmp_idx < xmit.len) { /* all bytes transmited? */ |
130 | TWDR = xmit.buf[tmp_idx]; | |
35e9ec0c | 131 | ++tmp_idx; |
61b0cfe9 L |
132 | next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT); |
133 | } else { | |
35e9ec0c L |
134 | tmp_stat |= XMIT_DONE; |
135 | tmp_stat &= ~BUSY; | |
61b0cfe9 L |
136 | next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO); |
137 | } | |
138 | break; | |
139 | ||
140 | case TWI_MTX_ADR_ACK: | |
61b0cfe9 | 141 | case TWI_MTX_DATA_ACK: |
85787726 L |
142 | if ((twsr&0xf8) == TWI_MTX_ADR_ACK) |
143 | tmp_stat |= ADDR_ACK; | |
144 | else | |
145 | tmp_stat |= DATA_ACK; | |
61b0cfe9 | 146 | |
61b0cfe9 L |
147 | if (tmp_idx < xmit.len) { /* all bytes transmited? */ |
148 | TWDR = xmit.buf[tmp_idx]; | |
35e9ec0c | 149 | ++tmp_idx; |
61b0cfe9 L |
150 | next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT); |
151 | } else { | |
35e9ec0c L |
152 | tmp_stat |= XMIT_DONE; |
153 | tmp_stat &= ~BUSY; | |
61b0cfe9 L |
154 | next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO); |
155 | } | |
61b0cfe9 L |
156 | break; |
157 | ||
158 | case TWI_MTX_DATA_NACK: | |
35e9ec0c L |
159 | tmp_stat |= XMIT_DONE; |
160 | tmp_stat &= ~BUSY; | |
61b0cfe9 L |
161 | next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO); |
162 | break; | |
163 | ||
61b0cfe9 | 164 | case TWI_MRX_DATA_ACK: |
61b0cfe9 | 165 | xmit.buf[tmp_idx] = TWDR; |
35e9ec0c | 166 | ++tmp_idx; |
85787726 L |
167 | /* fall thru */ |
168 | case TWI_MRX_ADR_ACK: | |
169 | if ((twsr&0xf8) == TWI_MRX_ADR_ACK) | |
170 | tmp_stat |= ADDR_ACK; | |
171 | else | |
172 | tmp_stat |= DATA_ACK; | |
173 | ||
35e9ec0c L |
174 | n = xmit.len-1; |
175 | if (tmp_idx < n) { | |
61b0cfe9 L |
176 | next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA); |
177 | } else { | |
61b0cfe9 L |
178 | next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT); |
179 | } | |
180 | break; | |
181 | ||
182 | case TWI_MRX_DATA_NACK: | |
35e9ec0c | 183 | tmp_stat |= ADDR_ACK | DATA_ACK; |
61b0cfe9 | 184 | |
61b0cfe9 | 185 | xmit.buf[tmp_idx] = TWDR; |
35e9ec0c | 186 | ++tmp_idx; |
61b0cfe9 | 187 | /* fall thru */ |
61b0cfe9 | 188 | default: |
35e9ec0c | 189 | tmp_stat &= ~BUSY; |
61b0cfe9 L |
190 | next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO); |
191 | break; | |
192 | } | |
193 | ||
35e9ec0c L |
194 | xmit.stat = tmp_stat; |
195 | xmit.idx = tmp_idx; | |
196 | ||
f14850db | 197 | debug_i2c("|%02x", twsr); |
61b0cfe9 L |
198 | TWCR = next_twcr; |
199 | } | |
200 | ||
201 | ||
202 | /*------------------------------------------------------------------*/ | |
203 | ||
204 | static uint8_t twps; | |
205 | static uint8_t twbr; | |
206 | ||
207 | ||
208 | static void _init(void) | |
209 | { | |
210 | xmit.stat = 0; | |
61b0cfe9 L |
211 | |
212 | /* Disable TWI, disable TWI interrupt. */ | |
213 | /* (Reset TWI hardware state machine.) */ | |
214 | TWCR = TWI_C_DISABLE; | |
215 | _delay_us(5); | |
f14850db | 216 | #if DEBUG_I2C |
35e9ec0c L |
217 | memset((void *) xmit.buf, 0xdf, sizeof(xmit.buf)); |
218 | #endif | |
61b0cfe9 | 219 | |
61b0cfe9 | 220 | TWDR = 0xff; |
35e9ec0c L |
221 | TWBR = twbr; |
222 | TWSR = twps & 0x03; | |
223 | TWCR = TWI_C_ENABLE; | |
61b0cfe9 L |
224 | } |
225 | ||
226 | void i2c_init(uint32_t speed) | |
227 | { | |
228 | twps = 0; | |
35e9ec0c | 229 | uint32_t tmp_twbr = F_CPU /2 / speed - 8; |
04a63b0d | 230 | |
35e9ec0c L |
231 | while (tmp_twbr > 255) { |
232 | tmp_twbr >>= 4; | |
61b0cfe9 L |
233 | twps += 1; |
234 | } | |
35e9ec0c L |
235 | debug_cond((twps > 3), "*** TWCLK too low: %lu Hz\n", speed); |
236 | ||
237 | twbr = (uint8_t) tmp_twbr; | |
04a63b0d L |
238 | |
239 | PRR0 &= ~_BV(PRTWI); | |
61b0cfe9 L |
240 | _init(); |
241 | } | |
242 | ||
243 | ||
244 | int_fast8_t i2c_waitready(void) | |
245 | { | |
246 | uint32_t timer = get_timer(0); | |
247 | uint8_t timeout = 0; | |
04a63b0d | 248 | |
61b0cfe9 L |
249 | do { |
250 | if (get_timer(timer) >= 30) { | |
251 | timeout = TIMEOUT; | |
252 | _init(); | |
253 | } | |
254 | } while ((TWCR & ((1<<TWIE)|(1<<TWSTO))) != 0 && !timeout); | |
255 | ||
256 | xmit.stat |= timeout; | |
257 | ||
f14850db | 258 | #if DEBUG_I2C |
61b0cfe9 L |
259 | dump_ram((uint8_t *) &xmit, 4, "=== i2c_wait ready: (done)"); |
260 | _delay_ms(30); | |
04a63b0d | 261 | #endif |
61b0cfe9 L |
262 | return xmit.stat; |
263 | } | |
264 | ||
f14850db | 265 | static |
61b0cfe9 L |
266 | int i2c_send(uint8_t chip, uint16_t addr, uint8_t alen, uint8_t *buffer, int8_t len) |
267 | { | |
268 | uint8_t i, n; | |
269 | uint8_t rc; | |
270 | ||
271 | rc = i2c_waitready(); | |
272 | if ((rc & (BUSY | TIMEOUT)) != 0) | |
273 | return rc; | |
274 | ||
275 | xmit.stat = BUSY; | |
276 | xmit.buf[0] = chip<<1; | |
277 | for (i = 1; i < alen+1; i++) { | |
278 | xmit.buf[i] = (uint8_t) addr; | |
279 | addr >>= 8; | |
280 | } | |
281 | for (n = len + i; i < n; i++) | |
282 | xmit.buf[i] = *buffer++; | |
283 | xmit.len = i; | |
04a63b0d | 284 | |
f14850db | 285 | #if DEBUG_I2C |
61b0cfe9 L |
286 | dump_ram((uint8_t *) &xmit, 0x20, "=== i2c_send"); |
287 | _delay_ms(30); | |
04a63b0d | 288 | #endif |
61b0cfe9 L |
289 | /* Enable TWI, TWI int and initiate start condition */ |
290 | TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA); | |
291 | ||
292 | rc = xmit.stat; | |
293 | ||
294 | return rc; | |
295 | } | |
296 | ||
f14850db | 297 | static |
61b0cfe9 L |
298 | int i2c_recv(uint8_t chip, uint8_t *buffer, int8_t len) |
299 | { | |
300 | uint8_t rc; | |
301 | ||
302 | rc = i2c_waitready(); | |
303 | if ((rc & (BUSY | TIMEOUT)) != 0) | |
304 | return rc; | |
305 | ||
306 | xmit.stat = BUSY; | |
307 | xmit.len = len + 1; | |
308 | xmit.buf[0] = (chip<<1) | 1; | |
309 | ||
f14850db | 310 | #if DEBUG_I2C |
61b0cfe9 L |
311 | dump_ram((uint8_t *) &xmit, 0x20, "=== i2c_recv: before start"); |
312 | _delay_ms(30); | |
313 | #endif | |
314 | /* Enable TWI, TWI int and initiate start condition */ | |
315 | TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA); | |
316 | rc = i2c_waitready(); | |
317 | ||
f14850db | 318 | #if DEBUG_I2C |
61b0cfe9 L |
319 | dump_ram((uint8_t *) &xmit, 0x20, "=== i2c_recv: after completion"); |
320 | _delay_ms(30); | |
321 | #endif | |
322 | if (rc & DATA_ACK) { | |
323 | /* at least 1 byte received */ | |
324 | for (uint8_t i=1, n=xmit.idx; i < n; i++) | |
325 | *buffer++ = xmit.buf[i]; | |
326 | } | |
327 | ||
328 | return rc; | |
329 | } | |
330 | ||
331 | /* | |
332 | * Read/Write interface: | |
333 | * chip: I2C chip address, range 0..127 | |
334 | * addr: Memory (register) address within the chip | |
335 | * alen: Number of bytes to use for addr (typically 1, 2 for larger | |
336 | * memories, 0 for register type devices with only one | |
337 | * register) | |
338 | * buffer: Where to read/write the data | |
339 | * len: How many bytes to read/write | |
340 | * | |
341 | * Returns: 0 on success, not 0 on failure | |
342 | */ | |
343 | ||
344 | int i2c_write(uint8_t chip, unsigned int addr, uint_fast8_t alen, | |
345 | uint8_t *buffer, uint_fast8_t len) | |
346 | { | |
347 | int rc; | |
04a63b0d | 348 | |
61b0cfe9 L |
349 | if ((alen > 2) || (1 + alen + len > CONFIG_SYS_I2C_BUFSIZE)) { |
350 | debug("** i2c_write: buffer overflow, alen: %u, len: %u\n", | |
351 | alen, len); | |
352 | return -1; | |
353 | } | |
04a63b0d | 354 | |
61b0cfe9 L |
355 | i2c_send(chip, addr, alen, buffer, len); |
356 | rc = i2c_waitready(); | |
61b0cfe9 L |
357 | |
358 | return (rc & XMIT_DONE) != 0; | |
359 | } | |
360 | ||
361 | int i2c_read(uint8_t chip, unsigned int addr, uint_fast8_t alen, | |
362 | uint8_t *buffer, uint_fast8_t len) | |
363 | { | |
364 | int rc; | |
365 | ||
366 | if ((alen > 2) || (1 + len > CONFIG_SYS_I2C_BUFSIZE)) { | |
367 | debug("** i2c_read: parameter error: alen: %u, len: %u\n", | |
368 | alen, len); | |
369 | return -1; | |
370 | } | |
04a63b0d | 371 | |
61b0cfe9 L |
372 | if (alen != 0) { |
373 | i2c_send(chip, addr, alen, NULL, 0); | |
04a63b0d | 374 | } |
61b0cfe9 | 375 | rc = i2c_recv(chip, buffer, len); |
61b0cfe9 L |
376 | |
377 | return !((rc & (XMIT_DONE|DATA_ACK)) == (XMIT_DONE|DATA_ACK)); | |
378 | } |