// 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://github.com/raspberrypi/documentation/blob/master/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf
		// page 84 and 95 ~ 98.

		// 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
			}
			drvGPIO.gpioMemory.pullRegister[offset] = pullState << uint((p.number%16)<<1)
		} 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://github.com/raspberrypi/documentation/blob/master/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf
// pages 83-84.
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{}
