1446 lines
42 KiB
Go
1446 lines
42 KiB
Go
// Copyright 2017 The Periph Authors. All rights reserved.
|
|
// Use of this source code is governed under the Apache License, Version 2.0
|
|
// that can be found in the LICENSE file.
|
|
|
|
package bcm283x
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"periph.io/x/conn/v3/driver/driverreg"
|
|
"periph.io/x/conn/v3/gpio"
|
|
"periph.io/x/conn/v3/gpio/gpioreg"
|
|
"periph.io/x/conn/v3/gpio/gpiostream"
|
|
"periph.io/x/conn/v3/physic"
|
|
"periph.io/x/conn/v3/pin"
|
|
"periph.io/x/host/v3/distro"
|
|
"periph.io/x/host/v3/pmem"
|
|
"periph.io/x/host/v3/sysfs"
|
|
"periph.io/x/host/v3/videocore"
|
|
)
|
|
|
|
// All the pins supported by the CPU.
|
|
var (
|
|
GPIO0 *Pin // I2C0_SDA
|
|
GPIO1 *Pin // I2C0_SCL
|
|
GPIO2 *Pin // I2C1_SDA
|
|
GPIO3 *Pin // I2C1_SCL
|
|
GPIO4 *Pin // CLK0
|
|
GPIO5 *Pin // CLK1
|
|
GPIO6 *Pin // CLK2
|
|
GPIO7 *Pin // SPI0_CS1
|
|
GPIO8 *Pin // SPI0_CS0
|
|
GPIO9 *Pin // SPI0_MISO
|
|
GPIO10 *Pin // SPI0_MOSI
|
|
GPIO11 *Pin // SPI0_CLK
|
|
GPIO12 *Pin // PWM0
|
|
GPIO13 *Pin // PWM1
|
|
GPIO14 *Pin // UART0_TX, UART1_TX
|
|
GPIO15 *Pin // UART0_RX, UART1_RX
|
|
GPIO16 *Pin // UART0_CTS, SPI1_CS2, UART1_CTS
|
|
GPIO17 *Pin // UART0_RTS, SPI1_CS1, UART1_RTS
|
|
GPIO18 *Pin // I2S_SCK, SPI1_CS0, PWM0
|
|
GPIO19 *Pin // I2S_WS, SPI1_MISO, PWM1
|
|
GPIO20 *Pin // I2S_DIN, SPI1_MOSI, CLK0
|
|
GPIO21 *Pin // I2S_DOUT, SPI1_CLK, CLK1
|
|
GPIO22 *Pin //
|
|
GPIO23 *Pin //
|
|
GPIO24 *Pin //
|
|
GPIO25 *Pin //
|
|
GPIO26 *Pin //
|
|
GPIO27 *Pin //
|
|
GPIO28 *Pin // I2C0_SDA, I2S_SCK
|
|
GPIO29 *Pin // I2C0_SCL, I2S_WS
|
|
GPIO30 *Pin // I2S_DIN, UART0_CTS, UART1_CTS
|
|
GPIO31 *Pin // I2S_DOUT, UART0_RTS, UART1_RTS
|
|
GPIO32 *Pin // CLK0, UART0_TX, UART1_TX
|
|
GPIO33 *Pin // UART0_RX, UART1_RX
|
|
GPIO34 *Pin // CLK0
|
|
GPIO35 *Pin // SPI0_CS1
|
|
GPIO36 *Pin // SPI0_CS0, UART0_TX
|
|
GPIO37 *Pin // SPI0_MISO, UART0_RX
|
|
GPIO38 *Pin // SPI0_MOSI, UART0_RTS
|
|
GPIO39 *Pin // SPI0_CLK, UART0_CTS
|
|
GPIO40 *Pin // PWM0, SPI2_MISO, UART1_TX
|
|
GPIO41 *Pin // PWM1, SPI2_MOSI, UART1_RX
|
|
GPIO42 *Pin // CLK1, SPI2_CLK, UART1_RTS
|
|
GPIO43 *Pin // CLK2, SPI2_CS0, UART1_CTS
|
|
GPIO44 *Pin // CLK1, I2C0_SDA, I2C1_SDA, SPI2_CS1
|
|
GPIO45 *Pin // PWM1, I2C0_SCL, I2C1_SCL, SPI2_CS2
|
|
GPIO46 *Pin //
|
|
// Pins 47~53 are not exposed because using them would lead to immediate SD
|
|
// Card corruption.
|
|
)
|
|
|
|
// Present returns true if running on a Broadcom bcm283x based CPU.
|
|
func Present() bool {
|
|
if isArm {
|
|
for _, line := range distro.DTCompatible() {
|
|
if strings.HasPrefix(line, "brcm,bcm") {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// PinsRead0To31 returns the value of all GPIO0 to GPIO31 at their corresponding
|
|
// bit as a single read operation.
|
|
//
|
|
// This function is extremely fast and does no error checking.
|
|
//
|
|
// The returned bits are valid for both inputs and outputs.
|
|
func PinsRead0To31() uint32 {
|
|
return drvGPIO.gpioMemory.level[0]
|
|
}
|
|
|
|
// PinsClear0To31 clears the value of GPIO0 to GPIO31 pin for the bit set at
|
|
// their corresponding bit as a single write operation.
|
|
//
|
|
// This function is extremely fast and does no error checking.
|
|
func PinsClear0To31(mask uint32) {
|
|
drvGPIO.gpioMemory.outputClear[0] = mask
|
|
}
|
|
|
|
// PinsSet0To31 sets the value of GPIO0 to GPIO31 pin for the bit set at their
|
|
// corresponding bit as a single write operation.
|
|
func PinsSet0To31(mask uint32) {
|
|
drvGPIO.gpioMemory.outputSet[0] = mask
|
|
}
|
|
|
|
// PinsRead32To46 returns the value of all GPIO32 to GPIO46 at their
|
|
// corresponding 'bit minus 32' as a single read operation.
|
|
//
|
|
// This function is extremely fast and does no error checking.
|
|
//
|
|
// The returned bits are valid for both inputs and outputs.
|
|
//
|
|
// Bits above 15 are guaranteed to be 0.
|
|
//
|
|
// This function is not recommended on Raspberry Pis as these GPIOs are not
|
|
// easily accessible.
|
|
func PinsRead32To46() uint32 {
|
|
return drvGPIO.gpioMemory.level[1] & 0x7fff
|
|
}
|
|
|
|
// PinsClear32To46 clears the value of GPIO31 to GPIO46 pin for the bit set at
|
|
// their corresponding 'bit minus 32' as a single write operation.
|
|
//
|
|
// This function is extremely fast and does no error checking.
|
|
//
|
|
// Bits above 15 are ignored.
|
|
//
|
|
// This function is not recommended on Raspberry Pis as these GPIOs are not
|
|
// easily accessible.
|
|
func PinsClear32To46(mask uint32) {
|
|
drvGPIO.gpioMemory.outputClear[1] = (mask & 0x7fff)
|
|
}
|
|
|
|
// PinsSet32To46 sets the value of GPIO31 to GPIO46 pin for the bit set at
|
|
// their corresponding 'bit minus 32' as a single write operation.
|
|
//
|
|
// This function is extremely fast and does no error checking.
|
|
//
|
|
// Bits above 15 are ignored.
|
|
//
|
|
// This function is not recommended on Raspberry Pis as these GPIOs are not
|
|
// easily accessible.
|
|
func PinsSet32To46(mask uint32) {
|
|
drvGPIO.gpioMemory.outputSet[1] = (mask & 0x7fff)
|
|
}
|
|
|
|
// PinsSetup0To27 sets the output current drive strength, output slew limiting
|
|
// and input hysteresis for GPIO 0 to 27.
|
|
//
|
|
// Default drive is 8mA, slew unlimited and hysteresis enabled.
|
|
//
|
|
// Can only be used if driver bcm283x-dma was loaded.
|
|
func PinsSetup0To27(drive physic.ElectricCurrent, slewLimit, hysteresis bool) error {
|
|
if drvDMA.gpioPadMemory == nil {
|
|
return errors.New("bcm283x-dma not initialized; try again as root?")
|
|
}
|
|
drvDMA.gpioPadMemory.pads0.set(toPad(drive, slewLimit, hysteresis))
|
|
return nil
|
|
}
|
|
|
|
// PinsSetup28To45 sets the output current drive strength, output slew limiting
|
|
// and input hysteresis for GPIO 28 to 45.
|
|
//
|
|
// Default drive is 16mA, slew unlimited and hysteresis enabled.
|
|
//
|
|
// Can only be used if driver bcm283x-dma was loaded.
|
|
//
|
|
// This function is not recommended on Raspberry Pis as these GPIOs are not
|
|
// easily accessible.
|
|
func PinsSetup28To45(drive physic.ElectricCurrent, slewLimit, hysteresis bool) error {
|
|
if drvDMA.gpioPadMemory == nil {
|
|
return errors.New("bcm283x-dma not initialized; try again as root?")
|
|
}
|
|
drvDMA.gpioPadMemory.pads1.set(toPad(drive, slewLimit, hysteresis))
|
|
return nil
|
|
}
|
|
|
|
// Pin is a GPIO number (GPIOnn) on BCM238(5|6|7).
|
|
//
|
|
// Pin implements gpio.PinIO.
|
|
type Pin struct {
|
|
// Immutable.
|
|
number int
|
|
name string
|
|
defaultPull gpio.Pull // Default pull at system boot, as per datasheet.
|
|
|
|
// Immutable after driver initialization.
|
|
sysfsPin *sysfs.Pin // Set to the corresponding sysfs.Pin, if any.
|
|
|
|
// Mutable.
|
|
usingEdge bool // Set when edge detection is enabled.
|
|
usingClock bool // Set when a CLK, PWM or I2S/PCM clock is used.
|
|
dmaCh *dmaChannel // Set when DMA is used for PWM or I2S/PCM.
|
|
dmaBuf *videocore.Mem // Set when DMA is used for PWM or I2S/PCM.
|
|
}
|
|
|
|
// String implements conn.Resource.
|
|
func (p *Pin) String() string {
|
|
return p.name
|
|
}
|
|
|
|
// Halt implements conn.Resource.
|
|
//
|
|
// If the pin is running a clock, PWM or waiting for edges, it is halted.
|
|
//
|
|
// In the case of clock or PWM, all pins with this clock source are also
|
|
// disabled.
|
|
func (p *Pin) Halt() error {
|
|
if p.usingEdge {
|
|
if err := p.sysfsPin.Halt(); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
p.usingEdge = false
|
|
}
|
|
return p.haltClock()
|
|
}
|
|
|
|
// Name implements pin.Pin.
|
|
func (p *Pin) Name() string {
|
|
return p.name
|
|
}
|
|
|
|
// Number implements pin.Pin.
|
|
//
|
|
// This is the GPIO number, not the pin number on a header.
|
|
func (p *Pin) Number() int {
|
|
return p.number
|
|
}
|
|
|
|
// Function implements pin.Pin.
|
|
func (p *Pin) Function() string {
|
|
return string(p.Func())
|
|
}
|
|
|
|
// Func implements pin.PinFunc.
|
|
func (p *Pin) Func() pin.Func {
|
|
if drvGPIO.gpioMemory == nil {
|
|
if p.sysfsPin == nil {
|
|
return pin.FuncNone
|
|
}
|
|
return p.sysfsPin.Func()
|
|
}
|
|
switch f := p.function(); f {
|
|
case in:
|
|
if p.FastRead() {
|
|
return gpio.IN_HIGH
|
|
}
|
|
return gpio.IN_LOW
|
|
case out:
|
|
if p.FastRead() {
|
|
return gpio.OUT_HIGH
|
|
}
|
|
return gpio.OUT_LOW
|
|
case alt0:
|
|
if s := mapping[p.number][0]; len(s) != 0 {
|
|
return s
|
|
}
|
|
return pin.Func("ALT0")
|
|
case alt1:
|
|
if s := mapping[p.number][1]; len(s) != 0 {
|
|
return s
|
|
}
|
|
return pin.Func("ALT1")
|
|
case alt2:
|
|
if s := mapping[p.number][2]; len(s) != 0 {
|
|
return s
|
|
}
|
|
return pin.Func("ALT2")
|
|
case alt3:
|
|
if s := mapping[p.number][3]; len(s) != 0 {
|
|
return s
|
|
}
|
|
return pin.Func("ALT3")
|
|
case alt4:
|
|
if s := mapping[p.number][4]; len(s) != 0 {
|
|
return s
|
|
}
|
|
return pin.Func("ALT4")
|
|
case alt5:
|
|
if s := mapping[p.number][5]; len(s) != 0 {
|
|
return s
|
|
}
|
|
return pin.Func("ALT5")
|
|
default:
|
|
return pin.FuncNone
|
|
}
|
|
}
|
|
|
|
// SupportedFuncs implements pin.PinFunc.
|
|
func (p *Pin) SupportedFuncs() []pin.Func {
|
|
f := make([]pin.Func, 0, 2+4)
|
|
f = append(f, gpio.IN, gpio.OUT)
|
|
for _, m := range mapping[p.number] {
|
|
if m != "" {
|
|
f = append(f, m)
|
|
}
|
|
}
|
|
return f
|
|
}
|
|
|
|
// SetFunc implements pin.PinFunc.
|
|
func (p *Pin) SetFunc(f pin.Func) error {
|
|
if drvGPIO.gpioMemory == nil {
|
|
if p.sysfsPin == nil {
|
|
return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible"))
|
|
}
|
|
return p.sysfsPin.SetFunc(f)
|
|
}
|
|
switch f {
|
|
case gpio.FLOAT:
|
|
return p.In(gpio.Float, gpio.NoEdge)
|
|
case gpio.IN:
|
|
return p.In(gpio.PullNoChange, gpio.NoEdge)
|
|
case gpio.IN_LOW:
|
|
return p.In(gpio.PullDown, gpio.NoEdge)
|
|
case gpio.IN_HIGH:
|
|
return p.In(gpio.PullUp, gpio.NoEdge)
|
|
case gpio.OUT_HIGH:
|
|
return p.Out(gpio.High)
|
|
case gpio.OUT_LOW:
|
|
return p.Out(gpio.Low)
|
|
default:
|
|
isGeneral := f == f.Generalize()
|
|
for i, m := range mapping[p.number] {
|
|
if m == f || (isGeneral && m.Generalize() == f) {
|
|
if err := p.Halt(); err != nil {
|
|
return err
|
|
}
|
|
switch i {
|
|
case 0:
|
|
p.setFunction(alt0)
|
|
case 1:
|
|
p.setFunction(alt1)
|
|
case 2:
|
|
p.setFunction(alt2)
|
|
case 3:
|
|
p.setFunction(alt3)
|
|
case 4:
|
|
p.setFunction(alt4)
|
|
case 5:
|
|
p.setFunction(alt5)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
return p.wrap(errors.New("unsupported function"))
|
|
}
|
|
}
|
|
|
|
// In implements gpio.PinIn.
|
|
//
|
|
// Specifying a value for pull other than gpio.PullNoChange causes this
|
|
// function to be slightly slower (about 1ms).
|
|
//
|
|
// For pull down, the resistor is 50KOhm~60kOhm
|
|
// For pull up, the resistor is 50kOhm~65kOhm
|
|
//
|
|
// The pull resistor stays set even after the processor shuts down. It is not
|
|
// possible to 'read back' what value was specified for each pin.
|
|
//
|
|
// Will fail if requesting to change a pin that is set to special functionality.
|
|
//
|
|
// Using edge detection requires opening a gpio sysfs file handle. On Raspbian,
|
|
// make sure the user is member of group 'gpio'. The pin will be exported at
|
|
// /sys/class/gpio/gpio*/. Note that the pin will not be unexported at
|
|
// shutdown.
|
|
//
|
|
// For edge detection, the processor samples the input at its CPU clock rate
|
|
// and looks for '011' to rising and '100' for falling detection to avoid
|
|
// glitches. Because gpio sysfs is used, the latency is unpredictable.
|
|
func (p *Pin) In(pull gpio.Pull, edge gpio.Edge) error {
|
|
if p.usingEdge && edge == gpio.NoEdge {
|
|
if err := p.sysfsPin.Halt(); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
}
|
|
if drvGPIO.gpioMemory == nil {
|
|
if p.sysfsPin == nil {
|
|
return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible"))
|
|
}
|
|
if pull != gpio.PullNoChange {
|
|
return p.wrap(errors.New("pull cannot be used when subsystem gpiomem not initialized"))
|
|
}
|
|
if err := p.sysfsPin.In(pull, edge); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
p.usingEdge = edge != gpio.NoEdge
|
|
return nil
|
|
}
|
|
if err := p.haltClock(); err != nil {
|
|
return err
|
|
}
|
|
p.setFunction(in)
|
|
if pull != gpio.PullNoChange {
|
|
// Changing pull resistor requires a specific dance as described at
|
|
// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
|
|
// page 101.
|
|
// However, BCM2711 uses a simpler way of setting pull resistors, reference at
|
|
// https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf
|
|
// page 65 and 73 ~ 76.
|
|
|
|
// If we are running on a newer chip such as BCM2711, set Pull directly.
|
|
if !drvGPIO.useLegacyPull {
|
|
// GPIO_PUP_PDN_CNTRL_REG0 for GPIO0-15
|
|
// GPIO_PUP_PDN_CNTRL_REG1 for GPIO16-31
|
|
// GPIO_PUP_PDN_CNTRL_REG2 for GPIO32-47
|
|
// GPIO_PUP_PDN_CNTRL_REG3 for GPIO48-57
|
|
offset := p.number / 16
|
|
// Check page 94.
|
|
// Resistor Select for GPIOXX
|
|
// 00 = No resistor is selected
|
|
// 01 = Pull up resistor is selected
|
|
// 10 = Pull down resistor is selected
|
|
// 11 = Reserved
|
|
var pullState uint32
|
|
switch pull {
|
|
case gpio.PullDown:
|
|
pullState = 2
|
|
case gpio.PullUp:
|
|
pullState = 1
|
|
case gpio.Float:
|
|
pullState = 0
|
|
}
|
|
|
|
bitOffset := 2 * uint(p.number%16)
|
|
previous := drvGPIO.gpioMemory.pullRegister[offset] & ^(3 << bitOffset)
|
|
drvGPIO.gpioMemory.pullRegister[offset] = previous | (pullState << bitOffset)
|
|
} else {
|
|
// Set Pull
|
|
switch pull {
|
|
case gpio.PullDown:
|
|
drvGPIO.gpioMemory.pullEnable = 1
|
|
case gpio.PullUp:
|
|
drvGPIO.gpioMemory.pullEnable = 2
|
|
case gpio.Float:
|
|
drvGPIO.gpioMemory.pullEnable = 0
|
|
}
|
|
|
|
// Datasheet states caller needs to sleep 150 cycles.
|
|
sleep150cycles()
|
|
offset := p.number / 32
|
|
drvGPIO.gpioMemory.pullEnableClock[offset] = 1 << uint(p.number%32)
|
|
|
|
sleep150cycles()
|
|
drvGPIO.gpioMemory.pullEnable = 0
|
|
drvGPIO.gpioMemory.pullEnableClock[offset] = 0
|
|
}
|
|
}
|
|
if edge != gpio.NoEdge {
|
|
if p.sysfsPin == nil {
|
|
return p.wrap(fmt.Errorf("pin %d is not exported by sysfs", p.number))
|
|
}
|
|
// This resets pending edges.
|
|
if err := p.sysfsPin.In(gpio.PullNoChange, edge); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
p.usingEdge = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Read implements gpio.PinIn.
|
|
//
|
|
// This function is fast. It works even if the pin is set as output.
|
|
func (p *Pin) Read() gpio.Level {
|
|
if drvGPIO.gpioMemory == nil {
|
|
if p.sysfsPin == nil {
|
|
return gpio.Low
|
|
}
|
|
return p.sysfsPin.Read()
|
|
}
|
|
if p.number < 32 {
|
|
// Important: do not remove the &31 here even if not necessary. Testing
|
|
// showed that it slows down the performance by several percents.
|
|
return gpio.Level((drvGPIO.gpioMemory.level[0] & (1 << uint(p.number&31))) != 0)
|
|
}
|
|
return gpio.Level((drvGPIO.gpioMemory.level[1] & (1 << uint(p.number&31))) != 0)
|
|
}
|
|
|
|
// FastRead return the current pin level without any error checking.
|
|
//
|
|
// This function is very fast. It works even if the pin is set as output.
|
|
func (p *Pin) FastRead() gpio.Level {
|
|
if p.number < 32 {
|
|
// Important: do not remove the &31 here even if not necessary. Testing
|
|
// showed that it slows down the performance by several percents.
|
|
return gpio.Level((drvGPIO.gpioMemory.level[0] & (1 << uint(p.number&31))) != 0)
|
|
}
|
|
return gpio.Level((drvGPIO.gpioMemory.level[1] & (1 << uint(p.number&31))) != 0)
|
|
}
|
|
|
|
// WaitForEdge implements gpio.PinIn.
|
|
func (p *Pin) WaitForEdge(timeout time.Duration) bool {
|
|
if p.sysfsPin != nil {
|
|
return p.sysfsPin.WaitForEdge(timeout)
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Pull implements gpio.PinIn.
|
|
//
|
|
// bcm2711/bcm2838 support querying the pull resistor of all GPIO pins. Prior
|
|
// to it, bcm283x doesn't support querying the pull resistor of any GPIO pin.
|
|
func (p *Pin) Pull() gpio.Pull {
|
|
// sysfs does not have the capability to read pull resistor.
|
|
if drvGPIO.gpioMemory != nil {
|
|
if drvGPIO.useLegacyPull {
|
|
// TODO(maruel): The best that could be added is to cache the last set value
|
|
// and return it.
|
|
return gpio.PullNoChange
|
|
}
|
|
offset := p.number / 16
|
|
pullState := (drvGPIO.gpioMemory.pullRegister[offset] >> uint((p.number%16)<<1)) % 4
|
|
switch pullState {
|
|
case 0:
|
|
return gpio.Float
|
|
case 1:
|
|
return gpio.PullUp
|
|
case 2:
|
|
return gpio.PullDown
|
|
}
|
|
}
|
|
return gpio.PullNoChange
|
|
}
|
|
|
|
// DefaultPull implements gpio.PinIn.
|
|
//
|
|
// The CPU doesn't return the current pull.
|
|
func (p *Pin) DefaultPull() gpio.Pull {
|
|
return p.defaultPull
|
|
}
|
|
|
|
// Out implements gpio.PinOut.
|
|
//
|
|
// Fails if requesting to change a pin that is set to special functionality.
|
|
func (p *Pin) Out(l gpio.Level) error {
|
|
if drvGPIO.gpioMemory == nil {
|
|
if p.sysfsPin == nil {
|
|
return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible"))
|
|
}
|
|
return p.sysfsPin.Out(l)
|
|
}
|
|
// TODO(maruel): This function call is very costly.
|
|
if err := p.Halt(); err != nil {
|
|
return err
|
|
}
|
|
// Change output before changing mode to not create any glitch.
|
|
p.FastOut(l)
|
|
p.setFunction(out)
|
|
return nil
|
|
}
|
|
|
|
// FastOut sets a pin output level with Absolutely No error checking.
|
|
//
|
|
// Out() Must be called once first before calling FastOut(), otherwise the
|
|
// behavior is undefined. Then FastOut() can be used for minimal CPU overhead
|
|
// to reach Mhz scale bit banging.
|
|
func (p *Pin) FastOut(l gpio.Level) {
|
|
mask := uint32(1) << uint(p.number&31)
|
|
if l == gpio.Low {
|
|
if p.number < 32 {
|
|
drvGPIO.gpioMemory.outputClear[0] = mask
|
|
} else {
|
|
drvGPIO.gpioMemory.outputClear[1] = mask
|
|
}
|
|
} else {
|
|
if p.number < 32 {
|
|
drvGPIO.gpioMemory.outputSet[0] = mask
|
|
} else {
|
|
drvGPIO.gpioMemory.outputSet[1] = mask
|
|
}
|
|
}
|
|
}
|
|
|
|
// BUG(maruel): PWM(): There is no conflict verification when multiple pins are
|
|
// used simultaneously. The last call to PWM() will affect all pins of the same
|
|
// type (CLK0, CLK2, PWM0 or PWM1).
|
|
|
|
// PWM implements gpio.PinOut.
|
|
//
|
|
// It outputs a periodic signal on supported pins without CPU usage.
|
|
//
|
|
// PWM pins
|
|
//
|
|
// PWM0 is exposed on pins 12, 18 and 40. However, PWM0 is used for generating
|
|
// clock for DMA and unavailable for PWM.
|
|
//
|
|
// PWM1 is exposed on pins 13, 19, 41 and 45.
|
|
//
|
|
// PWM1 uses 25Mhz clock source. The frequency must be a divisor of 25Mhz.
|
|
//
|
|
// DMA driven PWM is available for all pins except PWM1 pins, its resolution is
|
|
// 200KHz which is down-sampled from 25MHz clock above. The number of DMA driven
|
|
// PWM is limited.
|
|
//
|
|
// Furthermore, these can only be used if the drive "bcm283x-dma" was loaded.
|
|
// It can only be loaded if the process has root level access.
|
|
//
|
|
// The user must call either Halt(), In(), Out(), PWM(0,..) or
|
|
// PWM(gpio.DutyMax,..) to stop the clock source and DMA engine before exiting
|
|
// the program.
|
|
func (p *Pin) PWM(duty gpio.Duty, freq physic.Frequency) error {
|
|
if duty == 0 {
|
|
return p.Out(gpio.Low)
|
|
} else if duty == gpio.DutyMax {
|
|
return p.Out(gpio.High)
|
|
}
|
|
f := out
|
|
useDMA := false
|
|
switch p.number {
|
|
case 12, 40: // PWM0 alt0: disabled
|
|
useDMA = true
|
|
case 13, 41, 45: // PWM1
|
|
f = alt0
|
|
case 18: // PWM0 alt5: disabled
|
|
useDMA = true
|
|
case 19: // PWM1
|
|
f = alt5
|
|
default:
|
|
useDMA = true
|
|
}
|
|
|
|
// Intentionally check later, so a more informative error is returned on
|
|
// unsupported pins.
|
|
if drvGPIO.gpioMemory == nil {
|
|
return p.wrap(errors.New("subsystem gpiomem not initialized"))
|
|
}
|
|
if drvDMA.pwmMemory == nil || drvDMA.clockMemory == nil {
|
|
return p.wrap(errors.New("bcm283x-dma not initialized; try again as root?"))
|
|
}
|
|
if useDMA {
|
|
if m := drvDMA.pwmDMAFreq / 2; m < freq {
|
|
return p.wrap(fmt.Errorf("frequency must be at most %s", m))
|
|
}
|
|
|
|
// Total cycles in the period
|
|
rng := uint64(drvDMA.pwmDMAFreq / freq)
|
|
// Pulse width cycles
|
|
dat := uint32((rng*uint64(duty) + uint64(gpio.DutyHalf)) / uint64(gpio.DutyMax))
|
|
var err error
|
|
// TODO(simokawa): Reuse DMA buffer if possible.
|
|
if err = p.haltDMA(); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
// Start clock before DMA starts.
|
|
if _, err = setPWMClockSource(); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
if p.dmaCh, p.dmaBuf, err = startPWMbyDMA(p, uint32(rng), dat); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
} else {
|
|
if m := drvDMA.pwmBaseFreq / 2; m < freq {
|
|
return p.wrap(fmt.Errorf("frequency must be at most %s", m))
|
|
}
|
|
// Total cycles in the period
|
|
rng := uint64(drvDMA.pwmBaseFreq / freq)
|
|
// Pulse width cycles
|
|
dat := uint32((rng*uint64(duty) + uint64(gpio.DutyHalf)) / uint64(gpio.DutyMax))
|
|
if _, err := setPWMClockSource(); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
// Bit shift for PWM0 and PWM1
|
|
shift := uint((p.number & 1) * 8)
|
|
if shift == 0 {
|
|
drvDMA.pwmMemory.rng1 = uint32(rng)
|
|
Nanospin(10 * time.Nanosecond)
|
|
drvDMA.pwmMemory.dat1 = uint32(dat)
|
|
} else {
|
|
drvDMA.pwmMemory.rng2 = uint32(rng)
|
|
Nanospin(10 * time.Nanosecond)
|
|
drvDMA.pwmMemory.dat2 = uint32(dat)
|
|
}
|
|
Nanospin(10 * time.Nanosecond)
|
|
old := drvDMA.pwmMemory.ctl
|
|
drvDMA.pwmMemory.ctl = (old & ^(0xff << shift)) | ((pwm1Enable | pwm1MS) << shift)
|
|
}
|
|
p.usingClock = true
|
|
p.setFunction(f)
|
|
return nil
|
|
}
|
|
|
|
// StreamIn implements gpiostream.PinIn.
|
|
//
|
|
// DMA driven StreamOut is available for GPIO0 to GPIO31 pin and the maximum
|
|
// resolution is 200kHz.
|
|
func (p *Pin) StreamIn(pull gpio.Pull, s gpiostream.Stream) error {
|
|
b, ok := s.(*gpiostream.BitStream)
|
|
if !ok {
|
|
return errors.New("bcm283x: other Stream than BitStream are not implemented yet")
|
|
}
|
|
if !b.LSBF {
|
|
return errors.New("bcm283x: MSBF BitStream is not implemented yet")
|
|
}
|
|
if b.Duration() == 0 {
|
|
return errors.New("bcm283x: can't read to empty BitStream")
|
|
}
|
|
if drvGPIO.gpioMemory == nil {
|
|
return p.wrap(errors.New("subsystem gpiomem not initialized"))
|
|
}
|
|
if err := p.In(pull, gpio.NoEdge); err != nil {
|
|
return err
|
|
}
|
|
if err := dmaReadStream(p, b); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// StreamOut implements gpiostream.PinOut.
|
|
//
|
|
// I2S/PCM driven StreamOut is available for GPIO21 pin. The resolution is up to
|
|
// 250MHz.
|
|
//
|
|
// For GPIO0 to GPIO31 except GPIO21 pin, DMA driven StreamOut is available and
|
|
// the maximum resolution is 200kHz.
|
|
func (p *Pin) StreamOut(s gpiostream.Stream) error {
|
|
if drvGPIO.gpioMemory == nil {
|
|
return p.wrap(errors.New("subsystem gpiomem not initialized"))
|
|
}
|
|
if err := p.Out(gpio.Low); err != nil {
|
|
return err
|
|
}
|
|
// If the pin is I2S_DOUT, use PCM for much nicer stream and lower memory
|
|
// usage.
|
|
if p.number == 21 || p.number == 31 {
|
|
alt := alt0
|
|
if p.number == 31 {
|
|
alt = alt2
|
|
}
|
|
p.setFunction(alt)
|
|
if err := dmaWriteStreamPCM(p, s); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
} else if err := dmaWriteStreamEdges(p, s); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Drive returns the configured output current drive strength for this GPIO.
|
|
//
|
|
// The current drive is configurable per GPIO groups: 0~27 and 28~45.
|
|
//
|
|
// The default value for GPIOs 0~27 is 8mA and for GPIOs 28~45 is 16mA.
|
|
//
|
|
// The value is a multiple 2mA between 2mA and 16mA.
|
|
//
|
|
// Can only be used if driver bcm283x-dma was loaded. Otherwise returns 0.
|
|
func (p *Pin) Drive() physic.ElectricCurrent {
|
|
if drvDMA.gpioPadMemory == nil {
|
|
return 0
|
|
}
|
|
var v pad
|
|
if p.number < 28 {
|
|
v = drvDMA.gpioPadMemory.pads0
|
|
} else {
|
|
// GPIO 46~53 are not exposed.
|
|
v = drvDMA.gpioPadMemory.pads1
|
|
}
|
|
switch v & 7 {
|
|
case padDrive2mA:
|
|
return 2 * physic.MilliAmpere
|
|
case padDrive4mA:
|
|
return 4 * physic.MilliAmpere
|
|
case padDrive6mA:
|
|
return 6 * physic.MilliAmpere
|
|
case padDrive8mA:
|
|
return 8 * physic.MilliAmpere
|
|
case padDrive10mA:
|
|
return 10 * physic.MilliAmpere
|
|
case padDrive12mA:
|
|
return 12 * physic.MilliAmpere
|
|
case padDrive14mA:
|
|
return 14 * physic.MilliAmpere
|
|
case padDrive16mA:
|
|
return 16 * physic.MilliAmpere
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// SlewLimit returns true if the output slew is limited to reduce interference.
|
|
//
|
|
// The slew is configurable per GPIO groups: 0~27 and 28~45.
|
|
//
|
|
// The default is true.
|
|
//
|
|
// Can only be used if driver bcm283x-dma was loaded. Otherwise returns false
|
|
// (the default value).
|
|
func (p *Pin) SlewLimit() bool {
|
|
if drvDMA.gpioPadMemory == nil {
|
|
return true
|
|
}
|
|
if p.number < 28 {
|
|
return drvDMA.gpioPadMemory.pads0&padSlewUnlimited == 0
|
|
}
|
|
return drvDMA.gpioPadMemory.pads1&padSlewUnlimited == 0
|
|
}
|
|
|
|
// Hysteresis returns true if the input hysteresis via a Schmitt trigger is
|
|
// enabled.
|
|
//
|
|
// The hysteresis is configurable per GPIO groups: 0~27 and 28~45.
|
|
//
|
|
// The default is true.
|
|
//
|
|
// Can only be used if driver bcm283x-dma was loaded. Otherwise returns true
|
|
// (the default value).
|
|
func (p *Pin) Hysteresis() bool {
|
|
if drvDMA.gpioPadMemory == nil {
|
|
return true
|
|
}
|
|
if p.number < 28 {
|
|
return drvDMA.gpioPadMemory.pads0&padHysteresisEnable != 0
|
|
}
|
|
return drvDMA.gpioPadMemory.pads1&padHysteresisEnable != 0
|
|
}
|
|
|
|
// Internal code.
|
|
|
|
func (p *Pin) haltDMA() error {
|
|
if p.dmaCh != nil {
|
|
p.dmaCh.reset()
|
|
p.dmaCh = nil
|
|
}
|
|
if p.dmaBuf != nil {
|
|
if err := p.dmaBuf.Close(); err != nil {
|
|
return p.wrap(err)
|
|
}
|
|
p.dmaBuf = nil
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// haltClock disables the CLK/PWM clock if used.
|
|
func (p *Pin) haltClock() error {
|
|
if err := p.haltDMA(); err != nil {
|
|
return err
|
|
}
|
|
if !p.usingClock {
|
|
return nil
|
|
}
|
|
p.usingClock = false
|
|
|
|
// Disable PWMx.
|
|
switch p.number {
|
|
// PWM0 is not used.
|
|
case 12, 18, 40:
|
|
// PWM1
|
|
case 13, 19, 41, 45:
|
|
for _, i := range []int{13, 19, 41, 45} {
|
|
if cpuPins[i].usingClock {
|
|
return nil
|
|
}
|
|
}
|
|
shift := uint((p.number & 1) * 8)
|
|
drvDMA.pwmMemory.ctl &= ^(0xff << shift)
|
|
}
|
|
|
|
// Disable PWM clock if nobody use.
|
|
for _, pin := range cpuPins {
|
|
if pin.usingClock {
|
|
return nil
|
|
}
|
|
}
|
|
err := resetPWMClockSource()
|
|
return err
|
|
}
|
|
|
|
// function returns the current GPIO pin function.
|
|
func (p *Pin) function() function {
|
|
if drvGPIO.gpioMemory == nil {
|
|
return alt5
|
|
}
|
|
return function((drvGPIO.gpioMemory.functionSelect[p.number/10] >> uint((p.number%10)*3)) & 7)
|
|
}
|
|
|
|
// setFunction changes the GPIO pin function.
|
|
func (p *Pin) setFunction(f function) {
|
|
off := p.number / 10
|
|
shift := uint(p.number%10) * 3
|
|
drvGPIO.gpioMemory.functionSelect[off] = (drvGPIO.gpioMemory.functionSelect[off] &^ (7 << shift)) | (uint32(f) << shift)
|
|
// If a pin switches from a specific functionality back to GPIO, the alias
|
|
// should be updated. For example both GPIO13 and GPIO19 support PWM1. By
|
|
// default, PWM1 will be associated to GPIO13, even if
|
|
// GPIO19.SetFunc(gpio.PWM) is called.
|
|
// TODO(maruel): pinreg.Unregister()
|
|
// TODO(maruel): pinreg.Register()
|
|
}
|
|
|
|
func (p *Pin) wrap(err error) error {
|
|
return fmt.Errorf("bcm283x-gpio (%s): %v", p, err)
|
|
}
|
|
|
|
//
|
|
|
|
// Each pin can have one of 7 functions.
|
|
const (
|
|
in function = 0
|
|
out function = 1
|
|
alt0 function = 4
|
|
alt1 function = 5
|
|
alt2 function = 6
|
|
alt3 function = 7
|
|
alt4 function = 3
|
|
alt5 function = 2
|
|
)
|
|
|
|
// cpuPins is all the pins as supported by the CPU. There is no guarantee that
|
|
// they are actually connected to anything on the board.
|
|
var cpuPins = []Pin{
|
|
{number: 0, name: "GPIO0", defaultPull: gpio.PullUp},
|
|
{number: 1, name: "GPIO1", defaultPull: gpio.PullUp},
|
|
{number: 2, name: "GPIO2", defaultPull: gpio.PullUp},
|
|
{number: 3, name: "GPIO3", defaultPull: gpio.PullUp},
|
|
{number: 4, name: "GPIO4", defaultPull: gpio.PullUp},
|
|
{number: 5, name: "GPIO5", defaultPull: gpio.PullUp},
|
|
{number: 6, name: "GPIO6", defaultPull: gpio.PullUp},
|
|
{number: 7, name: "GPIO7", defaultPull: gpio.PullUp},
|
|
{number: 8, name: "GPIO8", defaultPull: gpio.PullUp},
|
|
{number: 9, name: "GPIO9", defaultPull: gpio.PullDown},
|
|
{number: 10, name: "GPIO10", defaultPull: gpio.PullDown},
|
|
{number: 11, name: "GPIO11", defaultPull: gpio.PullDown},
|
|
{number: 12, name: "GPIO12", defaultPull: gpio.PullDown},
|
|
{number: 13, name: "GPIO13", defaultPull: gpio.PullDown},
|
|
{number: 14, name: "GPIO14", defaultPull: gpio.PullDown},
|
|
{number: 15, name: "GPIO15", defaultPull: gpio.PullDown},
|
|
{number: 16, name: "GPIO16", defaultPull: gpio.PullDown},
|
|
{number: 17, name: "GPIO17", defaultPull: gpio.PullDown},
|
|
{number: 18, name: "GPIO18", defaultPull: gpio.PullDown},
|
|
{number: 19, name: "GPIO19", defaultPull: gpio.PullDown},
|
|
{number: 20, name: "GPIO20", defaultPull: gpio.PullDown},
|
|
{number: 21, name: "GPIO21", defaultPull: gpio.PullDown},
|
|
{number: 22, name: "GPIO22", defaultPull: gpio.PullDown},
|
|
{number: 23, name: "GPIO23", defaultPull: gpio.PullDown},
|
|
{number: 24, name: "GPIO24", defaultPull: gpio.PullDown},
|
|
{number: 25, name: "GPIO25", defaultPull: gpio.PullDown},
|
|
{number: 26, name: "GPIO26", defaultPull: gpio.PullDown},
|
|
{number: 27, name: "GPIO27", defaultPull: gpio.PullDown},
|
|
{number: 28, name: "GPIO28", defaultPull: gpio.Float},
|
|
{number: 29, name: "GPIO29", defaultPull: gpio.Float},
|
|
{number: 30, name: "GPIO30", defaultPull: gpio.PullDown},
|
|
{number: 31, name: "GPIO31", defaultPull: gpio.PullDown},
|
|
{number: 32, name: "GPIO32", defaultPull: gpio.PullDown},
|
|
{number: 33, name: "GPIO33", defaultPull: gpio.PullDown},
|
|
{number: 34, name: "GPIO34", defaultPull: gpio.PullUp},
|
|
{number: 35, name: "GPIO35", defaultPull: gpio.PullUp},
|
|
{number: 36, name: "GPIO36", defaultPull: gpio.PullUp},
|
|
{number: 37, name: "GPIO37", defaultPull: gpio.PullDown},
|
|
{number: 38, name: "GPIO38", defaultPull: gpio.PullDown},
|
|
{number: 39, name: "GPIO39", defaultPull: gpio.PullDown},
|
|
{number: 40, name: "GPIO40", defaultPull: gpio.PullDown},
|
|
{number: 41, name: "GPIO41", defaultPull: gpio.PullDown},
|
|
{number: 42, name: "GPIO42", defaultPull: gpio.PullDown},
|
|
{number: 43, name: "GPIO43", defaultPull: gpio.PullDown},
|
|
{number: 44, name: "GPIO44", defaultPull: gpio.Float},
|
|
{number: 45, name: "GPIO45", defaultPull: gpio.Float},
|
|
{number: 46, name: "GPIO46", defaultPull: gpio.PullUp},
|
|
}
|
|
|
|
// This excludes the functions in and out.
|
|
var mapping = [][6]pin.Func{
|
|
{"I2C0_SDA"}, // 0
|
|
{"I2C0_SCL"},
|
|
{"I2C1_SDA"},
|
|
{"I2C1_SCL"},
|
|
{"CLK0"},
|
|
{"CLK1"}, // 5
|
|
{"CLK2"},
|
|
{"SPI0_CS1"},
|
|
{"SPI0_CS0"},
|
|
{"SPI0_MISO"},
|
|
{"SPI0_MOSI"}, // 10
|
|
{"SPI0_CLK"},
|
|
{"PWM0"},
|
|
{"PWM1"},
|
|
{"UART0_TX", "", "", "", "", "UART1_TX"},
|
|
{"UART0_RX", "", "", "", "", "UART1_RX"}, // 15
|
|
{"", "", "", "UART0_CTS", "SPI1_CS2", "UART1_CTS"},
|
|
{"", "", "", "UART0_RTS", "SPI1_CS1", "UART1_RTS"},
|
|
{"I2S_SCK", "", "", "", "SPI1_CS0", "PWM0"},
|
|
{"I2S_WS", "", "", "", "SPI1_MISO", "PWM1"},
|
|
{"I2S_DIN", "", "", "", "SPI1_MOSI", "CLK0"}, // 20
|
|
{"I2S_DOUT", "", "", "", "SPI1_CLK", "CLK1"},
|
|
{""},
|
|
{""},
|
|
{""},
|
|
{""}, // 25
|
|
{""},
|
|
{""},
|
|
{"I2C0_SDA", "", "I2S_SCK", "", "", ""},
|
|
{"I2C0_SCL", "", "I2S_WS", "", "", ""},
|
|
{"", "", "I2S_DIN", "UART0_CTS", "", "UART1_CTS"}, // 30
|
|
{"", "", "I2S_DOUT", "UART0_RTS", "", "UART1_RTS"},
|
|
{"CLK0", "", "", "UART0_TX", "", "UART1_TX"},
|
|
{"", "", "", "UART0_RX", "", "UART1_RX"},
|
|
{"CLK0"},
|
|
{"SPI0_CS1"}, // 35
|
|
{"SPI0_CS0", "", "UART0_TX", "", "", ""},
|
|
{"SPI0_MISO", "", "UART0_RX", "", "", ""},
|
|
{"SPI0_MOSI", "", "UART0_RTS", "", "", ""},
|
|
{"SPI0_CLK", "", "UART0_CTS", "", "", ""},
|
|
{"PWM0", "", "", "", "SPI2_MISO", "UART1_TX"}, // 40
|
|
{"PWM1", "", "", "", "SPI2_MOSI", "UART1_RX"},
|
|
{"CLK1", "", "", "", "SPI2_CLK", "UART1_RTS"},
|
|
{"CLK2", "", "", "", "SPI2_CS0", "UART1_CTS"},
|
|
{"CLK1", "I2C0_SDA", "I2C1_SDA", "", "SPI2_CS1", ""},
|
|
{"PWM1", "I2C0_SCL", "I2C1_SCL", "", "SPI2_CS2", ""}, // 45
|
|
{""},
|
|
}
|
|
|
|
// function specifies the active functionality of a pin. The alternative
|
|
// function is GPIO pin dependent.
|
|
type function uint8
|
|
|
|
// Mapping as
|
|
// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
|
|
// pages 90-91.
|
|
// And https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf pages
|
|
// 65 and 73 ~ 76.
|
|
type gpioMap struct {
|
|
// 0x00 RW GPIO Function Select 0 (GPIO0-9)
|
|
// 0x04 RW GPIO Function Select 1 (GPIO10-19)
|
|
// 0x08 RW GPIO Function Select 2 (GPIO20-29)
|
|
// 0x0C RW GPIO Function Select 3 (GPIO30-39)
|
|
// 0x10 RW GPIO Function Select 4 (GPIO40-49)
|
|
// 0x14 RW GPIO Function Select 5 (GPIO50-53)
|
|
functionSelect [6]uint32 // GPFSEL0~GPFSEL5
|
|
// 0x18 - Reserved
|
|
dummy0 uint32
|
|
// 0x1C W GPIO Pin Output Set 0 (GPIO0-31)
|
|
// 0x20 W GPIO Pin Output Set 1 (GPIO32-53)
|
|
outputSet [2]uint32 // GPSET0-GPSET1
|
|
// 0x24 - Reserved
|
|
dummy1 uint32
|
|
// 0x28 W GPIO Pin Output Clear 0 (GPIO0-31)
|
|
// 0x2C W GPIO Pin Output Clear 1 (GPIO32-53)
|
|
outputClear [2]uint32 // GPCLR0-GPCLR1
|
|
// 0x30 - Reserved
|
|
dummy2 uint32
|
|
// 0x34 R GPIO Pin Level 0 (GPIO0-31)
|
|
// 0x38 R GPIO Pin Level 1 (GPIO32-53)
|
|
level [2]uint32 // GPLEV0-GPLEV1
|
|
// 0x3C - Reserved
|
|
dummy3 uint32
|
|
// 0x40 RW GPIO Pin Event Detect Status 0 (GPIO0-31)
|
|
// 0x44 RW GPIO Pin Event Detect Status 1 (GPIO32-53)
|
|
eventDetectStatus [2]uint32 // GPEDS0-GPEDS1
|
|
// 0x48 - Reserved
|
|
dummy4 uint32
|
|
// 0x4C RW GPIO Pin Rising Edge Detect Enable 0 (GPIO0-31)
|
|
// 0x50 RW GPIO Pin Rising Edge Detect Enable 1 (GPIO32-53)
|
|
risingEdgeDetectEnable [2]uint32 // GPREN0-GPREN1
|
|
// 0x54 - Reserved
|
|
dummy5 uint32
|
|
// 0x58 RW GPIO Pin Falling Edge Detect Enable 0 (GPIO0-31)
|
|
// 0x5C RW GPIO Pin Falling Edge Detect Enable 1 (GPIO32-53)
|
|
fallingEdgeDetectEnable [2]uint32 // GPFEN0-GPFEN1
|
|
// 0x60 - Reserved
|
|
dummy6 uint32
|
|
// 0x64 RW GPIO Pin High Detect Enable 0 (GPIO0-31)
|
|
// 0x68 RW GPIO Pin High Detect Enable 1 (GPIO32-53)
|
|
highDetectEnable [2]uint32 // GPHEN0-GPHEN1
|
|
// 0x6C - Reserved
|
|
dummy7 uint32
|
|
// 0x70 RW GPIO Pin Low Detect Enable 0 (GPIO0-31)
|
|
// 0x74 RW GPIO Pin Low Detect Enable 1 (GPIO32-53)
|
|
lowDetectEnable [2]uint32 // GPLEN0-GPLEN1
|
|
// 0x78 - Reserved
|
|
dummy8 uint32
|
|
// 0x7C RW GPIO Pin Async Rising Edge Detect 0 (GPIO0-31)
|
|
// 0x80 RW GPIO Pin Async Rising Edge Detect 1 (GPIO32-53)
|
|
asyncRisingEdgeDetectEnable [2]uint32 // GPAREN0-GPAREN1
|
|
// 0x84 - Reserved
|
|
dummy9 uint32
|
|
// 0x88 RW GPIO Pin Async Falling Edge Detect 0 (GPIO0-31)
|
|
// 0x8C RW GPIO Pin Async Falling Edge Detect 1 (GPIO32-53)
|
|
asyncFallingEdgeDetectEnable [2]uint32 // GPAFEN0-GPAFEN1
|
|
// 0x90 - Reserved
|
|
dummy10 uint32
|
|
// 0x94 RW GPIO Pin Pull-up/down Enable (00=Float, 01=Down, 10=Up)
|
|
pullEnable uint32 // GPPUD
|
|
// 0x98 RW GPIO Pin Pull-up/down Enable Clock 0 (GPIO0-31)
|
|
// 0x9C RW GPIO Pin Pull-up/down Enable Clock 1 (GPIO32-53)
|
|
pullEnableClock [2]uint32 // GPPUDCLK0-GPPUDCLK1
|
|
// 0xA0 - Reserved
|
|
dummy uint32
|
|
// padding
|
|
dummy11 [3]uint32
|
|
// 0xB0 - Test (byte)
|
|
test uint32
|
|
// padding
|
|
dummy12 [12]uint32
|
|
// New in BCM2711
|
|
// 0xE4 RW GPIO Pull-up / Pull-down Register 0 (GPIO0-15)
|
|
// 0xE8 RW GPIO Pull-up / Pull-down Register 1 (GPIO16-31)
|
|
// 0xEC RW GPIO Pull-up / Pull-down Register 2 (GPIO32-47)
|
|
// 0xF0 RW GPIO Pull-up / Pull-down Register 3 (GPIO48-57)
|
|
pullRegister [4]uint32 // GPIO_PUP_PDN_CNTRL_REG0-GPIO_PUP_PDN_CNTRL_REG3
|
|
}
|
|
|
|
// pad defines the settings for a GPIO pad group.
|
|
type pad uint32
|
|
|
|
const (
|
|
padPasswd pad = 0x5A << 24 // Write protection
|
|
padSlewUnlimited pad = 1 << 4 // Output bandwidth limit to reduce bounce.
|
|
padHysteresisEnable pad = 1 << 3 // Schmitt trigger
|
|
padDrive2mA pad = 0
|
|
padDrive4mA pad = 1
|
|
padDrive6mA pad = 2
|
|
padDrive8mA pad = 3
|
|
padDrive10mA pad = 4
|
|
padDrive12mA pad = 5
|
|
padDrive14mA pad = 6
|
|
padDrive16mA pad = 7
|
|
)
|
|
|
|
// set changes the current drive strength for the GPIO pad group.
|
|
//
|
|
// We could disable the schmitt trigger or the slew limit.
|
|
func (p *pad) set(settings pad) {
|
|
*p = padPasswd | settings
|
|
}
|
|
|
|
func toPad(drive physic.ElectricCurrent, slewLimit, hysteresis bool) pad {
|
|
var p pad
|
|
d := int(drive / physic.MilliAmpere)
|
|
switch {
|
|
case d <= 2:
|
|
p = padDrive2mA
|
|
case d <= 4:
|
|
p = padDrive4mA
|
|
case d <= 6:
|
|
p = padDrive6mA
|
|
case d <= 8:
|
|
p = padDrive8mA
|
|
case d <= 10:
|
|
p = padDrive10mA
|
|
case d <= 12:
|
|
p = padDrive12mA
|
|
case d <= 14:
|
|
p = padDrive14mA
|
|
default:
|
|
p = padDrive16mA
|
|
}
|
|
if !slewLimit {
|
|
p |= padSlewUnlimited
|
|
}
|
|
if hysteresis {
|
|
p |= padHysteresisEnable
|
|
}
|
|
return p
|
|
}
|
|
|
|
// Mapping as https://scribd.com/doc/101830961/GPIO-Pads-Control2
|
|
type gpioPadMap struct {
|
|
dummy [11]uint32 // 0x00~0x28
|
|
pads0 pad // 0x2c GPIO 0~27
|
|
pads1 pad // 0x30 GPIO 28~45
|
|
pads2 pad // 0x34 GPIO 46~53
|
|
}
|
|
|
|
func init() {
|
|
GPIO0 = &cpuPins[0]
|
|
GPIO1 = &cpuPins[1]
|
|
GPIO2 = &cpuPins[2]
|
|
GPIO3 = &cpuPins[3]
|
|
GPIO4 = &cpuPins[4]
|
|
GPIO5 = &cpuPins[5]
|
|
GPIO6 = &cpuPins[6]
|
|
GPIO7 = &cpuPins[7]
|
|
GPIO8 = &cpuPins[8]
|
|
GPIO9 = &cpuPins[9]
|
|
GPIO10 = &cpuPins[10]
|
|
GPIO11 = &cpuPins[11]
|
|
GPIO12 = &cpuPins[12]
|
|
GPIO13 = &cpuPins[13]
|
|
GPIO14 = &cpuPins[14]
|
|
GPIO15 = &cpuPins[15]
|
|
GPIO16 = &cpuPins[16]
|
|
GPIO17 = &cpuPins[17]
|
|
GPIO18 = &cpuPins[18]
|
|
GPIO19 = &cpuPins[19]
|
|
GPIO20 = &cpuPins[20]
|
|
GPIO21 = &cpuPins[21]
|
|
GPIO22 = &cpuPins[22]
|
|
GPIO23 = &cpuPins[23]
|
|
GPIO24 = &cpuPins[24]
|
|
GPIO25 = &cpuPins[25]
|
|
GPIO26 = &cpuPins[26]
|
|
GPIO27 = &cpuPins[27]
|
|
GPIO28 = &cpuPins[28]
|
|
GPIO29 = &cpuPins[29]
|
|
GPIO30 = &cpuPins[30]
|
|
GPIO31 = &cpuPins[31]
|
|
GPIO32 = &cpuPins[32]
|
|
GPIO33 = &cpuPins[33]
|
|
GPIO34 = &cpuPins[34]
|
|
GPIO35 = &cpuPins[35]
|
|
GPIO36 = &cpuPins[36]
|
|
GPIO37 = &cpuPins[37]
|
|
GPIO38 = &cpuPins[38]
|
|
GPIO39 = &cpuPins[39]
|
|
GPIO40 = &cpuPins[40]
|
|
GPIO41 = &cpuPins[41]
|
|
GPIO42 = &cpuPins[42]
|
|
GPIO43 = &cpuPins[43]
|
|
GPIO44 = &cpuPins[44]
|
|
GPIO45 = &cpuPins[45]
|
|
GPIO46 = &cpuPins[46]
|
|
}
|
|
|
|
// Changing pull resistor require a 150 cycles sleep.
|
|
//
|
|
// Do not inline so the temporary value is not optimized out.
|
|
//
|
|
//go:noinline
|
|
func sleep150cycles() uint32 {
|
|
// Do not call into any kernel function, since this causes a high chance of
|
|
// being preempted.
|
|
// Abuse the fact that gpioMemory is uncached memory.
|
|
// TODO(maruel): No idea if this is too much or enough.
|
|
var out uint32
|
|
for i := 0; i < 150; i++ {
|
|
out += drvGPIO.gpioMemory.functionSelect[0]
|
|
}
|
|
return out
|
|
}
|
|
|
|
// driverGPIO implements periph.Driver.
|
|
type driverGPIO struct {
|
|
// baseAddr is the base for all the CPU registers.
|
|
//
|
|
// It is initialized by driverGPIO.Init().
|
|
baseAddr uint32
|
|
// dramBus is high bits to address uncached memory. See virtToUncachedPhys()
|
|
// in dma.go.
|
|
dramBus uint32
|
|
// gpioMemory is the memory map of the CPU GPIO registers.
|
|
gpioMemory *gpioMap
|
|
// gpioBaseAddr is needed for DMA transfers.
|
|
gpioBaseAddr uint32
|
|
// useLegacyPull is set when the old slow pull resistor setup method before
|
|
// bcm2711 must be used.
|
|
useLegacyPull bool
|
|
}
|
|
|
|
func (d *driverGPIO) Close() {
|
|
d.baseAddr = 0
|
|
d.dramBus = 0
|
|
d.gpioMemory = nil
|
|
d.gpioBaseAddr = 0
|
|
}
|
|
|
|
func (d *driverGPIO) String() string {
|
|
return "bcm283x-gpio"
|
|
}
|
|
|
|
func (d *driverGPIO) Prerequisites() []string {
|
|
return nil
|
|
}
|
|
|
|
func (d *driverGPIO) After() []string {
|
|
return []string{"sysfs-gpio"}
|
|
}
|
|
|
|
func (d *driverGPIO) Init() (bool, error) {
|
|
if !Present() {
|
|
return false, errors.New("bcm283x CPU not detected")
|
|
}
|
|
// It's kind of messy, some report bcm283x while others show bcm27xx.
|
|
// Let's play safe here.
|
|
dTCompatible := strings.Join(distro.DTCompatible(), " ")
|
|
// Reference: https://www.raspberrypi.org/documentation/hardware/raspberrypi/peripheral_addresses.md
|
|
if strings.Contains(dTCompatible, "bcm2708") ||
|
|
strings.Contains(dTCompatible, "bcm2835") {
|
|
// RPi0/1.
|
|
d.baseAddr = 0x20000000
|
|
d.dramBus = 0x40000000
|
|
d.useLegacyPull = true
|
|
} else if strings.Contains(dTCompatible, "bcm2709") ||
|
|
strings.Contains(dTCompatible, "bcm2836") ||
|
|
strings.Contains(dTCompatible, "bcm2710") ||
|
|
strings.Contains(dTCompatible, "bcm2837") {
|
|
// RPi2+
|
|
d.baseAddr = 0x3F000000
|
|
d.dramBus = 0xC0000000
|
|
d.useLegacyPull = true
|
|
} else {
|
|
// RPi4B+
|
|
d.baseAddr = 0xFE000000
|
|
d.dramBus = 0xC0000000
|
|
// BCM2711 (and perhaps future versions?) uses a simpler way to
|
|
// setup internal pull resistors.
|
|
d.useLegacyPull = false
|
|
}
|
|
// Page 6.
|
|
// Virtual addresses in kernel mode will range between 0xC0000000 and
|
|
// 0xEFFFFFFF.
|
|
// Virtual addresses in user mode (i.e. seen by processes running in ARM
|
|
// Linux) will range between 0x00000000 and 0xBFFFFFFF.
|
|
// Peripherals (at physical address 0x20000000 on) are mapped into the kernel
|
|
// virtual address space starting at address 0xF2000000. Thus a peripheral
|
|
// advertised here at bus address 0x7Ennnnnn is available in the ARM kenel at
|
|
// virtual address 0xF2nnnnnn.
|
|
d.gpioBaseAddr = d.baseAddr + 0x200000
|
|
|
|
// Mark the right pins as available even if the memory map fails so they can
|
|
// callback to sysfs.Pins.
|
|
functions := map[pin.Func]struct{}{}
|
|
for i := range cpuPins {
|
|
name := cpuPins[i].name
|
|
num := strconv.Itoa(cpuPins[i].number)
|
|
|
|
// Initializes the sysfs corresponding pin right away.
|
|
cpuPins[i].sysfsPin = sysfs.Pins[cpuPins[i].number]
|
|
|
|
// Unregister the pin if already registered. This happens with sysfs-gpio.
|
|
// Do not error on it, since sysfs-gpio may have failed to load.
|
|
_ = gpioreg.Unregister(name)
|
|
_ = gpioreg.Unregister(num)
|
|
|
|
if err := gpioreg.Register(&cpuPins[i]); err != nil {
|
|
return true, err
|
|
}
|
|
if err := gpioreg.RegisterAlias(num, name); err != nil {
|
|
return true, err
|
|
}
|
|
switch f := cpuPins[i].Func(); f {
|
|
case gpio.IN, gpio.OUT, gpio.IN_LOW, gpio.IN_HIGH, gpio.OUT_LOW, gpio.OUT_HIGH, pin.FuncNone:
|
|
default:
|
|
// Registering the same alias twice fails. This can happen if two pins
|
|
// are configured with the same function. For example both pin #12, #18
|
|
// and #40 could be configured to work as PWM0.
|
|
if _, ok := functions[f]; !ok {
|
|
functions[f] = struct{}{}
|
|
if err := gpioreg.RegisterAlias(string(f), name); err != nil {
|
|
return true, err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now do a second loop but do the alternate functions.
|
|
for i := range cpuPins {
|
|
for _, f := range cpuPins[i].SupportedFuncs() {
|
|
switch f {
|
|
case gpio.IN, gpio.OUT:
|
|
default:
|
|
if _, ok := functions[f]; !ok {
|
|
functions[f] = struct{}{}
|
|
if err := gpioreg.RegisterAlias(string(f), cpuPins[i].name); err != nil {
|
|
return true, err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Register some BCM-documentation specific names.
|
|
// Do not do UARTx_TXD/RXD nor the PCM_xxx ones.
|
|
aliases := [][2]string{
|
|
{"GPCLK0", "CLK0"},
|
|
{"GPCLK1", "CLK1"},
|
|
{"GPCLK2", "CLK2"},
|
|
{"PWM0_OUT", "PWM0"},
|
|
{"PWM1_OUT", "PWM1"},
|
|
}
|
|
for _, a := range aliases {
|
|
if err := gpioreg.RegisterAlias(a[0], a[1]); err != nil {
|
|
return true, err
|
|
}
|
|
}
|
|
|
|
m, err := pmem.MapGPIO()
|
|
if err != nil {
|
|
// Try without /dev/gpiomem. This is the case of not running on Raspbian or
|
|
// raspbian before Jessie. This requires running as root.
|
|
var err2 error
|
|
m, err2 = pmem.Map(uint64(d.gpioBaseAddr), 4096)
|
|
var err error
|
|
if err2 != nil {
|
|
if distro.IsRaspbian() {
|
|
// Raspbian specific error code to help guide the user to troubleshoot
|
|
// the problems.
|
|
if os.IsNotExist(err) && os.IsPermission(err2) {
|
|
return true, fmt.Errorf("/dev/gpiomem wasn't found; please upgrade to Raspbian Jessie or run as root")
|
|
}
|
|
}
|
|
if os.IsPermission(err2) {
|
|
return true, fmt.Errorf("need more access, try as root: %v", err)
|
|
}
|
|
return true, err
|
|
}
|
|
}
|
|
if err := m.AsPOD(&d.gpioMemory); err != nil {
|
|
return true, err
|
|
}
|
|
|
|
return true, sysfs.I2CSetSpeedHook(setSpeed)
|
|
}
|
|
|
|
func setSpeed(f physic.Frequency) error {
|
|
// Writing to "/sys/module/i2c_bcm2708/parameters/baudrate" was confirmed to
|
|
// not work.
|
|
// modprobe hangs when a bus is opened, so this must be called *before* the
|
|
// bus is opened.
|
|
// TL;DR: we can't do anything here.
|
|
/*
|
|
if err := exec.Command("modprobe", "-r", "i2c_bcm2708").Run(); err != nil {
|
|
return fmt.Errorf("bcm283x: failed to unload driver i2c_bcm2708: %v", err)
|
|
}
|
|
if err := exec.Command("modprobe", "i2c_bcm2708", "baudrate=600000"); err != nil {
|
|
return fmt.Errorf("bcm283x: failed to reload driver i2c_bcm2708: %v", err)
|
|
}
|
|
*/
|
|
return errors.New("bcm283x: to change the I²C bus speed, please refer to https://periph.io/platform/raspberrypi/#i²c")
|
|
}
|
|
|
|
func init() {
|
|
if isArm {
|
|
driverreg.MustRegister(&drvGPIO)
|
|
}
|
|
}
|
|
|
|
var drvGPIO driverGPIO
|
|
|
|
var _ gpio.PinIO = &Pin{}
|
|
var _ gpio.PinIn = &Pin{}
|
|
var _ gpio.PinOut = &Pin{}
|
|
var _ gpiostream.PinIn = &Pin{}
|
|
var _ gpiostream.PinOut = &Pin{}
|
|
var _ pin.PinFunc = &Pin{}
|