aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/machine/machine_rp2_gpio.go
blob: d49d12f2ba8b531e5f76cd2e4811287867789e6b (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
//go:build rp2040 || rp2350

package machine

import (
	"device/rp"
	"runtime/interrupt"
	"runtime/volatile"
	"unsafe"
)

type ioType struct {
	status volatile.Register32
	ctrl   volatile.Register32
}

type irqCtrl struct {
	intE [_NUMBANK0_IRQS]volatile.Register32
	intF [_NUMBANK0_IRQS]volatile.Register32
	intS [_NUMBANK0_IRQS]volatile.Register32
}

type irqSummary struct {
	proc [2]struct {
		secure    [2]volatile.Register32
		nonsecure [2]volatile.Register32
	}
	comaWake struct {
		secure    [2]volatile.Register32
		nonsecure [2]volatile.Register32
	}
}

type ioBank0Type struct {
	io                 [_NUMBANK0_GPIOS]ioType
	irqsum             [rp2350ExtraReg]irqSummary
	intR               [_NUMBANK0_IRQS]volatile.Register32
	proc0IRQctrl       irqCtrl
	proc1IRQctrl       irqCtrl
	dormantWakeIRQctrl irqCtrl
}

var ioBank0 = (*ioBank0Type)(unsafe.Pointer(rp.IO_BANK0))

type padsBank0Type struct {
	voltageSelect volatile.Register32
	io            [_NUMBANK0_GPIOS]volatile.Register32
}

var padsBank0 = (*padsBank0Type)(unsafe.Pointer(rp.PADS_BANK0))

// pinFunc represents a GPIO function.
//
// Each GPIO can have one function selected at a time.
// Likewise, each peripheral input (e.g. UART0 RX) should only be  selected
// on one GPIO at a time. If the same peripheral input is connected to multiple GPIOs,
// the peripheral sees the logical OR of these GPIO inputs.
type pinFunc uint8

func (p Pin) PortMaskSet() (*uint32, uint32) {
	return (*uint32)(unsafe.Pointer(&rp.SIO.GPIO_OUT_SET)), 1 << p
}

// set drives the pin high
func (p Pin) set() {
	mask := uint32(1) << p
	rp.SIO.GPIO_OUT_SET.Set(mask)
}

func (p Pin) PortMaskClear() (*uint32, uint32) {
	return (*uint32)(unsafe.Pointer(&rp.SIO.GPIO_OUT_CLR)), 1 << p
}

// clr drives the pin low
func (p Pin) clr() {
	mask := uint32(1) << p
	rp.SIO.GPIO_OUT_CLR.Set(mask)
}

// xor toggles the pin
func (p Pin) xor() {
	mask := uint32(1) << p
	rp.SIO.GPIO_OUT_XOR.Set(mask)
}

// get returns the pin value
func (p Pin) get() bool {
	return rp.SIO.GPIO_IN.HasBits(1 << p)
}

func (p Pin) ioCtrl() *volatile.Register32 {
	return &ioBank0.io[p].ctrl
}

func (p Pin) padCtrl() *volatile.Register32 {
	return &padsBank0.io[p]
}

func (p Pin) pullup() {
	p.padCtrl().SetBits(rp.PADS_BANK0_GPIO0_PUE)
	p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PDE)
}

func (p Pin) pulldown() {
	p.padCtrl().SetBits(rp.PADS_BANK0_GPIO0_PDE)
	p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE)
}

func (p Pin) pulloff() {
	p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PDE)
	p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE)
}

// setSlew sets pad slew rate control.
// true sets to fast. false sets to slow.
func (p Pin) setSlew(sr bool) {
	p.padCtrl().ReplaceBits(boolToBit(sr)<<rp.PADS_BANK0_GPIO0_SLEWFAST_Pos, rp.PADS_BANK0_GPIO0_SLEWFAST_Msk, 0)
}

// setSchmitt enables or disables Schmitt trigger.
func (p Pin) setSchmitt(trigger bool) {
	p.padCtrl().ReplaceBits(boolToBit(trigger)<<rp.PADS_BANK0_GPIO0_SCHMITT_Pos, rp.PADS_BANK0_GPIO0_SCHMITT_Msk, 0)
}

