diff options
author | Andrew McRae <amcrae@google.com> | 2021-12-04 08:29:02 +1100 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-12-22 07:44:41 +0000 |
commit | 5656dcd4891cd2388066c6dcd81319c20cbfdbd1 (patch) | |
tree | 7492c172d136f9fb5c6f01901f112ae023149f23 | |
parent | 9be8edcfccb75d7a564a88218c3b845a7692ba74 (diff) | |
download | chrome-ec-5656dcd4891cd2388066c6dcd81319c20cbfdbd1.tar.gz |
pinmap: Add utility to generate Zephyr DTS GPIO configs
Add a pinmap utility to generate Zephyr DTS GPIO configs
from a spreadsheet.
v2: Fix test package names, add tests for reader and generator.
BUG=none
TEST=./pinmap --chip=NPCX993 --output=/tmp/x.dts /tmp/nissa.csv
BRANCH=none
Signed-off-by: Andrew McRae <amcrae@google.com>
Change-Id: Id48ca6670f019c742f41824d6d9c41cb747aaa97
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3347353
Reviewed-by: Eizan Miyamoto <eizan@chromium.org>
-rw-r--r-- | util/pinmap/README.md | 101 | ||||
-rw-r--r-- | util/pinmap/chips/it81302.go | 243 | ||||
-rw-r--r-- | util/pinmap/chips/npcx993.go | 239 | ||||
-rw-r--r-- | util/pinmap/chips/npcx993_test.go | 93 | ||||
-rw-r--r-- | util/pinmap/chips/register.go | 15 | ||||
-rw-r--r-- | util/pinmap/go.mod | 3 | ||||
-rw-r--r-- | util/pinmap/pinmap/main.go | 73 | ||||
-rw-r--r-- | util/pinmap/pm/chip.go | 73 | ||||
-rw-r--r-- | util/pinmap/pm/chip_test.go | 68 | ||||
-rw-r--r-- | util/pinmap/pm/generate.go | 163 | ||||
-rw-r--r-- | util/pinmap/pm/generate_test.go | 148 | ||||
-rw-r--r-- | util/pinmap/pm/pins.go | 32 | ||||
-rw-r--r-- | util/pinmap/pm/reader.go | 43 | ||||
-rw-r--r-- | util/pinmap/pm/reader_test.go | 52 | ||||
-rw-r--r-- | util/pinmap/readers/csv/csv.go | 111 | ||||
-rw-r--r-- | util/pinmap/readers/csv/csv_test.go | 61 | ||||
-rw-r--r-- | util/pinmap/readers/csv/register.go | 13 | ||||
-rw-r--r-- | util/pinmap/readers/csv/testdata/data.csv | 9 |
18 files changed, 1540 insertions, 0 deletions
diff --git a/util/pinmap/README.md b/util/pinmap/README.md new file mode 100644 index 0000000000..13a295500a --- /dev/null +++ b/util/pinmap/README.md @@ -0,0 +1,101 @@ +# 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 intended to be the download 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](reader/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 | +| `I2C_CLOCK` | The clock signal for an I2C bus | +| `I2C_DATA` | The data signal for an I2C bus | +| `INPUT` | A GPIO input signal | +| `OUTPUT` | A GPIO output signal | +| `OUTPUT_ODR` | A GPIO output open drain signal | +| `OUTPUT_ODL` | A GPIO output open drain signal (default low) | +| `INTERRUPT_FALLING` | A GPIO input signal used as an interrupt (triggered when falling) | +| `INTERRUPT_RISING` | A GPIO input signal used as an interrupt (triggered when rising) | +| `INTERRUPT_BOTH` | A GPIO input signal used as an interrupt (triggered both directions) | +| `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 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 + +- Add unit tests for generator.go +- 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..1a2d530ad5 --- /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 0", 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..60d1487b5e --- /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 { + pwm := fmt.Sprintf("pwm%s 0 0", ss[3:]) + c.okay = append(c.okay, pwm) + return pwm + } + } + 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..13307e4f0b --- /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 0 0" { + t.Errorf("Expected \"pwm4 0 0\", 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..8931b199f8 --- /dev/null +++ b/util/pinmap/pm/generate.go @@ -0,0 +1,163 @@ +// 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" +) + +// TODO(b/211717378): Fix the date handling +const header = `/* 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. + * + * 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 + fmt.Fprintf(out, "%s", header) + 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 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\tgpios = <&%s %s>;\n", c, gtype) + 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};\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) { + if pin.PinType != PWM { + fmt.Printf("Unknown PWM type (%d) for pin %s, ignored\n", pin.PinType, pin.Pin) + return + } + 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>;\n", c) + 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};\n") +} diff --git a/util/pinmap/pm/generate_test.go b/util/pinmap/pm/generate_test.go new file mode 100644 index 0000000000..06d6dea91a --- /dev/null +++ b/util/pinmap/pm/generate_test.go @@ -0,0 +1,148 @@ +// 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" + + "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"}, + }, + Pwm: []*pm.Pin{ + &pm.Pin{pm.PWM, "E5", "EC_LED_1", "ENUM_LED_1"}, + }, + } + 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. + */ + exp := + `/* 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. + * + * 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 { + gpios = <&gpio C3 GPIO_INPUT>; + label = "EC_IN_1"; + enum-name = "ENUM_IN_1"; + }; + gpio_ec_out_2: ec_out_2 { + gpios = <&gpio D4 GPIO_OUTPUT>; + label = "EC_OUT_2"; + 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>; + label = "EC_LED_1"; + enum-name = "ENUM_LED_1"; + }; + }; +}; + +&adc0 { + status = "okay"; +}; + +&i2c0 { + status = "okay"; +}; + +&pwm1 { + status = "okay"; +}; +` + got := out.String() + if exp != got { + // Split each string into lines and compare the lines. + expLines := strings.Split(exp, "\n") + gotLines := strings.Split(exp, "\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..771d7162b2 --- /dev/null +++ b/util/pinmap/pm/pins.go @@ -0,0 +1,32 @@ +// 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 + I2C + Input + 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..96430d3c9d --- /dev/null +++ b/util/pinmap/readers/csv/csv.go @@ -0,0 +1,111 @@ +// 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 "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", + "INTERRUPT_FALLING", + "INTERRUPT_RISING", + "INTERRUPT_BOTH": + p.PinType = pm.Input + 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..266c5274d0 --- /dev/null +++ b/util/pinmap/readers/csv/csv_test.go @@ -0,0 +1,61 @@ +// 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", ""}, + }, + Pwm: []*pm.Pin{ + &pm.Pin{pm.PWM, "C3", "EC_PWM_1", "FAN_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..d68006800f --- /dev/null +++ b/util/pinmap/readers/csv/testdata/data.csv @@ -0,0 +1,9 @@ +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_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, |