// Copyright 2016 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.

// This file contains the definitions of all possible generic Allwinner pins and their
// implementation using a combination of sysfs and memory-mapped I/O.

package allwinner

import (
	"errors"
	"fmt"
	"os"
	"path"
	"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/physic"
	"periph.io/x/conn/v3/pin"
	"periph.io/x/host/v3/pmem"
	"periph.io/x/host/v3/sysfs"
)

// List of all known pins. These global variables can be used directly.
//
// The supported functionality of each gpio differs between CPUs. For example
// the R8 has the LCD-DE signal on gpio PD25 but the A64 has it on PD19.
//
// The availability of each gpio differs between CPUs. For example the R8 has
// 19 pins in the group PB but the A64 only has 10.
//
// So make sure to read the datasheet for the exact right CPU.
var (
	PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PA10, PA11, PA12, PA13, PA14, PA15, PA16, PA17                                                             *Pin
	PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15, PB16, PB17, PB18, PB19, PB20, PB21, PB22, PB23                         *Pin
	PC0, PC1, PC2, PC3, PC4, PC5, PC6, PC7, PC8, PC9, PC10, PC11, PC12, PC13, PC14, PC15, PC16, PC17, PC18, PC19, PC20, PC21, PC22, PC23, PC24                   *Pin
	PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7, PD8, PD9, PD10, PD11, PD12, PD13, PD14, PD15, PD16, PD17, PD18, PD19, PD20, PD21, PD22, PD23, PD24, PD25, PD26, PD27 *Pin
	PE0, PE1, PE2, PE3, PE4, PE5, PE6, PE7, PE8, PE9, PE10, PE11, PE12, PE13, PE14, PE15, PE16, PE17                                                             *Pin
	PF0, PF1, PF2, PF3, PF4, PF5, PF6                                                                                                                            *Pin
	PG0, PG1, PG2, PG3, PG4, PG5, PG6, PG7, PG8, PG9, PG10, PG11, PG12, PG13                                                                                     *Pin
	PH0, PH1, PH2, PH3, PH4, PH5, PH6, PH7, PH8, PH9, PH10, PH11, PH12, PH13, PH14, PH15, PH16, PH17, PH18, PH19, PH20, PH21, PH22, PH23, PH24, PH25, PH26, PH27 *Pin
	PI0, PI1, PI2, PI3, PI4, PI5, PI6, PI7, PI8, PI9, PI10, PI11, PI12, PI13, PI14, PI15, PI16, PI17, PI18, PI19, PI20, PI21                                     *Pin
)

// Pin implements the gpio.PinIO interface for generic Allwinner CPU pins using
// memory mapping for gpio in/out functionality.
type Pin struct {
	// Immutable.
	group       uint8     // as per register offset calculation
	offset      uint8     // as per register offset calculation
	name        string    // name as per datasheet
	defaultPull gpio.Pull // default pull at startup

	// Immutable after driver initialization.
	altFunc     [5]pin.Func // alternate functions
	sysfsPin    *sysfs.Pin  // Set to the corresponding sysfs.Pin, if any.
	available   bool        // Set when the pin is available on this CPU architecture.
	supportEdge bool        // Set when the pin supports interrupt based edge detection.

	// Mutable.
	usingEdge bool // Set when edge detection is enabled.
}

// String implements conn.Resource.
//
// It returns the pin name and number, ex: "PB5(37)".
func (p *Pin) String() string {
	return fmt.Sprintf("%s(%d)", p.name, p.Number())
}

// Halt implements conn.Resource.
//
// It stops edge detection if enabled.
func (p *Pin) Halt() error {
	if p.usingEdge {
		if err := p.sysfsPin.Halt(); err != nil {
			return p.wrap(err)
		}
		p.usingEdge = false
	}
	return nil
}

// Name implements pin.Pin.
//
// It returns the pin name, ex: "PB5".
func (p *Pin) Name() string {
	return p.name
}

// Number implements pin.Pin.
//
// It returns the GPIO pin number as represented by gpio sysfs.
func (p *Pin) Number() int {
	return int(p.group)*32 + int(p.offset)
}

// Function implements pin.Pin.
func (p *Pin) Function() string {
	return string(p.Func())
}

// Func implements pin.PinFunc.
func (p *Pin) Func() pin.Func {
	if !p.available {
		return pin.FuncNone
	}
	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 alt1:
		if p.altFunc[0] != "" {
			return pin.Func(p.altFunc[0])
		}
		return pin.Func("ALT1")
	case alt2:
		if p.altFunc[1] != "" {
			return pin.Func(p.altFunc[1])
		}
		return pin.Func("ALT2")
	case alt3:
		if p.altFunc[2] != "" {
			return pin.Func(p.altFunc[2])
		}
		return pin.Func("ALT3")
	case alt4:
		if p.altFunc[3] != "" {
			if strings.Contains(string(p.altFunc[3]), "EINT") {
				// It's an input supporting interrupts.
				if p.FastRead() {
					return gpio.IN_HIGH
				}
				return gpio.IN_LOW
			}
			return pin.Func(p.altFunc[3])
		}
		return pin.Func("ALT4")
	case alt5:
		if p.altFunc[4] != "" {
			if strings.Contains(string(p.altFunc[4]), "EINT") {
				// It's an input supporting interrupts.
				if p.FastRead() {
					return gpio.IN_HIGH
				}
				return gpio.IN_LOW
			}
			return pin.Func(p.altFunc[4])
		}
		return pin.Func("ALT5")
	case disabled:
		return pin.FuncNone
	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 p.altFunc {
		if m != pin.FuncNone && !strings.Contains(string(m), "EINT") {
			f = append(f, m)
		}
	}
	return f
}