// setFunc will set pin function to fn.
func (p Pin) setFunc(fn pinFunc) {
	// Set input enable, Clear output disable
	p.padCtrl().ReplaceBits(rp.PADS_BANK0_GPIO0_IE, padEnableMask, 0)

	// Zero all fields apart from fsel; we want this IO to do what the peripheral tells it.
	// This doesn't affect e.g. pullup/pulldown, as these are in pad controls.
	p.ioCtrl().Set(uint32(fn) << rp.IO_BANK0_GPIO0_CTRL_FUNCSEL_Pos)
}

// init initializes the gpio pin
func (p Pin) init() {
	mask := uint32(1) << p
	rp.SIO.GPIO_OE_CLR.Set(mask)
	p.clr()
}

// Set drives the pin high if value is true else drives it low.
func (p Pin) Set(value bool) {
	if p == NoPin {
		return
	}
	if value {
		p.set()
	} else {
		p.clr()
	}
}

// Get reads the pin value.
func (p Pin) Get() bool {
	return p.get()
}

// PinChange represents one or more trigger events that can happen on a given GPIO pin
// on the RP2040. ORed PinChanges are valid input to most IRQ functions.
type PinChange uint8

// Pin change interrupt constants for SetInterrupt.
const (
	// Edge falling
	PinFalling PinChange = 4 << iota
	// Edge rising
	PinRising

	PinToggle = PinFalling | PinRising
)

// Callbacks to be called for pins configured with SetInterrupt.
var (
	pinCallbacks [2][_NUMBANK0_GPIOS]func(Pin)
	setInt       [2][_NUMBANK0_GPIOS]bool
)

// SetInterrupt sets an interrupt to be executed when a particular pin changes
// state. The pin should already be configured as an input, including a pull up
// or down if no external pull is provided.
//
// This call will replace a previously set callback on this pin. You can pass a
// nil func to unset the pin change interrupt. If you do so, the change
// parameter is ignored and can be set to any value (such as 0).
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
	if p == NoPin {
		return nil
	}
	if p > 31 || p < 0 {
		return ErrInvalidInputPin
	}
	core := CurrentCore()
	if callback == nil {
		// disable current interrupt
		p.setInterrupt(change, false)
		pinCallbacks[core][p] = nil
		return nil
	}

	if pinCallbacks[core][p] != nil {
		// Callback already configured. Should disable callback by passing a nil callback first.
		return ErrNoPinChangeChannel
	}
	p.setInterrupt(change, true)
	pinCallbacks[core][p] = callback

	if setInt[core][p] {
		// interrupt has already been set. Exit.
		return nil
	}
	interrupt.New(rp.IRQ_IO_IRQ_BANK0, gpioHandleInterrupt).Enable()
	irqSet(rp.IRQ_IO_IRQ_BANK0, true)
	return nil
}

// gpioHandleInterrupt finds the corresponding pin for the interrupt.
// C SDK equivalent of gpio_irq_handler
func gpioHandleInterrupt(intr interrupt.Interrupt) {

	core := CurrentCore()
	var gpio Pin
	for gpio = 0; gpio < _NUMBANK0_GPIOS; gpio++ {
		var base *irqCtrl
		switch core {
		case 0:
			base = &ioBank0.proc0IRQctrl
		case 1:
			base = &ioBank0.proc1IRQctrl
		}

		statreg := base.intS[gpio>>3].Get()
		change := getIntChange(gpio, statreg)
		if change != 0 {
			gpio.acknowledgeInterrupt(change)
			callback := pinCallbacks[core][gpio]
			if callback != nil {
				callback(gpio)
			}
		}
	}
}

// events returns the bit representation of the pin change for the rp2040.
func (change PinChange) events() uint32 {
	return uint32(change)
}

// intBit is the bit storage form of a PinChange for a given Pin
// in the IO_BANK0 interrupt registers (page 269 RP2040 Datasheet).
func (p Pin) ioIntBit(change PinChange) uint32 {
	return change.events() << (4 * (p % 8))
}

// Acquire interrupt data from a INT status register.
func getIntChange(p Pin, status uint32) PinChange {
	return PinChange(status>>(4*(p%8))) & 0xf
}