aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/machine/machine_nrf528xx.go
blob: f8937aec06415bb03583d31315fcf966d056684b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//go:build nrf52840 || nrf52833

package machine

import (
	"device/nrf"
	"unsafe"
)

// I2C on the NRF528xx.
type I2C struct {
	Bus  *nrf.TWIM_Type // Called Bus to align with Bus field in nrf51
	BusT *nrf.TWIS_Type
	mode I2CMode
}

// There are 2 I2C interfaces on the NRF.
var (
	I2C0 = &I2C{Bus: nrf.TWIM0, BusT: nrf.TWIS0}
	I2C1 = &I2C{Bus: nrf.TWIM1, BusT: nrf.TWIS1}
)

func (i2c *I2C) enableAsController() {
	i2c.Bus.ENABLE.Set(nrf.TWIM_ENABLE_ENABLE_Enabled)
}

func (i2c *I2C) enableAsTarget() {
	i2c.BusT.ENABLE.Set(nrf.TWIS_ENABLE_ENABLE_Enabled)
}

func (i2c *I2C) disable() {
	i2c.Bus.ENABLE.Set(0)
}

// Tx does a single I2C transaction at the specified address (when in controller mode).
//
// It clocks out the given address, writes the bytes in w, reads back len(r)
// bytes and stores them in r, and generates a stop condition on the bus.
func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
	i2c.Bus.ADDRESS.Set(uint32(addr))

	i2c.Bus.EVENTS_STOPPED.Set(0)
	i2c.Bus.EVENTS_ERROR.Set(0)
	i2c.Bus.EVENTS_RXSTARTED.Set(0)
	i2c.Bus.EVENTS_TXSTARTED.Set(0)
	i2c.Bus.EVENTS_LASTRX.Set(0)
	i2c.Bus.EVENTS_LASTTX.Set(0)
	i2c.Bus.EVENTS_SUSPENDED.Set(0)

	// Configure for a single shot to perform both write and read (as applicable)
	if len(w) != 0 {
		i2c.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
		i2c.Bus.TXD.MAXCNT.Set(uint32(len(w)))

		// If no read, immediately signal stop after TX
		if len(r) == 0 {
			i2c.Bus.SHORTS.Set(nrf.TWIM_SHORTS_LASTTX_STOP)
		}
	}
	if len(r) != 0 {
		i2c.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
		i2c.Bus.RXD.MAXCNT.Set(uint32(len(r)))

		// Auto-start Rx after Tx and Stop after Rx
		i2c.Bus.SHORTS.Set(nrf.TWIM_SHORTS_LASTTX_STARTRX | nrf.TWIM_SHORTS_LASTRX_STOP)
	}

	// Fire the transaction
	i2c.Bus.TASKS_RESUME.Set(1)
	if len(w) != 0 {
		i2c.Bus.TASKS_STARTTX.Set(1)
	} else if len(r) != 0 {
		i2c.Bus.TASKS_STARTRX.Set(1)
	}

	// Wait until transaction stopped to ensure buffers fully processed
	for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
		// Allow scheduler to run
		gosched()

		// Handle errors by ensuring STOP sent on bus
		if i2c.Bus.EVENTS_ERROR.Get() != 0 {
			if i2c.Bus.EVENTS_STOPPED.Get() == 0 {
				// STOP cannot be sent during SUSPEND
				i2c.Bus.TASKS_RESUME.Set(1)
				i2c.Bus.TASKS_STOP.Set(1)
			}
			err = twiCError(i2c.Bus.ERRORSRC.Get())
		}
	}

	return
}

// Listen starts listening for I2C requests sent to specified address
//
// addr is the address to listen to
func (i2c *I2C) Listen(addr uint8) error {
	i2c.BusT.ADDRESS[0].Set(uint32(addr))
	i2c.BusT.CONFIG.Set(nrf.TWIS_CONFIG_ADDRESS0_Enabled)

	i2c.BusT.EVENTS_STOPPED.Set(0)
	i2c.BusT.EVENTS_ERROR.Set(0)
	i2c.BusT.EVENTS_RXSTARTED.Set(0)
	i2c.BusT.EVENTS_TXSTARTED.Set(0)
	i2c.BusT.EVENTS_WRITE.Set(0)
	i2c.BusT.EVENTS_READ.Set(0)

	return nil
}