// SetFunc implements pin.PinFunc.
func (p *Pin) SetFunc(f pin.Func) error {
	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 p.altFunc {
			if m == f || (isGeneral && m.Generalize() == f) {
				if err := p.Halt(); err != nil {
					return err
				}
				switch i {
				case 0:
					p.setFunction(alt1)
				case 1:
					p.setFunction(alt2)
				case 2:
					p.setFunction(alt3)
				case 3:
					p.setFunction(alt4)
				case 4:
					p.setFunction(alt5)
				}
				return nil
			}
		}
		return p.wrap(errors.New("unsupported function"))
	}
}

// In implements gpio.PinIn.
//
// It sets the pin direction to input and optionally enables a pull-up/down
// resistor as well as edge detection.
//
// Not all pins support edge detection on Allwinner processors!
//
// Edge detection requires opening a gpio sysfs file handle. The pin will be
// exported at /sys/class/gpio/gpio*/. Note that the pin will not be unexported
// at shutdown.
func (p *Pin) In(pull gpio.Pull, edge gpio.Edge) error {
	if !p.available {
		// We do not want the error message about uninitialized system.
		return p.wrap(errors.New("not available on this CPU architecture"))
	}
	if edge != gpio.NoEdge && !p.supportEdge {
		return p.wrap(errors.New("edge detection is not supported on this pin"))
	}
	if p.usingEdge && edge == gpio.NoEdge {
		if err := p.sysfsPin.Halt(); err != nil {
			return p.wrap(err)
		}
		p.usingEdge = false
	}
	if drvGPIO.gpioMemory == nil {
		if p.sysfsPin == nil {
			return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible; try running as root?"))
		}
		if pull != gpio.PullNoChange {
			return p.wrap(errors.New("pull cannot be used when subsystem gpiomem not initialized; try running as root?"))
		}
		if err := p.sysfsPin.In(pull, edge); err != nil {
			return p.wrap(err)
		}
		p.usingEdge = edge != gpio.NoEdge
		return nil
	}
	p.setFunction(in)
	if pull != gpio.PullNoChange {
		off := p.offset / 16
		shift := 2 * (p.offset % 16)
		// Do it in a way that is concurrency safe.
		drvGPIO.gpioMemory.groups[p.group].pull[off] &^= 3 << shift
		switch pull {
		case gpio.PullDown:
			drvGPIO.gpioMemory.groups[p.group].pull[off] = 2 << shift
		case gpio.PullUp:
			drvGPIO.gpioMemory.groups[p.group].pull[off] = 1 << shift
		default:
		}
	}
	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.
//
// It returns the current pin level. This function is fast.
func (p *Pin) Read() gpio.Level {
	if !p.available {
		return gpio.Low
	}
	if drvGPIO.gpioMemory == nil {
		if p.sysfsPin == nil {
			return gpio.Low
		}
		return p.sysfsPin.Read()
	}
	return gpio.Level(drvGPIO.gpioMemory.groups[p.group].data&(1<<p.offset) != 0)
}

// FastRead return the current pin level without any error checking.
//
// This function is very fast.
func (p *Pin) FastRead() gpio.Level {
	return gpio.Level(drvGPIO.gpioMemory.groups[p.group].data&(1<<p.offset) != 0)
}

// WaitForEdge implements gpio.PinIn.
//
// It waits for an edge as previously set using In() or the expiration of a
// timeout.
func (p *Pin) WaitForEdge(timeout time.Duration) bool {
	if p.sysfsPin != nil {
		return p.sysfsPin.WaitForEdge(timeout)
	}
	return false
}

// Pull implements gpio.PinIn.
func (p *Pin) Pull() gpio.Pull {
	if drvGPIO.gpioMemory == nil || !p.available {
		return gpio.PullNoChange
	}
	v := drvGPIO.gpioMemory.groups[p.group].pull[p.offset/16]
	switch (v >> (2 * (p.offset % 16))) & 3 {
	case 0:
		return gpio.Float
	case 1:
		return gpio.PullUp
	case 2:
		return gpio.PullDown
	default:
		// Confused.
		return gpio.PullNoChange
	}
}

// DefaultPull implements gpio.PinIn.
func (p *Pin) DefaultPull() gpio.Pull {
	return p.defaultPull
}

// Out implements gpio.PinOut.
func (p *Pin) Out(l gpio.Level) error {
	if !p.available {
		// We do not want the error message about uninitialized system.
		return p.wrap(errors.New("not available on this CPU architecture"))
	}
	if drvGPIO.gpioMemory == nil {
		if p.sysfsPin != nil {
			return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible; try running as root?"))
		}
		return p.sysfsPin.Out(l)
	}
	// First disable edges.
	if err := p.Halt(); err != nil {
		return err
	}
	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) {
	bit := uint32(1 << p.offset)
	// Pn_DAT  n*0x24+0x10  Port n Data Register (n from 0(A) to 8(I))
	// This is a switch on p.group rather than an index to the groups array for
	// performance reasons: to avoid Go's array bound checking code.
	// See https://periph.io/news/2017/gpio_perf/ for details.
	switch p.group {
	case 0:
		if l {
			drvGPIO.gpioMemory.groups[0].data |= bit
		} else {
			drvGPIO.gpioMemory.groups[0].data &^= bit
		}
	case 1:
		if l {
			drvGPIO.gpioMemory.groups[1].data |= bit
		} else {
			drvGPIO.gpioMemory.groups[1].data &^= bit
		}
	case 2:
		if l {
			drvGPIO.gpioMemory.groups[2].data |= bit
		} else {
			drvGPIO.gpioMemory.groups[2].data &^= bit
		}
	case 3:
		if l {
			drvGPIO.gpioMemory.groups[3].data |= bit
		} else {
			drvGPIO.gpioMemory.groups[3].data &^= bit
		}
	case 4:
		if l {
			drvGPIO.gpioMemory.groups[4].data |= bit
		} else {
			drvGPIO.gpioMemory.groups[4].data &^= bit
		}
	case 5:
		if l {
			drvGPIO.gpioMemory.groups[5].data |= bit
		} else {
			drvGPIO.gpioMemory.groups[5].data &^= bit
		}
	case 6:
		if l {
			drvGPIO.gpioMemory.groups[6].data |= bit
		} else {
			drvGPIO.gpioMemory.groups[6].data &^= bit
		}
	case 7:
		if l {
			drvGPIO.gpioMemory.groups[7].data |= bit
		} else {
			drvGPIO.gpioMemory.groups[7].data &^= bit
		}
	case 8:
		if l {
			drvGPIO.gpioMemory.groups[8].data |= bit
		} else {
			drvGPIO.gpioMemory.groups[8].data &^= bit
		}
	}
}

// PWM implements gpio.PinOut.
func (p *Pin) PWM(gpio.Duty, physic.Frequency) error {
	return p.wrap(errors.New("not available on this CPU architecture"))
}

//

// drive returns the configured output current drive strength for this GPIO.
//
// The value returned by this function is not yet verified to be correct. Use
// with suspicion.
func (p *Pin) drive() physic.ElectricCurrent {
	if drvGPIO.gpioMemory == nil {
		return 0
	}
	// Explanation of the buffer configuration, but doesn't state what's the
	// expected drive strength!
	// http://files.pine64.org/doc/datasheet/pine-h64/Allwinner_H6%20V200_User_Manual_V1.1.pdf
	// Section 3.21.3.4 page 381~382
	//
	// The A64 and H3 datasheets call for 20mA, so it could be reasonable to
	// think that the values are 5mA, 10mA, 15mA, 20mA but we don't know for
	// sure.
	v := (drvGPIO.gpioMemory.groups[p.group].drv[p.offset/16] >> (2 * (p.offset & 15))) & 3
	return physic.ElectricCurrent(v+1) * 5 * physic.MilliAmpere
}

// function returns the current GPIO pin function.
func (p *Pin) function() function {
	if drvGPIO.gpioMemory == nil {
		return disabled
	}
	shift := 4 * (p.offset % 8)
	return function((drvGPIO.gpioMemory.groups[p.group].cfg[p.offset/8] >> shift) & 7)
}

// setFunction changes the GPIO pin function.
func (p *Pin) setFunction(f function) {
	off := p.offset / 8
	shift := 4 * (p.offset % 8)
	mask := uint32(disabled) << shift
	v := (uint32(f) << shift) ^ mask
	// First disable, then setup. This is concurrent safe.
	drvGPIO.gpioMemory.groups[p.group].cfg[off] |= mask
	drvGPIO.gpioMemory.groups[p.group].cfg[off] &^= v
	if p.function() != f {
		panic(f)
	}
}

func (p *Pin) wrap(err error) error {
	return fmt.Errorf("allwinner-gpio (%s): %v", p, err)
}

//

// A64: Page 23~24
// R8: Page 322-334.
// Each pin can have one of 7 functions.
const (
	in       function = 0
	out      function = 1
	alt1     function = 2
	alt2     function = 3
	alt3     function = 4
	alt4     function = 5
	alt5     function = 6 // often interrupt based edge detection as input
	disabled function = 7
)

// cpupins that may be implemented by a generic Allwinner CPU. Not all pins
// will be present on all models and even if the CPU model supports them they
// may not be connected to anything on the board. The net effect is that it may
// look like more pins are available than really are, but trying to get the pin
// list 100% correct on all platforms seems futile, hence periph errs on the
// side of caution.
var cpupins = map[string]*Pin{
	"PA0":  {group: 0, offset: 0, name: "PA0", defaultPull: gpio.Float},
	"PA1":  {group: 0, offset: 1, name: "PA1", defaultPull: gpio.Float},
	"PA2":  {group: 0, offset: 2, name: "PA2", defaultPull: gpio.Float},
	"PA3":  {group: 0, offset: 3, name: "PA3", defaultPull: gpio.Float},
	"PA4":  {group: 0, offset: 4, name: "PA4", defaultPull: gpio.Float},
	"PA5":  {group: 0, offset: 5, name: "PA5", defaultPull: gpio.Float},
	"PA6":  {group: 0, offset: 6, name: "PA6", defaultPull: gpio.Float},
	"PA7":  {group: 0, offset: 7, name: "PA7", defaultPull: gpio.Float},
	"PA8":  {group: 0, offset: 8, name: "PA8", defaultPull: gpio.Float},
	"PA9":  {group: 0, offset: 9, name: "PA9", defaultPull: gpio.Float},
	"PA10": {group: 0, offset: 10, name: "PA10", defaultPull: gpio.Float},
	"PA11": {group: 0, offset: 11, name: "PA11", defaultPull: gpio.Float},
	"PA12": {group: 0, offset: 12, name: "PA12", defaultPull: gpio.Float},
	"PA13": {group: 0, offset: 13, name: "PA13", defaultPull: gpio.Float},
	"PA14": {group: 0, offset: 14, name: "PA14", defaultPull: gpio.Float},
	"PA15": {group: 0, offset: 15, name: "PA15", defaultPull: gpio.Float},
	"PA16": {group: 0, offset: 16, name: "PA16", defaultPull: gpio.Float},
	"PA17": {group: 0, offset: 17, name: "PA17", defaultPull: gpio.Float},
	"PB0":  {group: 1, offset: 0, name: "PB0", defaultPull: gpio.Float},
	"PB1":  {group: 1, offset: 1, name: "PB1", defaultPull: gpio.Float},
	"PB2":  {group: 1, offset: 2, name: "PB2", defaultPull: gpio.Float},
	"PB3":  {group: 1, offset: 3, name: "PB3", defaultPull: gpio.Float},
	"PB4":  {group: 1, offset: 4, name: "PB4", defaultPull: gpio.Float},
	"PB5":  {group: 1, offset: 5, name: "PB5", defaultPull: gpio.Float},
	"PB6":  {group: 1, offset: 6, name: "PB6", defaultPull: gpio.Float},
	"PB7":  {group: 1, offset: 7, name: "PB7", defaultPull: gpio.Float},
	"PB8":  {group: 1, offset: 8, name: "PB8", defaultPull: gpio.Float},
	"PB9":  {group: 1, offset: 9, name: "PB9", defaultPull: gpio.Float},
	"PB10": {group: 1, offset: 10, name: "PB10", defaultPull: gpio.Float},
	"PB11": {group: 1, offset: 11, name: "PB11", defaultPull: gpio.Float},
	"PB12": {group: 1, offset: 12, name: "PB12", defaultPull: gpio.Float},
	"PB13": {group: 1, offset: 13, name: "PB13", defaultPull: gpio.Float},
	"PB14": {group: 1, offset: 14, name: "PB14", defaultPull: gpio.Float},
	"PB15": {group: 1, offset: 15, name: "PB15", defaultPull: gpio.Float},
	"PB16": {group: 1, offset: 16, name: "PB16", defaultPull: gpio.Float},
	"PB17": {group: 1, offset: 17, name: "PB17", defaultPull: gpio.Float},
	"PB18": {group: 1, offset: 18, name: "PB18", defaultPull: gpio.Float},
	"PB19": {group: 1, offset: 19, name: "PB19", defaultPull: gpio.Float},
	"PB20": {group: 1, offset: 20, name: "PB20", defaultPull: gpio.Float},
	"PB21": {group: 1, offset: 21, name: "PB21", defaultPull: gpio.Float},
	"PB22": {group: 1, offset: 22, name: "PB22", defaultPull: gpio.Float},
	"PB23": {group: 1, offset: 23, name: "PB23", defaultPull: gpio.Float},
	"PC0":  {group: 2, offset: 0, name: "PC0", defaultPull: gpio.Float},
	"PC1":  {group: 2, offset: 1, name: "PC1", defaultPull: gpio.Float},
	"PC2":  {group: 2, offset: 2, name: "PC2", defaultPull: gpio.Float},
	"PC3":  {group: 2, offset: 3, name: "PC3", defaultPull: gpio.PullUp},
	"PC4":  {group: 2, offset: 4, name: "PC4", defaultPull: gpio.PullUp},
	"PC5":  {group: 2, offset: 5, name: "PC5", defaultPull: gpio.Float},
	"PC6":  {group: 2, offset: 6, name: "PC6", defaultPull: gpio.PullUp},
	"PC7":  {group: 2, offset: 7, name: "PC7", defaultPull: gpio.PullUp},
	"PC8":  {group: 2, offset: 8, name: "PC8", defaultPull: gpio.Float},
	"PC9":  {group: 2, offset: 9, name: "PC9", defaultPull: gpio.Float},
	"PC10": {group: 2, offset: 10, name: "PC10", defaultPull: gpio.Float},
	"PC11": {group: 2, offset: 11, name: "PC11", defaultPull: gpio.Float},
	"PC12": {group: 2, offset: 12, name: "PC12", defaultPull: gpio.Float},
	"PC13": {group: 2, offset: 13, name: "PC13", defaultPull: gpio.Float},
	"PC14": {group: 2, offset: 14, name: "PC14", defaultPull: gpio.Float},
	"PC15": {group: 2, offset: 15, name: "PC15", defaultPull: gpio.Float},
	"PC16": {group: 2, offset: 16, name: "PC16", defaultPull: gpio.Float},
	"PC17": {group: 2, offset: 17, name: "PC17", defaultPull: gpio.Float},
	"PC18": {group: 2, offset: 18, name: "PC18", defaultPull: gpio.Float},
	"PC19": {group: 2, offset: 19, name: "PC19", defaultPull: gpio.Float},
	"PC20": {group: 2, offset: 20, name: "PC20", defaultPull: gpio.Float},
	"PC21": {group: 2, offset: 21, name: "PC21", defaultPull: gpio.Float},
	"PC22": {group: 2, offset: 22, name: "PC22", defaultPull: gpio.Float},
	"PC23": {group: 2, offset: 23, name: "PC23", defaultPull: gpio.Float},
	"PC24": {group: 2, offset: 24, name: "PC24", defaultPull: gpio.Float},
	"PD0":  {group: 3, offset: 0, name: "PD0", defaultPull: gpio.Float},
	"PD1":  {group: 3, offset: 1, name: "PD1", defaultPull: gpio.Float},
	"PD2":  {group: 3, offset: 2, name: "PD2", defaultPull: gpio.Float},
	"PD3":  {group: 3, offset: 3, name: "PD3", defaultPull: gpio.Float},
	"PD4":  {group: 3, offset: 4, name: "PD4", defaultPull: gpio.Float},
	"PD5":  {group: 3, offset: 5, name: "PD5", defaultPull: gpio.Float},
	"PD6":  {group: 3, offset: 6, name: "PD6", defaultPull: gpio.Float},
	"PD7":  {group: 3, offset: 7, name: "PD7", defaultPull: gpio.Float},
	"PD8":  {group: 3, offset: 8, name: "PD8", defaultPull: gpio.Float},
	"PD9":  {group: 3, offset: 9, name: "PD9", defaultPull: gpio.Float},
	"PD10": {group: 3, offset: 10, name: "PD10", defaultPull: gpio.Float},
	"PD11": {group: 3, offset: 11, name: "PD11", defaultPull: gpio.Float},
	"PD12": {group: 3, offset: 12, name: "PD12", defaultPull: gpio.Float},
	"PD13": {group: 3, offset: 13, name: "PD13", defaultPull: gpio.Float},
	"PD14": {group: 3, offset: 14, name: "PD14", defaultPull: gpio.Float},
	"PD15": {group: 3, offset: 15, name: "PD15", defaultPull: gpio.Float},
	"PD16": {group: 3, offset: 16, name: "PD16", defaultPull: gpio.Float},
	"PD17": {group: 3, offset: 17, name: "PD17", defaultPull: gpio.Float},
	"PD18": {group: 3, offset: 18, name: "PD18", defaultPull: gpio.Float},
	"PD19": {group: 3, offset: 19, name: "PD19", defaultPull: gpio.Float},
	"PD20": {group: 3, offset: 20, name: "PD20", defaultPull: gpio.Float},
	"PD21": {group: 3, offset: 21, name: "PD21", defaultPull: gpio.Float},
	"PD22": {group: 3, offset: 22, name: "PD22", defaultPull: gpio.Float},
	"PD23": {group: 3, offset: 23, name: "PD23", defaultPull: gpio.Float},
	"PD24": {group: 3, offset: 24, name: "PD24", defaultPull: gpio.Float},
	"PD25": {group: 3, offset: 25, name: "PD25", defaultPull: gpio.Float},
	"PD26": {group: 3, offset: 26, name: "PD26", defaultPull: gpio.Float},
	"PD27": {group: 3, offset: 27, name: "PD27", defaultPull: gpio.Float},
	"PE0":  {group: 4, offset: 0, name: "PE0", defaultPull: gpio.Float},
	"PE1":  {group: 4, offset: 1, name: "PE1", defaultPull: gpio.Float},
	"PE2":  {group: 4, offset: 2, name: "PE2", defaultPull: gpio.Float},
	"PE3":  {group: 4, offset: 3, name: "PE3", defaultPull: gpio.Float},
	"PE4":  {group: 4, offset: 4, name: "PE4", defaultPull: gpio.Float},
	"PE5":  {group: 4, offset: 5, name: "PE5", defaultPull: gpio.Float},
	"PE6":  {group: 4, offset: 6, name: "PE6", defaultPull: gpio.Float},
	"PE7":  {group: 4, offset: 7, name: "PE7", defaultPull: gpio.Float},
	"PE8":  {group: 4, offset: 8, name: "PE8", defaultPull: gpio.Float},
	"PE9":  {group: 4, offset: 9, name: "PE9", defaultPull: gpio.Float},
	"PE10": {group: 4, offset: 10, name: "PE10", defaultPull: gpio.Float},
	"PE11": {group: 4, offset: 11, name: "PE11", defaultPull: gpio.Float},
	"PE12": {group: 4, offset: 12, name: "PE12", defaultPull: gpio.Float},
	"PE13": {group: 4, offset: 13, name: "PE13", defaultPull: gpio.Float},
	"PE14": {group: 4, offset: 14, name: "PE14", defaultPull: gpio.Float},
	"PE15": {group: 4, offset: 15, name: "PE15", defaultPull: gpio.Float},
	"PE16": {group: 4, offset: 16, name: "PE16", defaultPull: gpio.Float},
	"PE17": {group: 4, offset: 17, name: "PE17", defaultPull: gpio.Float},
	"PF0":  {group: 5, offset: 0, name: "PF0", defaultPull: gpio.Float},
	"PF1":  {group: 5, offset: 1, name: "PF1", defaultPull: gpio.Float},
	"PF2":  {group: 5, offset: 2, name: "PF2", defaultPull: gpio.Float},
	"PF3":  {group: 5, offset: 3, name: "PF3", defaultPull: gpio.Float},
	"PF4":  {group: 5, offset: 4, name: "PF4", defaultPull: gpio.Float},
	"PF5":  {group: 5, offset: 5, name: "PF5", defaultPull: gpio.Float},
	"PF6":  {group: 5, offset: 6, name: "PF6", defaultPull: gpio.Float},
	"PG0":  {group: 6, offset: 0, name: "PG0", defaultPull: gpio.Float},
	"PG1":  {group: 6, offset: 1, name: "PG1", defaultPull: gpio.Float},
	"PG2":  {group: 6, offset: 2, name: "PG2", defaultPull: gpio.Float},
	"PG3":  {group: 6, offset: 3, name: "PG3", defaultPull: gpio.Float},
	"PG4":  {group: 6, offset: 4, name: "PG4", defaultPull: gpio.Float},
	"PG5":  {group: 6, offset: 5, name: "PG5", defaultPull: gpio.Float},
	"PG6":  {group: 6, offset: 6, name: "PG6", defaultPull: gpio.Float},
	"PG7":  {group: 6, offset: 7, name: "PG7", defaultPull: gpio.Float},
	"PG8":  {group: 6, offset: 8, name: "PG8", defaultPull: gpio.Float},
	"PG9":  {group: 6, offset: 9, name: "PG9", defaultPull: gpio.Float},
	"PG10": {group: 6, offset: 10, name: "PG10", defaultPull: gpio.Float},
	"PG11": {group: 6, offset: 11, name: "PG11", defaultPull: gpio.Float},
	"PG12": {group: 6, offset: 12, name: "PG12", defaultPull: gpio.Float},
	"PG13": {group: 6, offset: 13, name: "PG13", defaultPull: gpio.Float},
	"PH0":  {group: 7, offset: 0, name: "PH0", defaultPull: gpio.Float},
	"PH1":  {group: 7, offset: 1, name: "PH1", defaultPull: gpio.Float},
	"PH2":  {group: 7, offset: 2, name: "PH2", defaultPull: gpio.Float},
	"PH3":  {group: 7, offset: 3, name: "PH3", defaultPull: gpio.Float},
	"PH4":  {group: 7, offset: 4, name: "PH4", defaultPull: gpio.Float},
	"PH5":  {group: 7, offset: 5, name: "PH5", defaultPull: gpio.Float},
	"PH6":  {group: 7, offset: 6, name: "PH6", defaultPull: gpio.Float},
	"PH7":  {group: 7, offset: 7, name: "PH7", defaultPull: gpio.Float},
	"PH8":  {group: 7, offset: 8, name: "PH8", defaultPull: gpio.Float},
	"PH9":  {group: 7, offset: 9, name: "PH9", defaultPull: gpio.Float},
	"PH10": {group: 7, offset: 10, name: "PH10", defaultPull: gpio.Float},
	"PH11": {group: 7, offset: 11, name: "PH11", defaultPull: gpio.Float},
	"PH12": {group: 7, offset: 12, name: "PH12", defaultPull: gpio.Float},
	"PH13": {group: 7, offset: 13, name: "PH13", defaultPull: gpio.Float},
	"PH14": {group: 7, offset: 14, name: "PH14", defaultPull: gpio.Float},
	"PH15": {group: 7, offset: 15, name: "PH15", defaultPull: gpio.Float},
	"PH16": {group: 7, offset: 16, name: "PH16", defaultPull: gpio.Float},
	"PH17": {group: 7, offset: 17, name: "PH17", defaultPull: gpio.Float},
	"PH18": {group: 7, offset: 18, name: "PH18", defaultPull: gpio.Float},
	"PH19": {group: 7, offset: 19, name: "PH19", defaultPull: gpio.Float},
	"PH20": {group: 7, offset: 20, name: "PH20", defaultPull: gpio.Float},
	"PH21": {group: 7, offset: 21, name: "PH21", defaultPull: gpio.Float},
	"PH22": {group: 7, offset: 22, name: "PH22", defaultPull: gpio.Float},
	"PH23": {group: 7, offset: 23, name: "PH23", defaultPull: gpio.Float},
	"PH24": {group: 7, offset: 24, name: "PH24", defaultPull: gpio.Float},
	"PH25": {group: 7, offset: 25, name: "PH25", defaultPull: gpio.Float},
	"PH26": {group: 7, offset: 26, name: "PH26", defaultPull: gpio.Float},
	"PH27": {group: 7, offset: 27, name: "PH27", defaultPull: gpio.Float},
	"PI0":  {group: 8, offset: 0, name: "PI0", defaultPull: gpio.Float},
	"PI1":  {group: 8, offset: 1, name: "PI1", defaultPull: gpio.Float},
	"PI2":  {group: 8, offset: 2, name: "PI2", defaultPull: gpio.Float},
	"PI3":  {group: 8, offset: 3, name: "PI3", defaultPull: gpio.Float},
	"PI4":  {group: 8, offset: 4, name: "PI4", defaultPull: gpio.Float},
	"PI5":  {group: 8, offset: 5, name: "PI5", defaultPull: gpio.Float},
	"PI6":  {group: 8, offset: 6, name: "PI6", defaultPull: gpio.Float},
	"PI7":  {group: 8, offset: 7, name: "PI7", defaultPull: gpio.Float},
	"PI8":  {group: 8, offset: 8, name: "PI8", defaultPull: gpio.Float},
	"PI9":  {group: 8, offset: 9, name: "PI9", defaultPull: gpio.Float},
	"PI10": {group: 8, offset: 10, name: "PI10", defaultPull: gpio.Float},
	"PI11": {group: 8, offset: 11, name: "PI11", defaultPull: gpio.Float},
	"PI12": {group: 8, offset: 12, name: "PI12", defaultPull: gpio.Float},
	"PI13": {group: 8, offset: 13, name: "PI13", defaultPull: gpio.Float},
	"PI14": {group: 8, offset: 14, name: "PI14", defaultPull: gpio.Float},
	"PI15": {group: 8, offset: 15, name: "PI15", defaultPull: gpio.Float},
	"PI16": {group: 8, offset: 16, name: "PI16", defaultPull: gpio.Float},
	"PI17": {group: 8, offset: 17, name: "PI17", defaultPull: gpio.Float},
	"PI18": {group: 8, offset: 18, name: "PI18", defaultPull: gpio.Float},
	"PI19": {group: 8, offset: 19, name: "PI19", defaultPull: gpio.Float},
	"PI20": {group: 8, offset: 20, name: "PI20", defaultPull: gpio.Float},
	"PI21": {group: 8, offset: 21, name: "PI21", defaultPull: gpio.Float},
}

func init() {
	PA0 = cpupins["PA0"]
	PA1 = cpupins["PA1"]
	PA2 = cpupins["PA2"]
	PA3 = cpupins["PA3"]
	PA4 = cpupins["PA4"]
	PA5 = cpupins["PA5"]
	PA6 = cpupins["PA6"]
	PA7 = cpupins["PA7"]
	PA8 = cpupins["PA8"]
	PA9 = cpupins["PA9"]
	PA10 = cpupins["PA10"]
	PA11 = cpupins["PA11"]
	PA12 = cpupins["PA12"]
	PA13 = cpupins["PA13"]
	PA14 = cpupins["PA14"]
	PA15 = cpupins["PA15"]
	PA16 = cpupins["PA16"]
	PA17 = cpupins["PA17"]
	PB0 = cpupins["PB0"]
	PB1 = cpupins["PB1"]
	PB2 = cpupins["PB2"]
	PB3 = cpupins["PB3"]
	PB4 = cpupins["PB4"]
	PB5 = cpupins["PB5"]
	PB6 = cpupins["PB6"]
	PB7 = cpupins["PB7"]
	PB8 = cpupins["PB8"]
	PB9 = cpupins["PB9"]
	PB10 = cpupins["PB10"]
	PB11 = cpupins["PB11"]
	PB12 = cpupins["PB12"]
	PB13 = cpupins["PB13"]
	PB14 = cpupins["PB14"]
	PB15 = cpupins["PB15"]
	PB16 = cpupins["PB16"]
	PB17 = cpupins["PB17"]
	PB18 = cpupins["PB18"]
	PB19 = cpupins["PB19"]
	PB20 = cpupins["PB20"]
	PB21 = cpupins["PB21"]
	PB22 = cpupins["PB22"]
	PB23 = cpupins["PB23"]
	PC0 = cpupins["PC0"]
	PC1 = cpupins["PC1"]
	PC2 = cpupins["PC2"]
	PC3 = cpupins["PC3"]
	PC4 = cpupins["PC4"]
	PC5 = cpupins["PC5"]
	PC6 = cpupins["PC6"]
	PC7 = cpupins["PC7"]
	PC8 = cpupins["PC8"]
	PC9 = cpupins["PC9"]
	PC10 = cpupins["PC10"]
	PC11 = cpupins["PC11"]
	PC12 = cpupins["PC12"]
	PC13 = cpupins["PC13"]
	PC14 = cpupins["PC14"]
	PC15 = cpupins["PC15"]
	PC16 = cpupins["PC16"]
	PC17 = cpupins["PC17"]
	PC18 = cpupins["PC18"]
	PC19 = cpupins["PC19"]
	PC20 = cpupins["PC20"]
	PC21 = cpupins["PC21"]
	PC22 = cpupins["PC22"]
	PC23 = cpupins["PC23"]
	PC24 = cpupins["PC24"]
	PD0 = cpupins["PD0"]
	PD1 = cpupins["PD1"]
	PD2 = cpupins["PD2"]
	PD3 = cpupins["PD3"]
	PD4 = cpupins["PD4"]
	PD5 = cpupins["PD5"]
	PD6 = cpupins["PD6"]
	PD7 = cpupins["PD7"]
	PD8 = cpupins["PD8"]
	PD9 = cpupins["PD9"]
	PD10 = cpupins["PD10"]
	PD11 = cpupins["PD11"]
	PD12 = cpupins["PD12"]
	PD13 = cpupins["PD13"]
	PD14 = cpupins["PD14"]
	PD15 = cpupins["PD15"]
	PD16 = cpupins["PD16"]
	PD17 = cpupins["PD17"]
	PD18 = cpupins["PD18"]
	PD19 = cpupins["PD19"]
	PD20 = cpupins["PD20"]
	PD21 = cpupins["PD21"]
	PD22 = cpupins["PD22"]
	PD23 = cpupins["PD23"]
	PD24 = cpupins["PD24"]
	PD25 = cpupins["PD25"]
	PD26 = cpupins["PD26"]
	PD27 = cpupins["PD27"]
	PE0 = cpupins["PE0"]
	PE1 = cpupins["PE1"]
	PE2 = cpupins["PE2"]
	PE3 = cpupins["PE3"]
	PE4 = cpupins["PE4"]
	PE5 = cpupins["PE5"]
	PE6 = cpupins["PE6"]
	PE7 = cpupins["PE7"]
	PE8 = cpupins["PE8"]
	PE9 = cpupins["PE9"]
	PE10 = cpupins["PE10"]
	PE11 = cpupins["PE11"]
	PE12 = cpupins["PE12"]
	PE13 = cpupins["PE13"]
	PE14 = cpupins["PE14"]
	PE15 = cpupins["PE15"]
	PE16 = cpupins["PE16"]
	PE17 = cpupins["PE17"]
	PF0 = cpupins["PF0"]
	PF1 = cpupins["PF1"]
	PF2 = cpupins["PF2"]
	PF3 = cpupins["PF3"]
	PF4 = cpupins["PF4"]
	PF5 = cpupins["PF5"]
	PF6 = cpupins["PF6"]
	PG0 = cpupins["PG0"]
	PG1 = cpupins["PG1"]
	PG2 = cpupins["PG2"]
	PG3 = cpupins["PG3"]
	PG4 = cpupins["PG4"]
	PG5 = cpupins["PG5"]
	PG6 = cpupins["PG6"]
	PG7 = cpupins["PG7"]
	PG8 = cpupins["PG8"]
	PG9 = cpupins["PG9"]
	PG10 = cpupins["PG10"]
	PG11 = cpupins["PG11"]
	PG12 = cpupins["PG12"]
	PG13 = cpupins["PG13"]
	PH0 = cpupins["PH0"]
	PH1 = cpupins["PH1"]
	PH2 = cpupins["PH2"]
	PH3 = cpupins["PH3"]
	PH4 = cpupins["PH4"]
	PH5 = cpupins["PH5"]
	PH6 = cpupins["PH6"]
	PH7 = cpupins["PH7"]
	PH8 = cpupins["PH8"]
	PH9 = cpupins["PH9"]
	PH10 = cpupins["PH10"]
	PH11 = cpupins["PH11"]
	PH12 = cpupins["PH12"]
	PH13 = cpupins["PH13"]
	PH14 = cpupins["PH14"]
	PH15 = cpupins["PH15"]
	PH16 = cpupins["PH16"]
	PH17 = cpupins["PH17"]
	PH18 = cpupins["PH18"]
	PH19 = cpupins["PH19"]
	PH20 = cpupins["PH20"]
	PH21 = cpupins["PH21"]
	PH22 = cpupins["PH22"]
	PH23 = cpupins["PH23"]
	PH24 = cpupins["PH24"]
	PH25 = cpupins["PH25"]
	PH26 = cpupins["PH26"]
	PH27 = cpupins["PH27"]
	PI0 = cpupins["PI0"]
	PI1 = cpupins["PI1"]
	PI2 = cpupins["PI2"]
	PI3 = cpupins["PI3"]
	PI4 = cpupins["PI4"]
	PI5 = cpupins["PI5"]
	PI6 = cpupins["PI6"]
	PI7 = cpupins["PI7"]
	PI8 = cpupins["PI8"]
	PI9 = cpupins["PI9"]
	PI10 = cpupins["PI10"]
	PI11 = cpupins["PI11"]
	PI12 = cpupins["PI12"]
	PI13 = cpupins["PI13"]
	PI14 = cpupins["PI14"]
	PI15 = cpupins["PI15"]
	PI16 = cpupins["PI16"]
	PI17 = cpupins["PI17"]
	PI18 = cpupins["PI18"]
	PI19 = cpupins["PI19"]
	PI20 = cpupins["PI20"]
	PI21 = cpupins["PI21"]
}

// initPins initializes the mapping of pins by function, sets the alternate
// functions of each pin, and registers all the pins with gpio.
func initPins() error {
	functions := map[pin.Func]struct{}{}
	for name, p := range cpupins {
		num := strconv.Itoa(p.Number())
		gpion := "GPIO" + num

		// 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(gpion)
		_ = gpioreg.Unregister(num)

		// Register the pin with gpio.
		if err := gpioreg.Register(p); err != nil {
			return err
		}
		if err := gpioreg.RegisterAlias(gpion, name); err != nil {
			return err
		}
		if err := gpioreg.RegisterAlias(num, name); err != nil {
			return err
		}
		switch f := p.Func(); f {
		case gpio.IN, gpio.OUT, pin.FuncNone:
		default:
			// Registering the same alias twice fails. This can happen if two pins
			// are configured with the same function.
			if _, ok := functions[f]; !ok {
				functions[f] = struct{}{}
				if err := gpioreg.RegisterAlias(string(f), name); err != nil {
					return err
				}
			}
		}
	}

	// Now do a second loop but do the alternate functions.
	for name, p := range cpupins {
		for _, f := range p.SupportedFuncs() {
			switch f {
			case gpio.IN, gpio.OUT:
			default:
				if _, ok := functions[f]; !ok {
					functions[f] = struct{}{}
					if err := gpioreg.RegisterAlias(string(f), name); err != nil {
						return err
					}
				}
			}
		}
	}
	return nil
}

// function encodes the active functionality of a pin. The alternate functions
// are GPIO pin dependent.
type function uint8

// gpioGroup is a memory-mapped structure for the hardware registers that
// control a group of at most 32 pins. In practice the number of valid pins per
// group varies between 10 and 27.
//
// http://files.pine64.org/doc/datasheet/pine64/Allwinner_A64_User_Manual_V1.0.pdf
// Page 376 GPIO PB to PH.
// Page 410 GPIO PL.
// Size is 36 bytes.
type gpioGroup struct {
	// Pn_CFGx n*0x24+x*4       Port n Configure Register x (n from 1(B) to 7(H))
	cfg [4]uint32
	// Pn_DAT  n*0x24+0x10      Port n Data Register (n from 1(B) to 7(H))
	data uint32
	// Pn_DRVx n*0x24+0x14+x*4  Port n Multi-Driving Register x (n from 1 to 7)
	drv [2]uint32
	// Pn_PULL n*0x24+0x1C+x*4  Port n Pull Register (n from 1(B) to 7(H))
	pull [2]uint32
}

// gpioMap memory-maps all the gpio pin groups.
type gpioMap struct {
	// PA to PI.
	groups [9]gpioGroup
}

// driverGPIO implements periph.Driver.
type driverGPIO struct {
	// gpioMemory is the memory map of the CPU GPIO registers.
	gpioMemory *gpioMap
}

func (d *driverGPIO) String() string {
	return "allwinner-gpio"
}

func (d *driverGPIO) Prerequisites() []string {
	return nil
}

func (d *driverGPIO) After() []string {
	return []string{"sysfs-gpio"}
}

// Init does nothing if an allwinner processor is not detected. If one is
// detected, it memory maps gpio CPU registers and then sets up the pin mapping
// for the exact processor model detected.
func (d *driverGPIO) Init() (bool, error) {
	if !Present() {
		return false, errors.New("no Allwinner CPU detected")
	}

	// Mark the right pins as available even if the memory map fails so they can
	// callback to sysfs.Pins.
	switch {
	case IsA64():
		if err := mapA64Pins(); err != nil {
			return true, err
		}
	case IsR8():
		if err := mapR8Pins(); err != nil {
			return true, err
		}
	case IsA20():
		if err := mapA20Pins(); err != nil {
			return true, err
		}
	default:
		return false, errors.New("unknown Allwinner CPU model")
	}

	// gpioBaseAddr is the physical base address of the GPIO registers.
	gpioBaseAddr := uint32(getBaseAddress())
	if err := pmem.MapAsPOD(uint64(gpioBaseAddr), &d.gpioMemory); err != nil {
		if os.IsPermission(err) {
			return true, fmt.Errorf("need more access, try as root: %v", err)
		}
		return true, err
	}

	return true, initPins()
}

func init() {
	if isArm {
		driverreg.MustRegister(&drvGPIO)
	}
}

// getBaseAddress queries the virtual file system to retrieve the base address
// of the GPIO registers for GPIO pins in groups PA to PI.
//
// Defaults to 0x01C20800 as per datasheet if it could not query the file
// system.
func getBaseAddress() uint64 {
	base := uint64(0x01C20800)
	link, err := os.Readlink("/sys/bus/platform/drivers/sun50i-pinctrl/driver")
	if err != nil {
		return base
	}
	parts := strings.SplitN(path.Base(link), ".", 2)
	if len(parts) != 2 {
		return base
	}
	base2, err := strconv.ParseUint(parts[0], 16, 64)
	if err != nil {
		return base
	}
	return base2
}

var drvGPIO driverGPIO

// Ensure that the various structs implement the interfaces they're supposed to.
var _ gpio.PinIO = &Pin{}
var _ gpio.PinIn = &Pin{}
var _ gpio.PinOut = &Pin{}
var _ pin.PinFunc = &Pin{}
