359 lines
11 KiB
Go
359 lines
11 KiB
Go
// 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.
|
|
|
|
package chip
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"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/pin"
|
|
"periph.io/x/conn/v3/pin/pinreg"
|
|
"periph.io/x/host/v3/allwinner"
|
|
"periph.io/x/host/v3/distro"
|
|
"periph.io/x/host/v3/fs"
|
|
)
|
|
|
|
// C.H.I.P. hardware pins.
|
|
var (
|
|
TEMP_SENSOR = &pin.BasicPin{N: "TEMP_SENSOR"}
|
|
PWR_SWITCH = &pin.BasicPin{N: "PWR_SWITCH"}
|
|
// XIO "gpio" pins attached to the pcf8574 I²C port extender.
|
|
XIO0, XIO1, XIO2, XIO3, XIO4, XIO5, XIO6, XIO7 gpio.PinIO
|
|
)
|
|
|
|
// The U13 header is opposite the power LED.
|
|
//
|
|
// The alternate pin functionality is described at pages 322-323 of
|
|
// https://github.com/NextThingCo/CHIP-Hardware/raw/master/CHIP%5Bv1_0%5D/CHIPv1_0-BOM-Datasheets/Allwinner%20R8%20User%20Manual%20V1.1.pdf
|
|
var (
|
|
U13_1 = pin.GROUND //
|
|
U13_2 = pin.DC_IN //
|
|
U13_3 = pin.V5 // (filtered)
|
|
U13_4 = pin.GROUND //
|
|
U13_5 = pin.V3_3 //
|
|
U13_6 = TEMP_SENSOR // Analog temp sensor input
|
|
U13_7 = pin.V1_8 //
|
|
U13_8 = pin.BAT_PLUS // External LiPo battery
|
|
U13_9 = allwinner.PB16 // I2C1_SDA
|
|
U13_10 = PWR_SWITCH // Power button
|
|
U13_11 = allwinner.PB15 // I2C1_SCL
|
|
U13_12 = pin.GROUND //
|
|
U13_13 = allwinner.X1 // Touch screen X1
|
|
U13_14 = allwinner.X2 // Touch screen X2
|
|
U13_15 = allwinner.Y1 // Touch screen Y1
|
|
U13_16 = allwinner.Y2 // Touch screen Y2
|
|
U13_17 = allwinner.PD2 // LCD-D2; UART2_TX firmware probe for 1-wire to detect DIP at boot; http://docs.getchip.com/dip.html#dip-identification
|
|
U13_18 = allwinner.PB2 // PWM0; EINT16
|
|
U13_19 = allwinner.PD4 // LCD-D4; UART2_CTS
|
|
U13_20 = allwinner.PD3 // LCD-D3; UART2_RX
|
|
U13_21 = allwinner.PD6 // LCD-D6
|
|
U13_22 = allwinner.PD5 // LCD-D5
|
|
U13_23 = allwinner.PD10 // LCD-D10
|
|
U13_24 = allwinner.PD7 // LCD-D7
|
|
U13_25 = allwinner.PD12 // LCD-D12
|
|
U13_26 = allwinner.PD11 // LCD-D11
|
|
U13_27 = allwinner.PD14 // LCD-D14
|
|
U13_28 = allwinner.PD13 // LCD-D13
|
|
U13_29 = allwinner.PD18 // LCD-D18
|
|
U13_30 = allwinner.PD15 // LCD-D15
|
|
U13_31 = allwinner.PD20 // LCD-D20
|
|
U13_32 = allwinner.PD19 // LCD-D19
|
|
U13_33 = allwinner.PD22 // LCD-D22
|
|
U13_34 = allwinner.PD21 // LCD-D21
|
|
U13_35 = allwinner.PD24 // LCD-CLK
|
|
U13_36 = allwinner.PD23 // LCD-D23
|
|
U13_37 = allwinner.PD26 // LCD-VSYNC
|
|
U13_38 = allwinner.PD27 // LCD-HSYNC
|
|
U13_39 = pin.GROUND //
|
|
U13_40 = allwinner.PD25 // LCD-DE: RGB666 data
|
|
)
|
|
|
|
// The U14 header is right next to the power LED.
|
|
var (
|
|
U14_1 = pin.GROUND //
|
|
U14_2 = pin.V5 // (filtered)
|
|
U14_3 = allwinner.PG3 // UART1_TX; EINT3
|
|
U14_4 = allwinner.HP_LEFT // Headphone left output
|
|
U14_5 = allwinner.PG4 // UART1_RX; EINT4
|
|
U14_6 = allwinner.HP_COM // Headphone amp out
|
|
U14_7 = allwinner.FEL // Boot mode selection
|
|
U14_8 = allwinner.HP_RIGHT // Headphone right output
|
|
U14_9 = pin.V3_3 //
|
|
U14_10 = allwinner.MIC_GND // Microphone ground
|
|
U14_11 = allwinner.KEY_ADC // LRADC Low res analog to digital
|
|
U14_12 = allwinner.MIC_IN // Microphone input
|
|
U14_13 = XIO0 // gpio via I²C controller
|
|
U14_14 = XIO1 // gpio via I²C controller
|
|
U14_15 = XIO2 // gpio via I²C controller
|
|
U14_16 = XIO3 // gpio via I²C controller
|
|
U14_17 = XIO4 // gpio via I²C controller
|
|
U14_18 = XIO5 // gpio via I²C controller
|
|
U14_19 = XIO6 // gpio via I²C controller
|
|
U14_20 = XIO7 // gpio via I²C controller
|
|
U14_21 = pin.GROUND //
|
|
U14_22 = pin.GROUND //
|
|
U14_23 = allwinner.PG1 // GPS_CLK; AP-EINT1
|
|
U14_24 = allwinner.PB3 // IR_TX; AP-EINT3 (EINT17)
|
|
U14_25 = allwinner.PB18 // I2C2_SDA
|
|
U14_26 = allwinner.PB17 // I2C2_SCL
|
|
U14_27 = allwinner.PE0 // CSIPCK: CMOS serial interface; SPI2_CS0; EINT14
|
|
U14_28 = allwinner.PE1 // CSICK: CMOS serial interface; SPI2_CLK; EINT15
|
|
U14_29 = allwinner.PE2 // CSIHSYNC; SPI2_MOSI
|
|
U14_30 = allwinner.PE3 // CSIVSYNC; SPI2_MISO
|
|
U14_31 = allwinner.PE4 // CSID0
|
|
U14_32 = allwinner.PE5 // CSID1
|
|
U14_33 = allwinner.PE6 // CSID2
|
|
U14_34 = allwinner.PE7 // CSID3
|
|
U14_35 = allwinner.PE8 // CSID4
|
|
U14_36 = allwinner.PE9 // CSID5
|
|
U14_37 = allwinner.PE10 // CSID6; UART1_RX
|
|
U14_38 = allwinner.PE11 // CSID7; UART1_TX
|
|
U14_39 = pin.GROUND //
|
|
U14_40 = pin.GROUND //
|
|
)
|
|
|
|
// Present returns true if running on a NextThing Co's C.H.I.P. board.
|
|
//
|
|
// It looks for "C.H.I.P" in the device tree. The following information is
|
|
// expected in the device dtree:
|
|
// root@chip2:/proc/device-tree# od -c compatible
|
|
// 0000000 n e x t t h i n g , c h i p \0 a
|
|
// 0000020 l l w i n n e r , s u n 5 i - r
|
|
// 0000040 8 \0
|
|
// root@chip2:/proc/device-tree# od -c model
|
|
// 0000000 N e x t T h i n g C . H . I .
|
|
// 0000020 P . \0
|
|
func Present() bool {
|
|
return strings.Contains(distro.DTModel(), "C.H.I.P")
|
|
}
|
|
|
|
//
|
|
|
|
// aliases is a list of aliases for the various gpio pins, this allows users to
|
|
// refer to pins using the documented and labeled names instead of some GPIOnnn
|
|
// name. The map key is the alias and the value is the real pin name.
|
|
var aliases = map[string]string{
|
|
"AP-EINT1": "PG1",
|
|
"AP-EINT3": "PB3",
|
|
"CSIPCK": "PE0",
|
|
"CSIHSYNC": "PE2",
|
|
"CSID0": "PE4",
|
|
"CSID2": "PE6",
|
|
"CSID4": "PE8",
|
|
"CSID6": "PE10",
|
|
"CSICK": "PE1",
|
|
"CSIVSYNC": "PE3",
|
|
"CSID1": "PE5",
|
|
"CSID3": "PE7",
|
|
"CSID5": "PE9",
|
|
"CSID7": "PE11",
|
|
"LCD-CLK": "PD24",
|
|
"LCD-D10": "PD10",
|
|
"LCD-D11": "PD11",
|
|
"LCD-D12": "PD12",
|
|
"LCD-D13": "PD13",
|
|
"LCD-D14": "PD14",
|
|
"LCD-D15": "PD15",
|
|
"LCD-D18": "PD18",
|
|
"LCD-D19": "PD19",
|
|
"LCD-D2": "PD2",
|
|
"LCD-D20": "PD20",
|
|
"LCD-D21": "PD21",
|
|
"LCD-D22": "PD22",
|
|
"LCD-D23": "PD23",
|
|
"LCD-D3": "PD3",
|
|
"LCD-D4": "PD4",
|
|
"LCD-D5": "PD5",
|
|
"LCD-D6": "PD6",
|
|
"LCD-D7": "PD7",
|
|
"LCD-DE": "PD25",
|
|
"LCD-HSYNC": "PD27",
|
|
"LCD-VSYNC": "PD26",
|
|
"TWI1-SCK": "PB15",
|
|
"TWI1-SDA": "PB16",
|
|
"TWI2-SCK": "PB17",
|
|
"TWI2-SDA": "PB18",
|
|
"UART1-RX": "PG4",
|
|
"UART1-TX": "PG3",
|
|
}
|
|
|
|
func init() {
|
|
// These are initialized later by the driver.
|
|
XIO0 = gpio.INVALID
|
|
XIO1 = gpio.INVALID
|
|
XIO2 = gpio.INVALID
|
|
XIO3 = gpio.INVALID
|
|
XIO4 = gpio.INVALID
|
|
XIO5 = gpio.INVALID
|
|
XIO6 = gpio.INVALID
|
|
XIO7 = gpio.INVALID
|
|
// These must be reinitialized.
|
|
U14_13 = XIO0
|
|
U14_14 = XIO1
|
|
U14_15 = XIO2
|
|
U14_16 = XIO3
|
|
U14_17 = XIO4
|
|
U14_18 = XIO5
|
|
U14_19 = XIO6
|
|
U14_20 = XIO7
|
|
}
|
|
|
|
// findXIOBase calculates the base of the XIO-P? gpio pins as explained in
|
|
// http://docs.getchip.com/chip.html#kernel-4-3-vs-4-4-gpio-how-to-tell-the-difference
|
|
//
|
|
// The XIO-P? sysfs mapped pin number changed in kernel 4.3, 4.4.11 and again
|
|
// in 4.4.13 so it is better to query sysfs.
|
|
func findXIOBase() int {
|
|
chips, err := filepath.Glob("/sys/class/gpio/gpiochip*/label")
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
for _, item := range chips {
|
|
f, err := fs.Open(item, os.O_RDONLY)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
b, err := ioutil.ReadAll(f)
|
|
if err1 := f.Close(); err == nil {
|
|
err = err1
|
|
}
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if string(b) == "pcf8574a\n" {
|
|
id, err := strconv.Atoi(filepath.Base(filepath.Dir(item))[8:])
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
return id
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// driver implements drivers.Driver.
|
|
type driver struct {
|
|
}
|
|
|
|
func (d *driver) String() string {
|
|
return "chip"
|
|
}
|
|
|
|
func (d *driver) Prerequisites() []string {
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) After() []string {
|
|
// has allwinner cpu, needs sysfs for XIO0-XIO7 "gpio" pins
|
|
return []string{"allwinner-gpio", "sysfs-gpio"}
|
|
}
|
|
|
|
func (d *driver) Init() (bool, error) {
|
|
if !Present() {
|
|
return false, errors.New("NextThing Co. CHIP board not detected")
|
|
}
|
|
|
|
base := findXIOBase()
|
|
if base == -1 {
|
|
return true, errors.New("couldn't find XIO pins base number")
|
|
}
|
|
for i := 0; i < 8; i++ {
|
|
aliases[fmt.Sprintf("XIO-P%d", i)] = fmt.Sprintf("GPIO%d", base+i)
|
|
}
|
|
|
|
// At this point the sysfs driver has initialized and discovered its pins,
|
|
// we can now hook-up the appropriate CHIP pins to sysfs gpio pins.
|
|
for alias, real := range aliases {
|
|
if err := gpioreg.RegisterAlias(alias, real); err != nil {
|
|
return true, err
|
|
}
|
|
}
|
|
// These must be explicitly initialized.
|
|
XIO0 = gpioreg.ByName("XIO-P0")
|
|
XIO1 = gpioreg.ByName("XIO-P1")
|
|
XIO2 = gpioreg.ByName("XIO-P2")
|
|
XIO3 = gpioreg.ByName("XIO-P3")
|
|
XIO4 = gpioreg.ByName("XIO-P4")
|
|
XIO5 = gpioreg.ByName("XIO-P5")
|
|
XIO6 = gpioreg.ByName("XIO-P6")
|
|
XIO7 = gpioreg.ByName("XIO-P7")
|
|
U14_13 = XIO0
|
|
U14_14 = XIO1
|
|
U14_15 = XIO2
|
|
U14_16 = XIO3
|
|
U14_17 = XIO4
|
|
U14_18 = XIO5
|
|
U14_19 = XIO6
|
|
U14_20 = XIO7
|
|
|
|
// U13 is one of the 20x2 connectors.
|
|
U13 := [][]pin.Pin{
|
|
{U13_1, U13_2},
|
|
{U13_3, U13_4},
|
|
{U13_5, U13_6},
|
|
{U13_7, U13_8},
|
|
{gpioreg.ByName("TWI1-SDA"), U13_10},
|
|
{gpioreg.ByName("TWI1-SCK"), U13_12},
|
|
{U13_13, U13_14},
|
|
{U13_15, U13_16},
|
|
{gpioreg.ByName("LCD-D2"), gpioreg.ByName("PWM0")},
|
|
{gpioreg.ByName("LCD-D4"), gpioreg.ByName("LCD-D3")},
|
|
{gpioreg.ByName("LCD-D6"), gpioreg.ByName("LCD-D5")},
|
|
{gpioreg.ByName("LCD-D10"), gpioreg.ByName("LCD-D7")},
|
|
{gpioreg.ByName("LCD-D12"), gpioreg.ByName("LCD-D11")},
|
|
{gpioreg.ByName("LCD-D14"), gpioreg.ByName("LCD-D13")},
|
|
{gpioreg.ByName("LCD-D18"), gpioreg.ByName("LCD-D15")},
|
|
{gpioreg.ByName("LCD-D20"), gpioreg.ByName("LCD-D19")},
|
|
{gpioreg.ByName("LCD-D22"), gpioreg.ByName("LCD-D21")},
|
|
{gpioreg.ByName("LCD-CLK"), gpioreg.ByName("LCD-D23")},
|
|
{gpioreg.ByName("LCD-VSYNC"), gpioreg.ByName("LCD-HSYNC")},
|
|
{U13_39, gpioreg.ByName("LCD-DE")},
|
|
}
|
|
if err := pinreg.Register("U13", U13); err != nil {
|
|
return true, err
|
|
}
|
|
|
|
// U14 is one of the 20x2 connectors.
|
|
U14 := [][]pin.Pin{
|
|
{U14_1, U14_2},
|
|
{gpioreg.ByName("UART1-TX"), U14_4},
|
|
{gpioreg.ByName("UART1-RX"), U14_6},
|
|
{U14_7, U14_8},
|
|
{U14_9, U14_10},
|
|
{U14_11, U14_12}, // TODO(maruel): switch to LRADC once analog support is added
|
|
{U14_13, U14_14},
|
|
{U14_15, U14_16},
|
|
{U14_17, U14_18},
|
|
{U14_19, U14_20},
|
|
{U14_21, U14_22},
|
|
{gpioreg.ByName("AP-EINT1"), gpioreg.ByName("AP-EINT3")},
|
|
{gpioreg.ByName("TWI2-SDA"), gpioreg.ByName("TWI2-SCK")},
|
|
{gpioreg.ByName("CSIPCK"), gpioreg.ByName("CSICK")},
|
|
{gpioreg.ByName("CSIHSYNC"), gpioreg.ByName("CSIVSYNC")},
|
|
{gpioreg.ByName("CSID0"), gpioreg.ByName("CSID1")},
|
|
{gpioreg.ByName("CSID2"), gpioreg.ByName("CSID3")},
|
|
{gpioreg.ByName("CSID4"), gpioreg.ByName("CSID5")},
|
|
{gpioreg.ByName("CSID6"), gpioreg.ByName("CSID7")},
|
|
{U14_39, U14_40},
|
|
}
|
|
return true, pinreg.Register("U14", U14)
|
|
}
|
|
|
|
func init() {
|
|
if isArm {
|
|
driverreg.MustRegister(&drv)
|
|
}
|
|
}
|
|
|
|
var drv driver
|