// 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