summaryrefslogtreecommitdiff
path: root/util/pinmap
diff options
context:
space:
mode:
Diffstat (limited to 'util/pinmap')
-rw-r--r--util/pinmap/README.md100
-rw-r--r--util/pinmap/chips/it81302.go243
-rw-r--r--util/pinmap/chips/npcx993.go239
-rw-r--r--util/pinmap/chips/npcx993_test.go93
-rw-r--r--util/pinmap/chips/register.go15
-rw-r--r--util/pinmap/go.mod3
-rw-r--r--util/pinmap/pinmap/main.go73
-rw-r--r--util/pinmap/pm/chip.go73
-rw-r--r--util/pinmap/pm/chip_test.go68
-rw-r--r--util/pinmap/pm/generate.go172
-rw-r--r--util/pinmap/pm/generate_test.go166
-rw-r--r--util/pinmap/pm/pins.go35
-rw-r--r--util/pinmap/pm/reader.go43
-rw-r--r--util/pinmap/pm/reader_test.go52
-rw-r--r--util/pinmap/readers/csv/csv.go117
-rw-r--r--util/pinmap/readers/csv/csv_test.go63
-rw-r--r--util/pinmap/readers/csv/register.go13
-rw-r--r--util/pinmap/readers/csv/testdata/data.csv11
18 files changed, 1579 insertions, 0 deletions
diff --git a/util/pinmap/README.md b/util/pinmap/README.md
new file mode 100644
index 0000000000..a0b4823b2b
--- /dev/null
+++ b/util/pinmap/README.md
@@ -0,0 +1,100 @@
+# pinmap
+
+## Overview
+
+This program reads a CSV (comma separated values) file and generates
+Zephyr Device Tree entries for GPIOs and other configuration.
+
+A basic Device Tree configuration is generated for I2C buses, ADC pins, GPIO pins
+and PWM pins, with labels and nodes generated for each of the signals in the
+spreadsheet.
+
+A separate overlay Device Tree file can be used to modify the generated DTS
+to allow specific properties and parameters to be set e.g bus speeds for I2C, conversion parameters
+for ADCs, frequencies for PWM etc.
+
+## Building
+
+An ebuild file will eventually be created to integrate the building of the
+utility to the standard host binary path.
+
+In the meantime, the utility can be built directly via:
+
+```
+
+cd pinmap/pinmap
+go build
+
+```
+
+This builds the `pinmap` binary in the `pinmap/pinmap` directory.
+This binary can be run directly or moved to an appropriate binary directory.
+
+## Executing
+
+Running `pinmap --help` prints a usage page.
+
+The `--reader` flag allows selecting different forms of input.
+The default is `csv`, which is expected to be the downloaded CSV from a spreadsheet.
+
+The `--chip` flag selects the EC part to be used.
+
+## Spreadsheet format
+
+An [example spreadsheet](http://go/cros-nissa-ec-pinmap) shows the format expected.
+
+The first row should contain column titles that the CSV reader can match against to retrieve the
+appropriate data. Currently the column names are fixed, but a TODO is to provide an external
+map that allows the reader to be informed which columns are to be used.
+
+Multiple EC chips may be supported in the same spreadsheet. The EC part name is set
+as one of the column headers, and this column can be selected using the `--chip` flag.
+
+An example of a working CSV file can also be viewed in the [file](readers/csv/testdata/data.csv)
+used for the unit tests.
+
+The key columns that are expected (and must match exactly) by the reader are:
+
+| Column Title | Description |
+| ----------- | ----------- |
+| Signal Name | The net name as used in the circuit |
+| Type | A drop down menu that indicates the type of pin (see the table below) |
+| Enum | If set, this string will be added to the DTS node as the `enum-name` property |
+| *chip* | This column contains the pin reference for this signal for this particular EC part number |
+
+The **Type** column indicates exactly what the type of signal is, and is used to
+generate the GPIO or other configuration flags in the DTS.
+
+| Type Name | Description |
+| ----------- | ----------- |
+| `ADC` | An analogue to digital converter signal |
+| `PWM` | A pulse width modulator signal |
+| `PWM_INVERT` | A pulse width modulator signal with inverted output |
+| `I2C_CLOCK` | The clock signal for an I2C bus |
+| `I2C_DATA` | The data signal for an I2C bus (ignored) |
+| `INPUT` | A GPIO input signal |
+| `INPUT_PU` | A GPIO input signal with internal pull-up |
+| `INPUT_PD` | A GPIO input signal with internal pull-down |
+| `OUTPUT` | A GPIO output signal |
+| `OUTPUT_ODR` | A GPIO output open drain signal |
+| `OUTPUT_ODL` | A GPIO output open drain signal (default low) |
+| `OTHER` | This signal is ignored, and no DTS configuration is generated for this pin |
+
+For the I2C signals, only the `I2C_CLOCK` signal is used to determine which I2C
+bus is referenced - the `I2C_DATA` signal is effectively ignored.
+
+## Example use
+
+Assume that the spreadsheet is downloaded as CSV format to the file `signals.csv`, and
+a NPCX993 EC chip is selected, the following command can be run:
+
+```
+pinmap --chip=NPCX993 --output=generated.dts signals.csv
+```
+
+The file `generated.dts` contains the DTS configuration as processed and generated by the utility.
+
+## TODO
+
+- Read signals from arbitrage (requires more data in arbitrage)
+- Build chip map from vendor data
diff --git a/util/pinmap/chips/it81302.go b/util/pinmap/chips/it81302.go
new file mode 100644
index 0000000000..fef82df49b
--- /dev/null
+++ b/util/pinmap/chips/it81302.go
@@ -0,0 +1,243 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package chips
+
+import (
+ "fmt"
+ "strings"
+)
+
+// As provided by ITE.
+var it81302_pins map[string]string = map[string]string{
+ "A1": "GPL5",
+ "A2": "GPL4",
+ "A3": "SMDAT1/GPC2",
+ "A4": "SMCLK1/GPC1",
+ "A5": "SMCLK0/GPB3",
+ "A6": "FSCK/GPG7",
+ "A7": "FMOSI/GPG4",
+ "A8": "GPH6/ID6",
+ "A9": "GPH4/ID4",
+ "A10": "SMINT11/PD1CC2/GPF5",
+ "A11": "RTS0#/SMINT9/GPF3",
+ "A12": "GPF1",
+ "A13": "ADC13/GPL0",
+ "A14": "ADC14/GPL1",
+ "A15": "ADC15/GPL2",
+ "B1": "GPL6",
+ "B2": "RING#/PWRFAIL#/CK32KOUT/LPCRST#/GPB7",
+ "B3": "SMDAT0/GPB4",
+ "B4": "SOUT0/GPB1",
+ "B5": "SIN0/GPB0",
+ "B6": "DTR1#/GPG1/ID7",
+ "B7": "FMISO/GPG5",
+ "B8": "FSCE#/GPG3",
+ "B9": "GPH5/ID5",
+ "B10": "GPH3/ID3",
+ "B11": "DTR0#/SMINT8/GPF2",
+ "B12": "CEC/GPF0",
+ "B13": "GPE2",
+ "B14": "GPE1",
+ "B15": "ADC16/GPL3",
+ "C1": "GPL7",
+ "C2": "SMCLK2/PECI/GPF6",
+ "C14": "DCD0#/GPJ4",
+ "C15": "GPE3",
+ "D1": "GPC4",
+ "D2": "SMDAT2/PECIRQT#/GPF7",
+ "D14": "TACH1B/SMINT7/GPJ3",
+ "D15": "RIG0#/GPJ5",
+ "E1": "GPB2",
+ "E2": "GPC0",
+ "E5": "VSTBY",
+ "E6": "VFSPI",
+ "E7": "DSR0#/GPG6",
+ "E8": "SOUT1/SMDAT3/PD2CC2/GPH2/ID2",
+ "E9": "CLKRUN#/GPH0/ID0",
+ "E10": "SMINT10/PD1CC1/GPF4",
+ "E11": "VSTBY",
+ "E14": "SMINT5/GPJ1",
+ "E15": "TACH0B/SMINT6/GPJ2",
+ "F1": "PWRSW/GPE4",
+ "F2": "GPC6",
+ "F5": "VSTBY",
+ "F6": "VSS",
+ "F7": "SSCE1#/GPG0",
+ "F8": "SSCE0#/GPG2",
+ "F9": "SIN1/SMCLK3/PD2CC1/GPH1/ID1",
+ "F10": "AVCC",
+ "F11": "AVSS",
+ "F14": "ADC7/CTS1#/GPI7",
+ "F15": "TACH2/SMINT4/GPJ0",
+ "G1": "CK32K/GPJ6",
+ "G2": "GA20/GPB5",
+ "G5": "VSS",
+ "G6": "VSS",
+ "G10": "ADC3/SMINT2/GPI3",
+ "G11": "ADC5/DCD1#/GPI5",
+ "G14": "ADC4/SMINT3/GPI4",
+ "G15": "ADC6/DSR1#/GPI6",
+ "H1": "ALERT#/SERIRQ/GPM6",
+ "H2": "GPJ7",
+ "H5": "VSS",
+ "H6": "VSS",
+ "H10": "KSI7",
+ "H11": "ADC0/GPI0",
+ "H14": "ADC1/SMINT0/GPI1",
+ "H15": "ADC2/SMINT1/GPI2",
+ "J1": "EIO3/LAD3/GPM3",
+ "J2": "ECS#/LFRAME#/GPM5",
+ "J5": "KBRST#/GPB6",
+ "J6": "VSS",
+ "J10": "KSI4",
+ "J11": "KSI5",
+ "J14": "KSI6",
+ "J15": "KSI3/SLIN#",
+ "K1": "EIO1/LAD1/GPM1",
+ "K2": "EIO2/LAD2/GPM2",
+ "K5": "VBAT",
+ "K6": "VCC",
+ "K7": "PWM5/SMDAT5/GPA5",
+ "K8": "KSO1/PD1",
+ "K9": "KSO5/PD5",
+ "K10": "KSI2/INIT#",
+ "K11": "KSO17/SMISO/GPC5",
+ "K14": "KSI1/AFD#",
+ "K15": "KSI0/STB#",
+ "L1": "ESCK/LPCCLK/GPM4",
+ "L2": "EIO0/LAD0/GPM0",
+ "L5": "VSTBY",
+ "L6": "VCORE",
+ "L7": "PWM4/SMCLK5/GPA4",
+ "L8": "PWM7/RIG1#/GPA7",
+ "L9": "KSO4/PD4",
+ "L10": "KSO9/BUSY",
+ "L11": "VSTBY",
+ "L14": "KSO16/SMOSI/GPC3",
+ "L15": "KSO15",
+ "M1": "ECSMI#/GPD4",
+ "M2": "WRST#",
+ "M14": "KSO14",
+ "M15": "KSO13",
+ "N1": "PWUREQ#/BBO/SMCLK2ALT/GPC7",
+ "N2": "LPCPD#/GPE6",
+ "N14": "KSO12/SLCT",
+ "N15": "WUI14/GPK6",
+ "P1": "WUI8/GPK0",
+ "P2": "RI1#/GPD0",
+ "P3": "L80HLAT/BAO/SMCLK4/GPE0",
+ "P4": "RI2#/GPD1",
+ "P5": "ECSCI#/GPD3",
+ "P6": "PWM1/GPA1",
+ "P7": "PWM3/GPA3",
+ "P8": "GINT/CTS0#/GPD5",
+ "P9": "RTS1#/GPE5",
+ "P10": "KSO2/PD2",
+ "P11": "KSO6/PD6",
+ "P12": "KSO8/ACK#",
+ "P13": "KSO10/PE",
+ "P14": "KSO11/ERR#",
+ "P15": "WUI15/GPK7",
+ "R1": "WUI9/GPK1",
+ "R2": "WUI10/GPK2",
+ "R3": "WUI11/GPK3",
+ "R4": "L80LLAT/SMDAT4/GPE7",
+ "R5": "ERST#/LPCRST#/GPD2",
+ "R6": "PWM0/GPA0",
+ "R7": "PWM2/GPA2",
+ "R8": "PWM6/SSCK/GPA6",
+ "R9": "KSO0/PD0",
+ "R10": "KSO3/PD3",
+ "R11": "KSO7/PD7",
+ "R12": "TACH0A/GPD6",
+ "R13": "TACH1A/GPD7",
+ "R14": "WUI13/GPK5",
+ "R15": "WUI12/GPK4",
+}
+
+// it81302 represents an ITE81302 EC.
+type It81302 struct {
+ okay []string // Nodes to enable.
+}
+
+// Name returns the name of this EC.
+func (c *It81302) Name() string {
+ return "IT81302"
+}
+
+// EnabledNodes returns a list of the DTS nodes that require enabling.
+func (c *It81302) EnabledNodes() []string {
+ return c.okay
+}
+
+// Adc returns the configuration of this pin as an ADC.
+func (c *It81302) Adc(p string) string {
+ s, ok := it81302_pins[p]
+ if ok {
+ // Found the pin, now find the ADC name.
+ for _, ss := range strings.Split(s, "/") {
+ if strings.HasPrefix(ss, "ADC") && len(ss) > 3 {
+ c.okay = append(c.okay, "adc0")
+ return fmt.Sprintf("%s", ss[3:])
+ }
+ }
+ return ""
+ } else {
+ return ""
+ }
+}
+
+// Gpio returns the configuration of this pin as a GPIO.
+func (c *It81302) Gpio(p string) string {
+ s, ok := it81302_pins[p]
+ if ok {
+ // Found the pin, now find the GP name.
+ for _, ss := range strings.Split(s, "/") {
+ if strings.HasPrefix(ss, "GP") && len(ss) == 4 {
+ lc := strings.ToLower(ss)
+ return fmt.Sprintf("gpio%c %c", lc[2], lc[3])
+ }
+ }
+ return ""
+ } else {
+ return ""
+ }
+}
+
+// I2c returns the configuration of this pin as an I2C bus.
+func (c *It81302) I2c(p string) string {
+ s, ok := it81302_pins[p]
+ if ok {
+ // Found the pin, now find the I2C name.
+ for _, ss := range strings.Split(s, "/") {
+ if strings.HasPrefix(ss, "SMCLK") && len(ss) > 5 {
+ i2c := fmt.Sprintf("i2c%s", ss[5:])
+ c.okay = append(c.okay, i2c)
+ return i2c
+ }
+ }
+ return ""
+ } else {
+ return ""
+ }
+}
+
+// Pwm returns the configuration of this pin as a PWM.
+func (c *It81302) Pwm(p string) string {
+ s, ok := it81302_pins[p]
+ if ok {
+ // Found the pin, now find the PWM name.
+ for _, ss := range strings.Split(s, "/") {
+ if strings.HasPrefix(ss, "PWM") && len(ss) > 3 {
+ pwm := fmt.Sprintf("pwm%s", ss[3:])
+ c.okay = append(c.okay, pwm)
+ return fmt.Sprintf("%s %s", pwm, ss[3:])
+ }
+ }
+ return ""
+ } else {
+ return ""
+ }
+}
diff --git a/util/pinmap/chips/npcx993.go b/util/pinmap/chips/npcx993.go
new file mode 100644
index 0000000000..88639232dc
--- /dev/null
+++ b/util/pinmap/chips/npcx993.go
@@ -0,0 +1,239 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package chips
+
+import (
+ "fmt"
+ "strings"
+)
+
+// As provided by Nuvoton.
+var npcx993_pins map[string]string = map[string]string{
+ "E7": "PSL_IN2#&GPI00,GPIO00",
+ "E6": "GPIO01,PSL_IN3#&GPI01",
+ "F7": "GPIO02,PSL_IN4#&GPI02",
+ "D9": "KSO16,GPIO03",
+ "D11": "KSO13,GPIO04",
+ "C11": "KSO12,GPIO05",
+ "B10": "KSO11&P80_DAT,GPIO06",
+ "B11": "KSO10&P80_CLK,GPIO07",
+ "C10": "KSO09,GPIO10,CR_SIN1",
+ "C9": "KSO08,GPIO11,CR_SOUT1",
+ "B9": "KSO07,GPO12,JEN#",
+ "C8": "KSO06,GPO13,GP_SEL#",
+ "C6": "KSO05,GPIO14",
+ "C7": "KSO04,GPIO15,XNOR",
+ "B8": "KSO03,GPIO16,JTAG_TDO0_SWO0",
+ "B7": "KSO02,GPIO17,JTAG_TDI0",
+ "B6": "KSO01,GPIO20,JTAG_TMS0_SWIO0",
+ "B5": "KSO00,GPIO21,JTAG_TCK0_SWCLK0",
+ "C5": "KSI7,GPIO22,S_SBUA",
+ "C4": "KSI6,GPIO23,S_SBUB",
+ "C3": "KSI5,GPIO24,,GP_MISO",
+ "B4": "KSI4,GPIO25,TRACECLK,GP_SCLK",
+ "B3": "KSI3,GPIO26,TRACEDATA0",
+ "A4": "KSI2,GPIO27,TRACEDATA1",
+ "A3": "KSI1,GPIO30,TRACEDATA2,GP_CS#",
+ "A2": "KSI0,GPIO31,TRACEDATA3,GP_MOSI",
+ "E4": "GPO32,TRIS#",
+ "D5": "GPIO33,CTS#,I2C5_SCL0",
+ "B2": "GPIO34,PS2_DAT2,ADC6",
+ "K2": "GPO35,CR_SOUT4,TEST#",
+ "D4": "GPIO36,RTS#,I2C5_SDA0",
+ "C1": "GPIO37,PS2_CLK2,ADC5",
+ "E5": "GPIO40,TA1",
+ "F4": "GPIOE0,ADC10",
+ "C2": "GPIO41,ADC4",
+ "D2": "GPIOF0,ADC9",
+ "D1": "AVCC",
+ "D3": "GPIO42,ADC3,RI#",
+ "E2": "GPIO43,ADC2",
+ "E3": "GPIO44,ADC1",
+ "F2": "GPIO45,ADC0",
+ "E1": "AVSS",
+ "F3": "GPIOE1,ADC7",
+ "G3": "GPIOF1,ADC8",
+ "H1": "LAD0,GPIO46",
+ "J1": "LAD1,GPIO47",
+ "G10": "GPIO50",
+ "K1": "LAD2,GPIO51",
+ "L1": "LAD3,GPIO52",
+ "L2": "LFRAME#,GPIO53",
+ "K3": "LRESET#,GPIO54",
+ "M1": "PCI_CLK,GPIO55",
+ "M2": "GPIO56,CLKRUN#",
+ "L3": "SER_IRQ,GPIO57",
+ "F1": "VHIF",
+ "L7": "GPIOE3,I2C6_SDA1,I3C_SDA",
+ "L6": "GPIOE4,I2C6_SCL1,I3C_SCL",
+ "G6": "GPIO60,PWM7",
+ "K4": "GPIO61,PWROFF#",
+ "H2": "GPIO62,PS2_CLK1",
+ "J2": "GPIO63,PS2_DAT1",
+ "G4": "GPIO64,CR_SIN1",
+ "H4": "GPO65,CR_SOUT1,FLPRG1#",
+ "G2": "GPIO66",
+ "J3": "GPIO67,PS2_CLK0",
+ "J4": "GPIO70,PS2_DAT0",
+ "L4": "VBAT",
+ "M4": "PWRGD,GPIO72",
+ "M5": "32KXOUT",
+ "L5": "32KXIN&32KCLKIN",
+ "G5": "GPIO73,TA2",
+ "H5": "GPIO74",
+ "J6": "GPIO75,32KHZ_OUT,RXD,CR_SIN2",
+ "J5": "GPIO76,EC_SCI#",
+ "K6": "VCC1_RST#,GPO77",
+ "K5": "GPIO80,PWM3",
+ "M6": "VREF_PECI",
+ "M7": "PECI_DATA,GPIO81",
+ "M10": "VSBY",
+ "J8": "PSL_OUT&GPIO85,GPO85",
+ "H6": "PSL_GPO,GPOD7",
+ "D6": "KSO14,GPIO82",
+ "D7": "KSO15,GPIO83",
+ "J9": "GPO86,TXD,CR_SOUT2,FLPRG2#",
+ "K7": "GPIO87,I2C1_SDA0",
+ "K8": "GPIO90,I2C1_SCL0",
+ "K9": "GPIO91,I2C2_SDA0",
+ "L8": "GPIO92,I2C2_SCL0",
+ "E11": "GPIO93,TA1,F_DIO2",
+ "M11": "GPIO94",
+ "M12": ",SPIP_MISO,GPIO95",
+ "G12": "F_DIO1,GPIO96",
+ "L10": ",GPIO97",
+ "G11": "F_CS0#,GPIOA0",
+ "L12": ",SPIP_SCLK,GPIOA1",
+ "F12": "F_SCLK,GPIOA2",
+ "K12": ",SPIP_MOSI,GPIOA3",
+ "H11": "F_DIO0,GPIOA4,TB1",
+ "K11": "GPIOA5",
+ "F11": "GPIOA6,PS2_CLK3,TA2,F_CS1#",
+ "J11": "GPIOA7,PS2_DAT3,TB2,F_DIO3",
+ "H12": "VSPI",
+ "L11": "GPIOB0",
+ "D8": "KSO17,GPIOB1,CR_SIN4",
+ "K10": "GPIOB2,I2C7_SDA0,DSR#",
+ "J10": "GPIOB3,I2C7_SCL0,DCD#",
+ "B12": "GPIOB4,I2C0_SDA0",
+ "C12": "GPIOB5,I2C0_SCL0",
+ "L9": "GPIOB6,PWM4",
+ "J7": "GPIOB7,PWM5",
+ "H8": "GPIOC0,PWM6",
+ "H9": "GPIOC1,I2C6_SDA0",
+ "H10": "GPIOC2,PWM1,I2C6_SCL0",
+ "G9": "GPIOC3,PWM0",
+ "G8": "GPIOC4,PWM2",
+ "H7": "GPIOC5,KBRST#",
+ "D10": "GPIOC6,SMI#",
+ "F10": "GPIOC7,DTR#_BOUT,ADC11",
+ "F9": "GPIOD0,I2C3_SDA0",
+ "F8": "GPIOD1,I2C3_SCL0",
+ "G7": "PSL_IN1#&GPID2,GPIOD2",
+ "E10": "GPIOD3,TB1",
+ "A9": "GPIOD4,CR_SIN3",
+ "A10": "GPIOD5,INTRUDER#",
+ "H3": "GPOD6,CR_SOUT3,SHDF_ESPI#",
+ "A11": "GPIOE2",
+ "A12": "GPIOE5",
+ "F6": "GPIOF2,I2C4_SDA1",
+ "F5": "GPIOF3,I2C4_SCL1",
+ "E9": "GPIOF4,I2C5_SDA1",
+ "E8": "GPIOF5,I2C5_SCL1",
+}
+
+type Npcx993 struct {
+ okay []string // Nodes to enable.
+}
+
+// Name returns the name of the chip.
+func (c *Npcx993) Name() string {
+ return "NPCX993"
+}
+
+// EnabledNodes returns the list of node names that are to
+// enabled in DTS.
+func (c *Npcx993) EnabledNodes() []string {
+ return c.okay
+}
+
+// Adc returns the ADC config associated with this pin.
+func (c *Npcx993) Adc(p string) string {
+ s, ok := npcx993_pins[p]
+ if ok {
+ // Found the pin, now find the ADC name.
+ for _, ss := range strings.Split(s, ",") {
+ if strings.HasPrefix(ss, "ADC") && len(ss) > 3 {
+ c.okay = append(c.okay, "adc0") // Enable ADC
+ return ss[3:]
+ }
+ }
+ return ""
+ } else {
+ return ""
+ }
+}
+
+// Gpio returns the GPIO config for this pin.
+func (c *Npcx993) Gpio(p string) string {
+ s, ok := npcx993_pins[p]
+ if ok {
+ // Found the pin, now find the GP name.
+ for _, ss := range strings.Split(s, ",") {
+ if strings.HasPrefix(ss, "GPO") && len(ss) == 5 {
+ lc := strings.ToLower(ss)
+ return fmt.Sprintf("gpio%c %c", lc[3], lc[4])
+ } else if strings.HasPrefix(ss, "GPIO") && len(ss) == 6 {
+ lc := strings.ToLower(ss)
+ return fmt.Sprintf("gpio%c %c", lc[4], lc[5])
+ }
+ }
+ return ""
+ } else {
+ return ""
+ }
+}
+
+// I2c returns the I2C config for this pin.
+// Searches for the pattern I2Cx_SCLy.
+func (c *Npcx993) I2c(p string) string {
+ s, ok := npcx993_pins[p]
+ if ok {
+ // Found the pin, now find the I2C port.
+ for _, ss := range strings.Split(s, ",") {
+ if len(ss) != 9 {
+ continue
+ }
+ if strings.HasPrefix(ss, "I2C") &&
+ ss[4:8] == "_SCL" {
+ i2c := fmt.Sprintf("i2c%c_%c", ss[3], ss[8])
+ c.okay = append(c.okay, i2c)
+ c.okay = append(c.okay, fmt.Sprintf("i2c_ctrl%c", ss[3]))
+ return i2c
+ }
+ }
+ return ""
+ } else {
+ return ""
+ }
+}
+
+// Pwm returns the PWM config associated with this pin.
+func (c *Npcx993) Pwm(p string) string {
+ s, ok := npcx993_pins[p]
+ if ok {
+ // Found the pin, now find the PWM name.
+ for _, ss := range strings.Split(s, ",") {
+ if strings.HasPrefix(ss, "PWM") && len(ss) > 3 {
+ ch := ss[3:]
+ c.okay = append(c.okay, fmt.Sprintf("pwm%s", ch))
+ return fmt.Sprintf("pwm%s %s", ch, ch)
+ }
+ }
+ return ""
+ } else {
+ return ""
+ }
+}
diff --git a/util/pinmap/chips/npcx993_test.go b/util/pinmap/chips/npcx993_test.go
new file mode 100644
index 0000000000..572e2a0f5a
--- /dev/null
+++ b/util/pinmap/chips/npcx993_test.go
@@ -0,0 +1,93 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package chips_test
+
+import (
+ "testing"
+
+ "reflect"
+ "sort"
+
+ "pinmap/chips"
+)
+
+func TestName(t *testing.T) {
+ expName := "NPCX993"
+ var n chips.Npcx993
+ name := n.Name()
+ if name != expName {
+ t.Errorf("Expected %s, got %s for Name()", expName, name)
+ }
+}
+
+func TestMissing(t *testing.T) {
+ var n chips.Npcx993
+
+ none := "None"
+ if n.Adc(none) != "" {
+ t.Errorf("Expected empty string, got %s for Adc()", n.Adc(none))
+ }
+ if n.Gpio(none) != "" {
+ t.Errorf("Expected empty string, got %s for Gpio()", n.Gpio(none))
+ }
+ if n.Pwm(none) != "" {
+ t.Errorf("Expected empty string, got %s for Pwm()", n.Pwm(none))
+ }
+ if n.I2c(none) != "" {
+ t.Errorf("Expected empty string, got %s for I2c()", n.I2c(none))
+ }
+}
+
+func TestMulti(t *testing.T) {
+ var n chips.Npcx993
+
+ pin := "F4"
+ if n.Adc(pin) != "10" {
+ t.Errorf("Expected \"10\", got %s for Adc()", n.Adc(pin))
+ }
+ if n.Gpio(pin) != "gpioe 0" {
+ t.Errorf("Expected \"gpioe 0\", got %s for Gpio()", n.Gpio(pin))
+ }
+ if n.Pwm(pin) != "" {
+ t.Errorf("Expected empty string, got %s for Pwm()", n.Pwm(pin))
+ }
+ if n.I2c(pin) != "" {
+ t.Errorf("Expected empty string, got %s for I2c()", n.I2c(pin))
+ }
+ pin = "L9"
+ if n.Pwm(pin) != "pwm4 4" {
+ t.Errorf("Expected \"pwm4 4\", got %s for Pwm()", n.Pwm(pin))
+ }
+ pin = "F8"
+ if n.I2c(pin) != "i2c3_0" {
+ t.Errorf("Expected \"i2c3_0\", got %s for I2c()", n.I2c(pin))
+ }
+}
+
+func TestAdcEnable(t *testing.T) {
+ var n chips.Npcx993
+
+ pin := "F4"
+ if n.Adc(pin) != "10" {
+ t.Errorf("Expected \"10\", got %s for Adc()", n.Adc(pin))
+ }
+ exp := []string{"adc0"}
+ if !reflect.DeepEqual(n.EnabledNodes(), exp) {
+ t.Errorf("Expected %v, got %v for EnabledNodes()", exp, n.EnabledNodes())
+ }
+}
+
+func TestI2cEnable(t *testing.T) {
+ var n chips.Npcx993
+
+ n.I2c("F5") // i2c4_1
+ n.I2c("C12") // i2c0_0
+ exp := []string{"i2c0_0", "i2c4_1", "i2c_ctrl0", "i2c_ctrl4"}
+ nodes := n.EnabledNodes()
+ sort.Strings(nodes)
+ if !reflect.DeepEqual(nodes, exp) {
+ t.Errorf("Expected %v, got %v for EnabledNodes()", exp, n.EnabledNodes())
+ }
+}
diff --git a/util/pinmap/chips/register.go b/util/pinmap/chips/register.go
new file mode 100644
index 0000000000..96b655814e
--- /dev/null
+++ b/util/pinmap/chips/register.go
@@ -0,0 +1,15 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package chips
+
+import (
+ "pinmap/pm"
+)
+
+// init registers the chips.
+func init() {
+ pm.RegisterChip(&It81302{})
+ pm.RegisterChip(&Npcx993{})
+}
diff --git a/util/pinmap/go.mod b/util/pinmap/go.mod
new file mode 100644
index 0000000000..7abf2b9886
--- /dev/null
+++ b/util/pinmap/go.mod
@@ -0,0 +1,3 @@
+module pinmap
+
+go 1.15
diff --git a/util/pinmap/pinmap/main.go b/util/pinmap/pinmap/main.go
new file mode 100644
index 0000000000..2bc5a94e24
--- /dev/null
+++ b/util/pinmap/pinmap/main.go
@@ -0,0 +1,73 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ _ "pinmap/chips"
+ "pinmap/pm"
+ _ "pinmap/readers/csv"
+)
+
+var chipFlag = flag.String("chip", "", "Chip to use for pinmap")
+var output = flag.String("output", "gpio.dts", "Output file")
+var reader = flag.String("reader", "csv", "Input source type")
+var force = flag.Bool("force", false, "Overwrite output file")
+
+func main() {
+ flag.Usage = Usage
+ flag.Parse()
+ if len(flag.Args()) == 0 {
+ Error("No input arguments")
+ }
+ chip := pm.FindChip(*chipFlag)
+ if chip == nil {
+ Error(fmt.Sprintf("No matching chip for '%s'", *chipFlag))
+ }
+ pins, err := pm.ReadPins(*reader, *chipFlag, flag.Arg(0))
+ if err != nil {
+ Error(fmt.Sprintf("%s - %s: %v", *reader, flag.Arg(0), err))
+ }
+ if !*force && fileExists(*output) {
+ Error(fmt.Sprintf("%s already exists - use --force to overwrite", *output))
+ }
+ out, err := os.Create(*output)
+ defer out.Close()
+ if err != nil {
+ Error(fmt.Sprintf("Failed to create %s: %v", *output, err))
+ }
+ pm.Generate(out, pins, chip)
+}
+
+// fileExists returns true if the file currently exists.
+func fileExists(name string) bool {
+ _, err := os.Stat(name)
+ return err == nil
+}
+
+// Error prints an error message to stderr and prints the usage.
+func Error(msg string) {
+ fmt.Fprintf(os.Stderr, "%s\n", msg)
+ Usage()
+}
+
+// Usage prints the usage of the command.
+func Usage() {
+ fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, "%s [ flags ] input-argument\n", os.Args[0])
+ flag.PrintDefaults()
+ fmt.Fprintf(os.Stderr, "Available chips are:\n")
+ for _, c := range pm.Chips() {
+ fmt.Fprintf(os.Stderr, "%s\n", c)
+ }
+ fmt.Fprintf(os.Stderr, "Available readers are:\n")
+ for _, r := range pm.Readers() {
+ fmt.Fprintf(os.Stderr, "%s\n", r)
+ }
+ os.Exit(1)
+}
diff --git a/util/pinmap/pm/chip.go b/util/pinmap/pm/chip.go
new file mode 100644
index 0000000000..292efaec97
--- /dev/null
+++ b/util/pinmap/pm/chip.go
@@ -0,0 +1,73 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package pm
+
+/*
+ * Chip represents an Embedded Controller IC, where
+ * pin names can be used to lookup various types of
+ * pin usages such as I2C buses, GPIOs etc.
+ * The pins are referenced as physical pin names such as "A4" etc.
+ */
+type Chip interface {
+ /*
+ * Name returns the name of the chip
+ */
+ Name() string
+ /*
+ * EnabledNodes returns a list of names of DTS nodes that
+ * require enabling i.e adding 'status = "okay"' on the nodes.
+ */
+ EnabledNodes() []string
+ /*
+ * Adc will return a DTS reference to the appropriate ADC
+ * that is connected to this pin.
+ */
+ Adc(pin string) string
+ /*
+ * Gpio will return a DTS reference to the appropriate GPIO
+ * that is connected to this pin.
+ */
+ Gpio(pin string) string
+ /*
+ * I2C will return a DTS reference to the appropriate I2C
+ * bus that is connected to this pin. The pin is assumed to be
+ * the I2C clock pin of the 2 wire bus.
+ */
+ I2c(pin string) string
+ /*
+ * Pwm will return a DTS reference to the appropriate PWM
+ * that is connected to this pin.
+ */
+ Pwm(pin string) string
+}
+
+// chipList contains a list of registered chips.
+// Each chip has a unique name that is used to match it.
+var chipList []Chip
+
+// RegisterChip adds this chip into the list of registered chips.
+func RegisterChip(chip Chip) {
+ chipList = append(chipList, chip)
+}
+
+// FindChip returns the registered chip matching this name, or nil
+// if none are found.
+func FindChip(name string) Chip {
+ for _, c := range chipList {
+ if c.Name() == name {
+ return c
+ }
+ }
+ return nil
+}
+
+// Chips returns the list of names of the registered chips.
+func Chips() []string {
+ var l []string
+ for _, c := range chipList {
+ l = append(l, c.Name())
+ }
+ return l
+}
diff --git a/util/pinmap/pm/chip_test.go b/util/pinmap/pm/chip_test.go
new file mode 100644
index 0000000000..031eefa2a8
--- /dev/null
+++ b/util/pinmap/pm/chip_test.go
@@ -0,0 +1,68 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package pm_test
+
+import (
+ "testing"
+
+ "reflect"
+ "sort"
+
+ "pinmap/pm"
+)
+
+type testChip struct {
+ name string
+ nodes []string
+ adc string
+ gpio string
+ i2c string
+ pwm string
+}
+
+func (c *testChip) Name() string {
+ return c.name
+}
+
+func (c *testChip) EnabledNodes() []string {
+ return c.nodes
+}
+
+func (c *testChip) Adc(pin string) string {
+ return c.adc
+}
+
+func (c *testChip) Gpio(pin string) string {
+ return c.gpio
+}
+
+func (c *testChip) I2c(pin string) string {
+ return c.i2c
+}
+
+func (c *testChip) Pwm(pin string) string {
+ return c.pwm
+}
+
+func TestName(t *testing.T) {
+ n1 := "Test1"
+ n2 := "Test2"
+ tc1 := &testChip{name: n1}
+ tc2 := &testChip{name: n2}
+ pm.RegisterChip(tc1)
+ pm.RegisterChip(tc2)
+ if pm.FindChip(n1) != tc1 {
+ t.Errorf("Did not match tc1")
+ }
+ if pm.FindChip(n2) != tc2 {
+ t.Errorf("Did not match tc2")
+ }
+ chips := pm.Chips()
+ sort.Strings(chips)
+ exp := []string{n1, n2}
+ if !reflect.DeepEqual(exp, chips) {
+ t.Errorf("Expected %v, got %v", exp, chips)
+ }
+}
diff --git a/util/pinmap/pm/generate.go b/util/pinmap/pm/generate.go
new file mode 100644
index 0000000000..0548a4a4db
--- /dev/null
+++ b/util/pinmap/pm/generate.go
@@ -0,0 +1,172 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package pm
+
+import (
+ "fmt"
+ "io"
+ "sort"
+ "strings"
+ "time"
+)
+
+const header = `/* Copyright %d The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * This file is auto-generated - do not edit!
+ */
+
+/ {
+`
+
+// Generate creates the DTS configuration from the pins using the chip as a
+// reference and writes the DTS to the output.
+func Generate(out io.Writer, pins *Pins, chip Chip) {
+ // Write header with date.
+ fmt.Fprintf(out, header, time.Now().Year())
+ pinConfig(out, "named-adc-channels", pins.Adc, chip, adcConfig)
+ pinConfig(out, "named-gpios", pins.Gpio, chip, gpioConfig)
+ pinConfig(out, "named-i2c-ports", pins.I2c, chip, i2cConfig)
+ pinConfig(out, "named-pwms", pins.Pwm, chip, pwmConfig)
+ fmt.Fprintf(out, "};\n")
+ // Retrieve the enabled nodes, sort, de-dup and
+ // generate overlays.
+ en := chip.EnabledNodes()
+ if len(en) != 0 {
+ sort.Strings(en)
+ var prev string
+ for _, s := range en {
+ if s == prev {
+ continue
+ }
+ fmt.Fprintf(out, "\n&%s {\n", s)
+ fmt.Fprintf(out, "\tstatus = \"okay\";\n")
+ fmt.Fprintf(out, "};\n")
+ prev = s
+ }
+ }
+}
+
+// pinConfig creates the DTS for a single pin.
+func pinConfig(out io.Writer, block string, pins []*Pin, chip Chip, cfunc func(io.Writer, *Pin, Chip)) {
+ if len(pins) == 0 {
+ return
+ }
+ // Sort the pins into alphbetical order.
+ sort.Slice(pins, func(i, j int) bool {
+ return pins[j].Signal > pins[i].Signal
+ })
+ // Generate start of block.
+ fmt.Fprintf(out, "\n\t%s {\n", block)
+ fmt.Fprintf(out, "\t\tcompatible = \"%s\";\n\n", block)
+ for _, p := range pins {
+ cfunc(out, p, chip)
+ }
+ fmt.Fprintf(out, "\t};\n")
+}
+
+// adcConfig is the handler for ADC pins.
+func adcConfig(out io.Writer, pin *Pin, chip Chip) {
+ if pin.PinType != ADC {
+ fmt.Printf("Unknown ADC type (%d) for pin %s, ignored\n", pin.PinType, pin.Pin)
+ return
+ }
+ c := chip.Adc(pin.Pin)
+ if len(c) == 0 {
+ fmt.Printf("No matching ADC for pin %s, ignored\n", pin.Pin)
+ return
+ }
+ lc := strings.ToLower(pin.Signal)
+ fmt.Fprintf(out, "\t\tadc_%s: %s {\n", lc, lc)
+ fmt.Fprintf(out, "\t\t\tlabel = \"%s\";\n", pin.Signal)
+ if len(pin.Enum) > 0 {
+ fmt.Fprintf(out, "\t\t\tenum-name = \"%s\";\n", pin.Enum)
+ }
+ fmt.Fprintf(out, "\t\t\tchannel = <%s>;\n", c)
+ fmt.Fprintf(out, "\t\t};\n")
+}
+
+// gpioConfig is the handler for GPIO pins.
+func gpioConfig(out io.Writer, pin *Pin, chip Chip) {
+ c := chip.Gpio(pin.Pin)
+ if len(c) == 0 {
+ fmt.Printf("No matching GPIO for pin %s, ignored\n", pin.Pin)
+ return
+ }
+ var gtype string
+ switch pin.PinType {
+ default:
+ fmt.Printf("Unknown GPIO type (%d) for pin %s, ignored\n", pin.PinType, pin.Pin)
+ return
+ case Input:
+ gtype = "GPIO_INPUT"
+ case InputPU:
+ gtype = "GPIO_INPUT_PULL_UP"
+ case InputPD:
+ gtype = "GPIO_INPUT_PULL_DOWN"
+ case Output:
+ gtype = "GPIO_OUTPUT"
+ case OutputOD:
+ gtype = "GPIO_ODR_HIGH"
+ case OutputODL:
+ gtype = "GPIO_ODR_LOW"
+ }
+ lc := strings.ToLower(pin.Signal)
+ fmt.Fprintf(out, "\t\tgpio_%s: %s {\n", lc, lc)
+ fmt.Fprintf(out, "\t\t\t#gpio-cells = <0>;\n")
+ fmt.Fprintf(out, "\t\t\tgpios = <&%s %s>;\n", c, gtype)
+ if len(pin.Enum) > 0 {
+ fmt.Fprintf(out, "\t\t\tenum-name = \"%s\";\n", pin.Enum)
+ }
+ fmt.Fprintf(out, "\t\t};\n")
+}
+
+// i2cConfig is the handler for I2C pins.
+func i2cConfig(out io.Writer, pin *Pin, chip Chip) {
+ if pin.PinType != I2C {
+ fmt.Printf("Unknown I2C type (%d) for pin %s, ignored\n", pin.PinType, pin.Pin)
+ return
+ }
+ c := chip.I2c(pin.Pin)
+ if len(c) == 0 {
+ fmt.Printf("No matching I2C for pin %s, ignored\n", pin.Pin)
+ return
+ }
+ // Trim off trailing clock name (if any)
+ lc := strings.TrimRight(strings.ToLower(pin.Signal), "_scl")
+ fmt.Fprintf(out, "\t\ti2c_%s: %s {\n", lc, lc)
+ fmt.Fprintf(out, "\t\t\ti2c-port = <&%s>;\n", c)
+ if len(pin.Enum) > 0 {
+ fmt.Fprintf(out, "\t\t\tenum-name = \"%s\";\n", pin.Enum)
+ }
+ fmt.Fprintf(out, "\t\t};\n")
+}
+
+// pwmConfig is the handler for PWM pins.
+func pwmConfig(out io.Writer, pin *Pin, chip Chip) {
+ var inv string
+ switch pin.PinType {
+ default:
+ fmt.Printf("Unknown PWM type (%d) for pin %s, ignored\n", pin.PinType, pin.Pin)
+ return
+ case PWM:
+ inv = "0"
+ case PWM_INVERT:
+ inv = "1"
+ }
+ c := chip.Pwm(pin.Pin)
+ if len(c) == 0 {
+ fmt.Printf("No matching PWM for pin %s, ignored\n", pin.Pin)
+ return
+ }
+ lc := strings.ToLower(pin.Signal)
+ fmt.Fprintf(out, "\t\tpwm_%s: %s {\n", lc, lc)
+ fmt.Fprintf(out, "\t\t\tpwms = <&%s %s>;\n", c, inv)
+ if len(pin.Enum) > 0 {
+ fmt.Fprintf(out, "\t\t\tenum-name = \"%s\";\n", pin.Enum)
+ }
+ fmt.Fprintf(out, "\t\t};\n")
+}
diff --git a/util/pinmap/pm/generate_test.go b/util/pinmap/pm/generate_test.go
new file mode 100644
index 0000000000..e899e7fa28
--- /dev/null
+++ b/util/pinmap/pm/generate_test.go
@@ -0,0 +1,166 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package pm_test
+
+import (
+ "testing"
+
+ "bytes"
+ "fmt"
+ "strings"
+ "time"
+
+ "pinmap/pm"
+)
+
+type genChip struct {
+}
+
+func (c *genChip) Name() string {
+ return "Test"
+}
+
+func (c *genChip) EnabledNodes() []string {
+ return []string{"adc0", "i2c0", "pwm1"}
+}
+
+func (c *genChip) Adc(pin string) string {
+ return pin
+}
+
+func (c *genChip) Gpio(pin string) string {
+ return fmt.Sprintf("gpio %s", pin)
+}
+
+func (c *genChip) I2c(pin string) string {
+ return "i2c0"
+}
+
+func (c *genChip) Pwm(pin string) string {
+ return "pwm1"
+}
+
+func TestGenerate(t *testing.T) {
+ pins := &pm.Pins{
+ Adc: []*pm.Pin{
+ &pm.Pin{pm.ADC, "A1", "EC_ADC_1", "ENUM_ADC_1"},
+ },
+ I2c: []*pm.Pin{
+ &pm.Pin{pm.I2C, "B2", "EC_I2C_CLK_0", "ENUM_I2C_0"},
+ },
+ Gpio: []*pm.Pin{
+ &pm.Pin{pm.Input, "C3", "EC_IN_1", "ENUM_IN_1"},
+ &pm.Pin{pm.Output, "D4", "EC_OUT_2", "ENUM_OUT_2"},
+ &pm.Pin{pm.InputPU, "G7", "EC_IN_3", "ENUM_IN_3"},
+ &pm.Pin{pm.InputPD, "H8", "EC_IN_4", "ENUM_IN_4"},
+ },
+ Pwm: []*pm.Pin{
+ &pm.Pin{pm.PWM, "E5", "EC_LED_1", "ENUM_LED_1"},
+ &pm.Pin{pm.PWM_INVERT, "F6", "EC_LED_2", "ENUM_LED_2"},
+ },
+ }
+ var out bytes.Buffer
+ pm.Generate(&out, pins, &genChip{})
+ /*
+ * Rather than doing a golden output text compare, it would be better
+ * to parse the device tree directly and ensuing it is correct.
+ * However this would considerably complicate this test.
+ */
+ expFmt :=
+ `/* Copyright %d The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * This file is auto-generated - do not edit!
+ */
+
+/ {
+
+ named-adc-channels {
+ compatible = "named-adc-channels";
+
+ adc_ec_adc_1: ec_adc_1 {
+ label = "EC_ADC_1";
+ enum-name = "ENUM_ADC_1";
+ channel = <A1>;
+ };
+ };
+
+ named-gpios {
+ compatible = "named-gpios";
+
+ gpio_ec_in_1: ec_in_1 {
+ #gpio-cells = <0>;
+ gpios = <&gpio C3 GPIO_INPUT>;
+ enum-name = "ENUM_IN_1";
+ };
+ gpio_ec_in_3: ec_in_3 {
+ #gpio-cells = <0>;
+ gpios = <&gpio G7 GPIO_INPUT_PULL_UP>;
+ enum-name = "ENUM_IN_3";
+ };
+ gpio_ec_in_4: ec_in_4 {
+ #gpio-cells = <0>;
+ gpios = <&gpio H8 GPIO_INPUT_PULL_DOWN>;
+ enum-name = "ENUM_IN_4";
+ };
+ gpio_ec_out_2: ec_out_2 {
+ #gpio-cells = <0>;
+ gpios = <&gpio D4 GPIO_OUTPUT>;
+ enum-name = "ENUM_OUT_2";
+ };
+ };
+
+ named-i2c-ports {
+ compatible = "named-i2c-ports";
+
+ i2c_ec_i2c_clk_0: ec_i2c_clk_0 {
+ i2c-port = <&i2c0>;
+ enum-name = "ENUM_I2C_0";
+ };
+ };
+
+ named-pwms {
+ compatible = "named-pwms";
+
+ pwm_ec_led_1: ec_led_1 {
+ pwms = <&pwm1 0>;
+ enum-name = "ENUM_LED_1";
+ };
+ pwm_ec_led_2: ec_led_2 {
+ pwms = <&pwm1 1>;
+ enum-name = "ENUM_LED_2";
+ };
+ };
+};
+
+&adc0 {
+ status = "okay";
+};
+
+&i2c0 {
+ status = "okay";
+};
+
+&pwm1 {
+ status = "okay";
+};
+`
+ exp := fmt.Sprintf(expFmt, time.Now().Year())
+ got := out.String()
+ if exp != got {
+ // Split each string into lines and compare the lines.
+ expLines := strings.Split(exp, "\n")
+ gotLines := strings.Split(got, "\n")
+ if len(expLines) != len(gotLines) {
+ t.Errorf("Expected %d lines, got %d lines", len(expLines), len(gotLines))
+ }
+ for i := range expLines {
+ if i < len(gotLines) && expLines[i] != gotLines[i] {
+ t.Errorf("%d: exp %s, got %s", i+1, expLines[i], gotLines[i])
+ }
+ }
+ }
+}
diff --git a/util/pinmap/pm/pins.go b/util/pinmap/pm/pins.go
new file mode 100644
index 0000000000..bb48fa8b92
--- /dev/null
+++ b/util/pinmap/pm/pins.go
@@ -0,0 +1,35 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package pm
+
+// Pin types enum constants
+const (
+ ADC = iota
+ PWM
+ PWM_INVERT
+ I2C
+ Input
+ InputPU
+ InputPD
+ Output
+ OutputOD
+ OutputODL
+)
+
+// Pin represents one EC pin.
+type Pin struct {
+ PinType int // Type of pin (from above)
+ Pin string // The reference of the physical pin.
+ Signal string // The net (circuit) name of the pin
+ Enum string // If set, the internal s/w name of the pin
+}
+
+// The accumulated pins of the EC.
+type Pins struct {
+ Adc []*Pin // Analogue to digital converters
+ I2c []*Pin // I2C busses
+ Gpio []*Pin // GPIO pins
+ Pwm []*Pin // Pwm pins
+}
diff --git a/util/pinmap/pm/reader.go b/util/pinmap/pm/reader.go
new file mode 100644
index 0000000000..d518b7fc3a
--- /dev/null
+++ b/util/pinmap/pm/reader.go
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package pm
+
+import (
+ "fmt"
+)
+
+// Reader reads the pin configuration from a source.
+type Reader interface {
+ Name() string
+ Read(arg string, chip string) (*Pins, error)
+}
+
+// readerlist is registered list of readers.
+var readerList []Reader
+
+// ReadPins will use the selected reader and the chip to
+// read the EC pin data.
+func ReadPins(reader, chip, arg string) (*Pins, error) {
+ for _, r := range readerList {
+ if r.Name() == reader {
+ return r.Read(chip, arg)
+ }
+ }
+ return nil, fmt.Errorf("%s: unknown reader", reader)
+}
+
+// Readers returns a list of the reader names.
+func Readers() []string {
+ var l []string
+ for _, r := range readerList {
+ l = append(l, r.Name())
+ }
+ return l
+}
+
+// RegisterReader will add this reader to the registered list of readers.
+func RegisterReader(reader Reader) {
+ readerList = append(readerList, reader)
+}
diff --git a/util/pinmap/pm/reader_test.go b/util/pinmap/pm/reader_test.go
new file mode 100644
index 0000000000..3f3929e103
--- /dev/null
+++ b/util/pinmap/pm/reader_test.go
@@ -0,0 +1,52 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package pm_test
+
+import (
+ "testing"
+
+ "reflect"
+
+ "pinmap/pm"
+)
+
+type testReader struct {
+ name string
+ arg string
+ chip string
+ pins pm.Pins
+}
+
+func (r *testReader) Name() string {
+ return r.name
+}
+
+func (r *testReader) Read(arg, chip string) (*pm.Pins, error) {
+ r.arg = arg
+ r.chip = chip
+ return &r.pins, nil
+}
+
+func TestReader(t *testing.T) {
+ n := "Test1"
+ tr1 := &testReader{name: n}
+ pm.RegisterReader(tr1)
+ p, err := pm.ReadPins(n, "arg1", "chiptest")
+ if err != nil {
+ t.Errorf("Error %v on reading pins", err)
+ }
+ if p != &tr1.pins {
+ t.Errorf("Did not match Pins")
+ }
+ p, err = pm.ReadPins("notMine", "arg1", "chiptest")
+ if err == nil {
+ t.Errorf("Should heve returned error")
+ }
+ readers := pm.Readers()
+ exp := []string{n}
+ if !reflect.DeepEqual(exp, readers) {
+ t.Errorf("Expected %v, got %v", exp, readers)
+ }
+}
diff --git a/util/pinmap/readers/csv/csv.go b/util/pinmap/readers/csv/csv.go
new file mode 100644
index 0000000000..e7677671de
--- /dev/null
+++ b/util/pinmap/readers/csv/csv.go
@@ -0,0 +1,117 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package csv
+
+import (
+ "bufio"
+ "encoding/csv"
+ "fmt"
+ "os"
+
+ "pinmap/pm"
+)
+
+// CSVReader reads the EC pin references from a comma separated
+// values file.
+type CSVReader struct {
+}
+
+// Name returns the name of this reader.
+func (r *CSVReader) Name() string {
+ return "csv"
+}
+
+// Read reads the CSV file (provided as the argument) and extracts
+// the pin reference data. The first line is expected to be column
+// titles that are used to identify the columns.
+func (r *CSVReader) Read(chipName, arg string) (*pm.Pins, error) {
+ f, err := os.Open(arg)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ rdr := csv.NewReader(bufio.NewReader(f))
+ data, err := rdr.ReadAll()
+ if err != nil {
+ return nil, err
+ }
+ if len(data) < 2 {
+ return nil, fmt.Errorf("no data in file")
+ }
+ // Put the CSV headers into a map.
+ cmap := make(map[string]int)
+ for c, s := range data[0] {
+ cmap[s] = c
+ }
+ // Find the matching columns that are needed.
+ signal, ok := cmap["Signal Name"]
+ if !ok {
+ return nil, fmt.Errorf("missing 'Signal Name' column")
+ }
+ // Find chip column
+ chip, ok := cmap[chipName]
+ if !ok {
+ return nil, fmt.Errorf("missing '%s' chip column", chipName)
+ }
+ ptype, ok := cmap["Type"]
+ if !ok {
+ return nil, fmt.Errorf("missing 'Type' column")
+ }
+ enum, ok := cmap["Enum"]
+ if !ok {
+ return nil, fmt.Errorf("missing 'Enum' column")
+ }
+ var pins pm.Pins
+ // Read the rest of the rows.
+ for i, row := range data[1:] {
+ p := new(pm.Pin)
+ switch row[ptype] {
+ default:
+ fmt.Printf("%s:%d: Unknown signal type (%s) - ignored", arg, i+1, row[ptype])
+ continue
+ case "OTHER":
+ // Skipped
+ continue
+ case "ADC":
+ p.PinType = pm.ADC
+ pins.Adc = append(pins.Adc, p)
+ case "PWM":
+ p.PinType = pm.PWM
+ pins.Pwm = append(pins.Pwm, p)
+ case "PWM_INVERT":
+ p.PinType = pm.PWM_INVERT
+ pins.Pwm = append(pins.Pwm, p)
+ case "I2C_DATA":
+ // Only the clock pin is used for the config
+ continue
+ case "I2C_CLOCK":
+ p.PinType = pm.I2C
+ pins.I2c = append(pins.I2c, p)
+ case "INPUT":
+ p.PinType = pm.Input
+ pins.Gpio = append(pins.Gpio, p)
+ case "INPUT_PU":
+ p.PinType = pm.InputPU
+ pins.Gpio = append(pins.Gpio, p)
+ case "INPUT_PD":
+ p.PinType = pm.InputPD
+ pins.Gpio = append(pins.Gpio, p)
+ case "OUTPUT":
+ p.PinType = pm.Output
+ pins.Gpio = append(pins.Gpio, p)
+ case "OUTPUT_ODL":
+ p.PinType = pm.OutputODL
+ pins.Gpio = append(pins.Gpio, p)
+ case "OUTPUT_ODR":
+ p.PinType = pm.OutputOD
+ pins.Gpio = append(pins.Gpio, p)
+ }
+ p.Signal = row[signal]
+ p.Pin = row[chip]
+ p.Enum = row[enum]
+ }
+
+ return &pins, nil
+}
diff --git a/util/pinmap/readers/csv/csv_test.go b/util/pinmap/readers/csv/csv_test.go
new file mode 100644
index 0000000000..94b134ee79
--- /dev/null
+++ b/util/pinmap/readers/csv/csv_test.go
@@ -0,0 +1,63 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package csv_test
+
+import (
+ "testing"
+
+ "path/filepath"
+ "reflect"
+
+ "pinmap/pm"
+ "pinmap/readers/csv"
+)
+
+const chipName = "MyCHIP"
+
+func TestName(t *testing.T) {
+ var r csv.CSVReader
+ if r.Name() != "csv" {
+ t.Errorf("expected %s, got %s", "csv", r.Name())
+ }
+ pins, err := r.Read(chipName, filepath.Join("testdata", "data.csv"))
+ if err != nil {
+ t.Fatalf("data.csv: %v", err)
+ }
+ exp := &pm.Pins{
+ Adc: []*pm.Pin{
+ &pm.Pin{pm.ADC, "A1", "EC_ADC_1", "ENUM_ADC_1"},
+ },
+ I2c: []*pm.Pin{
+ &pm.Pin{pm.I2C, "G7", "EC_I2C_CLK_0", "SENSOR"},
+ },
+ Gpio: []*pm.Pin{
+ &pm.Pin{pm.Input, "D4", "EC_GPIO_1", "GPIO1"},
+ &pm.Pin{pm.Output, "E5", "EC_GPIO_2", "GPIO2"},
+ &pm.Pin{pm.OutputODL, "F6", "EC_GPIO_3", ""},
+ &pm.Pin{pm.InputPU, "K10", "EC_GPIO_4", ""},
+ },
+ Pwm: []*pm.Pin{
+ &pm.Pin{pm.PWM, "C3", "EC_PWM_1", "FAN_1"},
+ &pm.Pin{pm.PWM_INVERT, "J9", "EC_PWM_2", "LED_1"},
+ },
+ }
+ check(t, "ADc", exp.Adc, pins.Adc)
+ check(t, "I2c", exp.I2c, pins.I2c)
+ check(t, "Gpio", exp.Gpio, pins.Gpio)
+ check(t, "Pwm", exp.Pwm, pins.Pwm)
+}
+
+func check(t *testing.T, name string, exp, got []*pm.Pin) {
+ if !reflect.DeepEqual(exp, got) {
+ t.Errorf("%s - expected:", name)
+ for _, p := range exp {
+ t.Errorf("%v", *p)
+ }
+ t.Errorf("got:")
+ for _, p := range got {
+ t.Errorf("%v", *p)
+ }
+ }
+}
diff --git a/util/pinmap/readers/csv/register.go b/util/pinmap/readers/csv/register.go
new file mode 100644
index 0000000000..b2f2529061
--- /dev/null
+++ b/util/pinmap/readers/csv/register.go
@@ -0,0 +1,13 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package csv
+
+import (
+ "pinmap/pm"
+)
+
+func init() {
+ pm.RegisterReader(&CSVReader{})
+}
diff --git a/util/pinmap/readers/csv/testdata/data.csv b/util/pinmap/readers/csv/testdata/data.csv
new file mode 100644
index 0000000000..6c7ac5ace9
--- /dev/null
+++ b/util/pinmap/readers/csv/testdata/data.csv
@@ -0,0 +1,11 @@
+Signal Name,MyCHIP,Type,Enum
+EC_ADC_1,A1,ADC,ENUM_ADC_1
+EC_IGNORED_1,B2,OTHER,
+EC_PWM_1,C3,PWM,FAN_1
+EC_PWM_2,J9,PWM_INVERT,LED_1
+EC_GPIO_1,D4,INPUT,GPIO1
+EC_GPIO_2,E5,OUTPUT,GPIO2
+EC_GPIO_3,F6,OUTPUT_ODL,
+EC_I2C_CLK_0,G7,I2C_CLOCK,SENSOR
+EC_I2C_DATA_0,H8,I2C_DATA,
+EC_GPIO_4,K10,INPUT_PU,