Skip to content

Commit fce82de

Browse files
committed
cpu/nrf5x i2c: Forbid receive NOSTOP, document rationale
1 parent b91161d commit fce82de

File tree

1 file changed

+34
-19
lines changed

1 file changed

+34
-19
lines changed

cpu/nrf5x_common/periph/i2c_nrf52_nrf9160.c

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,29 @@
1919
* @author Hauke Petersen <[email protected]>
2020
* @author Dylan Laduranty <[email protected]>
2121
*
22+
* As this implementation is based on the nRF5x TWIM peripheral, it can not
23+
* issue a read following a read (or a write following a write) without
24+
* creating a (repeated) start condition:
25+
* <https://devzone.nordicsemi.com/f/nordic-q-a/66615/nrf52840-twim-how-to-write-multiple-buffers-without-repeated-start-condition>,
26+
* backed also by later experiments discussed in the [Rust embedded
27+
* channel](https://matrix.to/#/!BHcierreUuwCMxVqOf:matrix.org/$JwNejRaeJx_tvqKgS88GenDG8ZNHrkTW09896dIehQ8?via=matrix.org&via=catircservices.org&via=tchncs.de).
28+
* Due to this shortcoming in the hardware, any operations with I2C_NOSTART
29+
* fail.
30+
*
31+
* Relatedly, the successful termination of a read or write can not be detected
32+
* by an interrupt (only the eventual STOPPED condition after the event
33+
* short-circuiting of LASTTX/LASTRX to STOP triggers one). There are LASTTX /
34+
* LASTRX interrupts, but while the LASTTX is sensible enough (the last byte
35+
* has been read, is being written, the caller may now repurpose the buffers),
36+
* the LASTRX interrupt fires at the start of the last byte reading, and the
37+
* user can not reliably know when the last byte was written (at least not
38+
* easily). Therefore, reads with I2C_NOSTOP are not supported.
39+
*
40+
* In combination, these still allow the typical I2C operations: A single
41+
* write, and a write (selecting a register) followed by a read, as well as
42+
* stand-alone reads. More complex patterns are not supported; in particular,
43+
* scatter-gather reads or writes are not possible.
44+
*
2245
* @}
2346
*/
2447

@@ -77,13 +100,15 @@ static inline NRF_TWIM_Type *bus(i2c_t dev)
77100
* * TWIM_INTEN_STOPPED_Msk (when a stop condition is to be set and the short
78101
* circuit will pull TWIM into the stopped condition)
79102
* * TWIM_INTEN_LASTTX_Msk (when sending without a stop condition)
80-
* * TWIM_INTEN_LASTRX_Msk (when reading without a stop condition)
103+
*
104+
* (TWIM_INTEN_LASTRX_Msk makes no sense here because that interrupt fires
105+
* before the data is ready).
81106
*
82107
* Any addition needs to be added to the mask in i2c_isr_handler.
83108
*/
84109
static int finish(i2c_t dev, int inten_success_flag)
85110
{
86-
DEBUG("[i2c] waiting for success (STOPPED/LAST.X) or ERROR event\n");
111+
DEBUG("[i2c] waiting for success (STOPPED/LASTTX) or ERROR event\n");
87112
/* Unmask interrupts */
88113
bus(dev)->INTENSET = inten_success_flag | TWIM_INTEN_ERROR_Msk;
89114
mutex_lock(&busy[dev]);
@@ -251,7 +276,7 @@ int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len,
251276
{
252277
assert((dev < I2C_NUMOF) && data && (len > 0) && (len < 256));
253278

254-
if (flags & (I2C_NOSTART | I2C_ADDR10)) {
279+
if (flags & (I2C_NOSTART | I2C_ADDR10 | I2C_NOSTOP)) {
255280
return -EOPNOTSUPP;
256281
}
257282
DEBUG("[i2c] read_bytes: %i bytes from addr 0x%02x\n", (int)len, (int)addr);
@@ -261,14 +286,8 @@ int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len,
261286
bus(dev)->RXD.MAXCNT = (uint8_t)len;
262287

263288
int inten_success_flag;
264-
if (!(flags & I2C_NOSTOP)) {
265-
bus(dev)->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
266-
inten_success_flag = TWIM_INTEN_STOPPED_Msk;
267-
}
268-
else {
269-
bus(dev)->SHORTS = 0;
270-
inten_success_flag = TWIM_INTEN_LASTRX_Msk;
271-
}
289+
bus(dev)->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
290+
inten_success_flag = TWIM_INTEN_STOPPED_Msk;
272291
/* Start transmission */
273292
bus(dev)->TASKS_STARTRX = 1;
274293

@@ -280,7 +299,7 @@ int i2c_read_regs(i2c_t dev, uint16_t addr, uint16_t reg,
280299
{
281300
assert((dev < I2C_NUMOF) && data && (len > 0) && (len < 256));
282301

283-
if (flags & (I2C_NOSTART | I2C_ADDR10)) {
302+
if (flags & (I2C_NOSTART | I2C_ADDR10 | I2C_NOSTOP)) {
284303
return -EOPNOTSUPP;
285304
}
286305

@@ -301,12 +320,8 @@ int i2c_read_regs(i2c_t dev, uint16_t addr, uint16_t reg,
301320
bus(dev)->RXD.PTR = (uint32_t)data;
302321
bus(dev)->RXD.MAXCNT = (uint8_t)len;
303322

304-
int inten_success_flag = TWIM_INTEN_LASTRX_Msk;
305-
bus(dev)->SHORTS = (TWIM_SHORTS_LASTTX_STARTRX_Msk);
306-
if (!(flags & I2C_NOSTOP)) {
307-
bus(dev)->SHORTS |= TWIM_SHORTS_LASTRX_STOP_Msk;
308-
inten_success_flag = TWIM_INTEN_STOPPED_Msk;
309-
}
323+
int inten_success_flag = TWIM_INTEN_STOPPED_Msk;
324+
bus(dev)->SHORTS = TWIM_SHORTS_LASTTX_STARTRX_Msk | TWIM_SHORTS_LASTRX_STOP_Msk;
310325

311326
/* Start transfer */
312327
bus(dev)->TASKS_STARTTX = 1;
@@ -346,7 +361,7 @@ void i2c_isr_handler(void *arg)
346361
i2c_t dev = (i2c_t)(uintptr_t)arg;
347362

348363
/* Mask interrupts to ensure that they only trigger once */
349-
bus(dev)->INTENCLR = TWIM_INTEN_STOPPED_Msk | TWIM_INTEN_ERROR_Msk | TWIM_INTEN_LASTTX_Msk | TWIM_INTEN_LASTRX_Msk;
364+
bus(dev)->INTENCLR = TWIM_INTEN_STOPPED_Msk | TWIM_INTEN_ERROR_Msk | TWIM_INTEN_LASTTX_Msk;
350365

351366
mutex_unlock(&busy[dev]);
352367
}

0 commit comments

Comments
 (0)