// 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/periph" "periph.io/x/periph/conn/gpio" "periph.io/x/periph/conn/gpio/gpioreg" "periph.io/x/periph/conn/physic" "periph.io/x/periph/conn/pin" "periph.io/x/periph/host/pmem" "periph.io/x/periph/host/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.Func("ERR") } 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<> (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("Allwinner CPU not 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 { periph.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{}