// WaitForEvent blocks the current go-routine until an I2C event is received (when in Target mode).
//
// The passed buffer will be populated for receive events, with the number of bytes
// received returned in count.  For other event types, buf is not modified and a count
// of zero is returned.
//
// For request events, the caller MUST call `Reply` to avoid hanging the i2c bus indefinitely.
func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err error) {
	i2c.BusT.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&buf[0]))))
	i2c.BusT.RXD.MAXCNT.Set(uint32(len(buf)))

	i2c.BusT.TASKS_PREPARERX.Set(nrf.TWIS_TASKS_PREPARERX_TASKS_PREPARERX_Trigger)

	i2c.Bus.TASKS_RESUME.Set(1)

	for i2c.BusT.EVENTS_STOPPED.Get() == 0 &&
		i2c.BusT.EVENTS_READ.Get() == 0 {
		gosched()

		if i2c.BusT.EVENTS_ERROR.Get() != 0 {
			i2c.BusT.EVENTS_ERROR.Set(0)
			return I2CReceive, 0, twisError(i2c.BusT.ERRORSRC.Get())
		}
	}

	count = 0
	evt = I2CFinish
	err = nil

	if i2c.BusT.EVENTS_WRITE.Get() != 0 {
		i2c.BusT.EVENTS_WRITE.Set(0)

		// Data was sent to this target.  We've waited for
		// READ or STOPPED event, so transmission should be
		// complete.
		count = int(i2c.BusT.RXD.AMOUNT.Get())
		evt = I2CReceive
	} else if i2c.BusT.EVENTS_READ.Get() != 0 {
		i2c.BusT.EVENTS_READ.Set(0)

		// Data is requested from this target, hw will stretch
		// the controller's clock until there is a reply to
		// send
		evt = I2CRequest
	} else if i2c.BusT.EVENTS_STOPPED.Get() != 0 {
		i2c.BusT.EVENTS_STOPPED.Set(0)
		evt = I2CFinish
	}

	return
}

// Reply supplies the response data the controller.
func (i2c *I2C) Reply(buf []byte) error {
	i2c.BusT.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&buf[0]))))
	i2c.BusT.TXD.MAXCNT.Set(uint32(len(buf)))

	i2c.BusT.EVENTS_STOPPED.Set(0)

	// Trigger Tx
	i2c.BusT.TASKS_PREPARETX.Set(nrf.TWIS_TASKS_PREPARETX_TASKS_PREPARETX_Trigger)

	// Block, waiting for Tx to complete
	for i2c.BusT.EVENTS_STOPPED.Get() == 0 {
		gosched()

		if i2c.BusT.EVENTS_ERROR.Get() != 0 {
			return twisError(i2c.BusT.ERRORSRC.Get())
		}
	}

	i2c.BusT.EVENTS_STOPPED.Set(0)

	return nil
}

// twiCError converts an I2C controller error to Go
func twiCError(val uint32) error {
	if val == 0 {
		return nil
	} else if val&nrf.TWIM_ERRORSRC_OVERRUN_Msk == nrf.TWIM_ERRORSRC_OVERRUN {
		return errI2CBusError
	} else if val&nrf.TWIM_ERRORSRC_ANACK_Msk == nrf.TWIM_ERRORSRC_ANACK {
		return errI2CAckExpected
	} else if val&nrf.TWIM_ERRORSRC_DNACK_Msk == nrf.TWIM_ERRORSRC_DNACK {
		return errI2CAckExpected
	}

	return errI2CBusError
}

// twisError converts an I2C target error to Go
func twisError(val uint32) error {
	if val == 0 {
		return nil
	} else if val&nrf.TWIS_ERRORSRC_OVERFLOW_Msk == nrf.TWIS_ERRORSRC_OVERFLOW {
		return errI2COverflow
	} else if val&nrf.TWIS_ERRORSRC_DNACK_Msk == nrf.TWIS_ERRORSRC_DNACK {
		return errI2CAckExpected
	} else if val&nrf.TWIS_ERRORSRC_OVERREAD_Msk == nrf.TWIS_ERRORSRC_OVERREAD {
		return errI2COverread
	}

	return errI2CBusError
}

var (
	Watchdog = &watchdogImpl{}
)

const (
	// WatchdogMaxTimeout in milliseconds (approx 36h)
	WatchdogMaxTimeout = (0xffffffff * 1000) / 32768
)

type watchdogImpl struct {
}

// Configure the watchdog.
//
// This method should not be called after the watchdog is started and on
// some platforms attempting to reconfigure after starting the watchdog
// is explicitly forbidden / will not work.
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
	// 32.768kHz counter
	crv := int32((int64(config.TimeoutMillis) * 32768) / 1000)
	nrf.WDT.CRV.Set(uint32(crv))

	// One source
	nrf.WDT.RREN.Set(0x1)

	// Run during sleep
	nrf.WDT.CONFIG.Set(nrf.WDT_CONFIG_SLEEP_Run)

	return nil
}

// Starts the watchdog.
func (wd *watchdogImpl) Start() error {
	nrf.WDT.TASKS_START.Set(nrf.WDT_TASKS_START_TASKS_START)
	return nil
}

// Update the watchdog, indicating that `source` is healthy.
func (wd *watchdogImpl) Update() {
	// 0x6E524635 = magic value from datasheet
	nrf.WDT.RR[0].Set(0x6E524635)
}