Skip to content

Commit 7445741

Browse files
soypatdeadprogram
authored andcommitted
machine/rp2040: fix i2c crash when getting abort while waiting for stop condition
1 parent bbe755f commit 7445741

File tree

1 file changed

+81
-16
lines changed

1 file changed

+81
-16
lines changed

src/machine/machine_rp2040_i2c.go

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,9 @@ type I2CConfig struct {
5252
}
5353

5454
type I2C struct {
55-
Bus *rp.I2C0_Type
56-
restartOnNext bool
57-
mode I2CMode
58-
txInProgress bool
55+
Bus *rp.I2C0_Type
56+
mode I2CMode
57+
txInProgress bool
5958
}
6059

6160
var (
@@ -236,7 +235,6 @@ func (i2c *I2C) init(config I2CConfig) error {
236235
if err := i2c.disable(); err != nil {
237236
return err
238237
}
239-
i2c.restartOnNext = false
240238

241239
i2c.mode = config.Mode
242240

@@ -306,16 +304,17 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
306304
i2c.Bus.IC_TAR.Set(uint32(addr))
307305
i2c.enable()
308306
abort := false
309-
var abortReason uint32
307+
var abortReason i2cAbortError
308+
txStop := rxlen == 0
310309
for txCtr := 0; txCtr < txlen; txCtr++ {
311310
if abort {
312311
break
313312
}
314313
first := txCtr == 0
315314
last := txCtr == txlen-1 && rxlen == 0
316315
i2c.Bus.IC_DATA_CMD.Set(
317-
(boolToBit(first && i2c.restartOnNext) << rp.I2C0_IC_DATA_CMD_RESTART_Pos) |
318-
(boolToBit(last) << rp.I2C0_IC_DATA_CMD_STOP_Pos) |
316+
(boolToBit(first) << rp.I2C0_IC_DATA_CMD_RESTART_Pos) |
317+
(boolToBit(last && txStop) << rp.I2C0_IC_DATA_CMD_STOP_Pos) |
319318
uint32(tx[txCtr]))
320319

321320
// Wait until the transmission of the address/data from the internal
@@ -356,6 +355,9 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
356355
// to take care of the abort.
357356
for !i2c.interrupted(rp.I2C0_IC_RAW_INTR_STAT_STOP_DET) {
358357
if ticks() > deadline {
358+
if abort {
359+
return abortReason
360+
}
359361
return errI2CWriteTimeout
360362
}
361363

@@ -365,6 +367,16 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
365367
}
366368
}
367369

370+
// Midway check for abort. Related issue https://github.com/tinygo-org/tinygo/issues/3671.
371+
// The root cause for an abort after writing registers was "tx data no ack" (abort code=8).
372+
// If the abort code was not registered then the whole peripheral would remain in disabled state forever.
373+
abortReason = i2c.getAbortReason()
374+
if abortReason != 0 {
375+
i2c.clearAbortReason()
376+
abort = true
377+
}
378+
379+
rxStart := txlen == 0
368380
if rxlen > 0 && !abort {
369381
for rxCtr := 0; rxCtr < rxlen; rxCtr++ {
370382
first := rxCtr == 0
@@ -373,14 +385,14 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
373385
gosched()
374386
}
375387
i2c.Bus.IC_DATA_CMD.Set(
376-
boolToBit(first && i2c.restartOnNext)<<rp.I2C0_IC_DATA_CMD_RESTART_Pos |
388+
boolToBit(first && rxStart)<<rp.I2C0_IC_DATA_CMD_RESTART_Pos |
377389
boolToBit(last)<<rp.I2C0_IC_DATA_CMD_STOP_Pos |
378390
rp.I2C0_IC_DATA_CMD_CMD) // -> 1 for read
379391

380392
for !abort && i2c.readAvailable() == 0 {
381393
abortReason = i2c.getAbortReason()
382-
i2c.clearAbortReason()
383394
if abortReason != 0 {
395+
i2c.clearAbortReason()
384396
abort = true
385397
}
386398
if ticks() > deadline {
@@ -407,7 +419,7 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
407419
// Address acknowledged, some data not acknowledged
408420
fallthrough
409421
default:
410-
err = makeI2CAbortError(abortReason)
422+
err = abortReason
411423
}
412424
}
413425
return err
@@ -534,8 +546,8 @@ func (i2c *I2C) clearAbortReason() {
534546
// getAbortReason reads IC_TX_ABRT_SOURCE register.
535547
//
536548
//go:inline
537-
func (i2c *I2C) getAbortReason() uint32 {
538-
return i2c.Bus.IC_TX_ABRT_SOURCE.Get()
549+
func (i2c *I2C) getAbortReason() i2cAbortError {
550+
return i2cAbortError(i2c.Bus.IC_TX_ABRT_SOURCE.Get())
539551
}
540552

541553
// returns true if RAW_INTR_STAT bits in mask are all set. performs:
@@ -554,9 +566,62 @@ func (b i2cAbortError) Error() string {
554566
return "i2c abort, reason " + itoa.Uitoa(uint(b))
555567
}
556568

557-
//go:inline
558-
func makeI2CAbortError(reason uint32) error {
559-
return i2cAbortError(reason)
569+
func (b i2cAbortError) Reasons() (reasons []string) {
570+
if b == 0 {
571+
return nil
572+
}
573+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK != 0 {
574+
reasons = append(reasons, "7-bit address no ack")
575+
}
576+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK != 0 {
577+
reasons = append(reasons, "10-bit address first byte no ack")
578+
}
579+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK != 0 {
580+
reasons = append(reasons, "10-bit address second byte no ack")
581+
}
582+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK != 0 {
583+
reasons = append(reasons, "tx data no ack")
584+
}
585+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_GCALL_NOACK != 0 {
586+
reasons = append(reasons, "general call no ack")
587+
}
588+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_GCALL_READ != 0 {
589+
reasons = append(reasons, "general call read")
590+
}
591+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_HS_ACKDET != 0 {
592+
reasons = append(reasons, "high speed ack detect")
593+
}
594+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_SBYTE_ACKDET != 0 {
595+
reasons = append(reasons, "start byte ack detect")
596+
}
597+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_HS_NORSTRT != 0 {
598+
reasons = append(reasons, "high speed no restart")
599+
}
600+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT != 0 {
601+
reasons = append(reasons, "start byte no restart")
602+
}
603+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_10B_RD_NORSTRT != 0 {
604+
reasons = append(reasons, "10-bit read no restart")
605+
}
606+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_MASTER_DIS != 0 {
607+
reasons = append(reasons, "master disabled")
608+
}
609+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ARB_LOST != 0 {
610+
reasons = append(reasons, "arbitration lost")
611+
}
612+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_SLVFLUSH_TXFIFO != 0 {
613+
reasons = append(reasons, "slave flush tx fifo")
614+
}
615+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_SLV_ARBLOST != 0 {
616+
reasons = append(reasons, "slave arbitration lost")
617+
}
618+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_SLVRD_INTX != 0 {
619+
reasons = append(reasons, "slave read while inactive")
620+
}
621+
if b&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_USER_ABRT != 0 {
622+
reasons = append(reasons, "user abort")
623+
}
624+
return reasons
560625
}
561626

562627
//go:inline

0 commit comments

Comments
 (0)