| // Copyright 2026 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| package main | |
| import ( | |
| "cmp" | |
| "fmt" | |
| "io" | |
| "maps" | |
| "regexp" | |
| "slices" | |
| "strconv" | |
| "strings" | |
| ) | |
| var ( | |
| digitsRegex = regexp.MustCompile(`[0-9]+`) | |
| npcxSclPinRegex = regexp.MustCompile(`I2C([0-9]+)_SCL([0-9]+)`) | |
| npcxI2cIndexRegex = regexp.MustCompile(`I2C[0-9]+_(SCL|SDA)([0-9]+)`) | |
| netNameReplacer = strings.NewReplacer(" ", "", "/", "_", "#", "_L") | |
| ) | |
| type gpioPin struct { | |
| label string | |
| pin string | |
| port string | |
| pinNum int | |
| flags string | |
| enum string | |
| } | |
| type adcPin struct { | |
| label string | |
| pin string | |
| channel int | |
| enum string | |
| } | |
| type i2cPort struct { | |
| label string | |
| pin string | |
| channel string | |
| portNode string | |
| enums []string | |
| } | |
| // dtsiGenerator holds the state for generating the DTSI file. | |
| type dtsiGenerator struct { | |
| w io.Writer | |
| chip *Chip | |
| gpios []gpioPin | |
| adcs []adcPin | |
| i2cPorts []i2cPort | |
| i2cEnables map[string][]string | |
| pwmEnables map[int]string | |
| adcEnabled bool | |
| adcPinctrls []string | |
| tachEnables map[int]string | |
| tachPorts map[int]string | |
| kbPins []string | |
| maxKsi int | |
| maxKso int | |
| } | |
| func newDTSIGenerator(w io.Writer, chip *Chip) *dtsiGenerator { | |
| return &dtsiGenerator{ | |
| w: w, | |
| chip: chip, | |
| i2cEnables: make(map[string][]string), | |
| pwmEnables: make(map[int]string), | |
| tachEnables: make(map[int]string), | |
| tachPorts: make(map[int]string), | |
| maxKsi: -1, | |
| maxKso: -1, | |
| } | |
| } | |
| // GenerateDTSI generates the Zephyr DTSI content and writes it to w. | |
| func GenerateDTSI(w io.Writer, configs []PinConfig, chip *Chip) error { | |
| g := newDTSIGenerator(w, chip) | |
| processedPins := make(map[string]bool) | |
| for _, cfg := range configs { | |
| if processedPins[cfg.PinNumber] { | |
| continue | |
| } | |
| processedPins[cfg.PinNumber] = true | |
| if cfg.NetName == "" { | |
| continue | |
| } | |
| netName := cfg.NetName | |
| if cfg.NetNameOverride != "" { | |
| netName = cfg.NetNameOverride | |
| } | |
| netName = cleanNetName(netName) | |
| // Check excluded net names | |
| if strings.HasPrefix(netName, "ESPI_") || strings.HasPrefix(netName, "UART_") || netName == "EC_RST_ODL" { | |
| continue | |
| } | |
| if cfg.PinTypeOverride == "OTHER" { | |
| continue | |
| } | |
| port, pinNum, gpioErr := chip.ResolveGPIO(cfg.PinNumber) | |
| gpName := "" | |
| if gpioErr == nil { | |
| gpName = fmt.Sprintf("gp%s%d", port, pinNum) | |
| } | |
| // Try processing as Keyboard matrix | |
| if g.tryProcessKeyboard(cfg, netName, gpName) { | |
| continue | |
| } | |
| // Try processing as Tachometer | |
| processedTach, err := g.tryProcessTach(cfg, netName, gpName) | |
| if err != nil { | |
| return err | |
| } | |
| if processedTach { | |
| continue | |
| } | |
| // Try processing as ADC | |
| processedADC, err := g.tryProcessADC(cfg, netName, gpName) | |
| if err != nil { | |
| return err | |
| } | |
| if processedADC { | |
| continue | |
| } | |
| // Try processing as PWM | |
| processedPWM, err := g.tryProcessPWM(cfg, port, pinNum, gpName) | |
| if err != nil { | |
| return err | |
| } | |
| if processedPWM { | |
| continue | |
| } | |
| // Try processing as I2C | |
| processedI2C, err := g.tryProcessI2C(cfg, netName, port, pinNum, gpName) | |
| if err != nil { | |
| return err | |
| } | |
| if processedI2C { | |
| continue | |
| } | |
| // Process as regular GPIO | |
| if isGPIO(cfg) { | |
| if gpioErr != nil { | |
| return fmt.Errorf("failed to resolve GPIO for pin %s: %v", cfg.PinNumber, gpioErr) | |
| } | |
| g.processGPIO(cfg, netName, port, pinNum, gpName) | |
| } | |
| } | |
| return g.write() | |
| } | |
| func (g *dtsiGenerator) tryProcessKeyboard(cfg PinConfig, netName, gpName string) bool { | |
| isKB := cfg.PinTypeOverride == "KEYBOARD" | |
| if !isKB { | |
| return false | |
| } | |
| prefix := "kso" | |
| if strings.Contains(netName, "KSI") { | |
| prefix = "ksi" | |
| } | |
| idx := findDigits(netName) | |
| if idx == "" { | |
| return false | |
| } | |
| idxNum, err := strconv.Atoi(idx) | |
| if err != nil { | |
| return false | |
| } | |
| if prefix == "ksi" { | |
| if idxNum > g.maxKsi { | |
| g.maxKsi = idxNum | |
| } | |
| } else { | |
| if idxNum > g.maxKso { | |
| g.maxKso = idxNum | |
| } | |
| } | |
| if len(idx) > 1 && idx[0] == '0' { | |
| if prefix == "ksi" || !g.chip.IsNPCX() { | |
| idx = idx[1:] | |
| } | |
| } | |
| if g.chip.IsNPCX() { | |
| if gpName != "" { | |
| g.kbPins = append(g.kbPins, fmt.Sprintf("&%s%s_%s", prefix, idx, gpName)) | |
| } | |
| } else { | |
| g.kbPins = append(g.kbPins, fmt.Sprintf("&%s%s_default", prefix, idx)) | |
| } | |
| return true | |
| } | |
| func (g *dtsiGenerator) tryProcessTach(cfg PinConfig, netName, gpName string) (bool, error) { | |
| isTach := cfg.PinTypeOverride == "TACH" || strings.HasSuffix(netName, "_TACH") | |
| if !isTach { | |
| return false, nil | |
| } | |
| chanNum, portLetter, err := g.chip.ResolveTACH(cfg.PinNumber) | |
| if err != nil { | |
| return false, fmt.Errorf("failed to resolve TACH for pin %s: %w", cfg.PinNumber, err) | |
| } | |
| if g.chip.IsNPCX() { | |
| pinctrl := fmt.Sprintf("&t%s%d_1_in_%s", strings.ToLower(portLetter), chanNum, gpName) | |
| g.tachEnables[chanNum] = pinctrl | |
| g.tachPorts[chanNum] = portLetter | |
| } else { | |
| // ITE | |
| pinctrl := fmt.Sprintf("&tach%d%s_%s_default", chanNum, strings.ToLower(portLetter), gpName) | |
| g.tachEnables[chanNum] = pinctrl | |
| g.tachPorts[chanNum] = portLetter | |
| } | |
| return true, nil | |
| } | |
| func (g *dtsiGenerator) tryProcessADC(cfg PinConfig, netName, gpName string) (bool, error) { | |
| isADC := cfg.IO == "ADC" || cfg.PinTypeOverride == "ADC" | |
| if !isADC { | |
| return false, nil | |
| } | |
| adcChan, err := g.chip.ResolveADC(cfg.PinNumber) | |
| if err != nil { | |
| return false, fmt.Errorf("failed to resolve ADC for pin %s: %v", cfg.PinNumber, err) | |
| } | |
| g.adcEnabled = true | |
| label := fmt.Sprintf("adc_%s", strings.ToLower(netName)) | |
| g.adcs = append(g.adcs, adcPin{ | |
| label: label, | |
| pin: strings.ToLower(netName), | |
| channel: adcChan, | |
| enum: cfg.Enum, | |
| }) | |
| if gpName != "" { | |
| if g.chip.IsNPCX() { | |
| g.adcPinctrls = append(g.adcPinctrls, fmt.Sprintf("&adc0_chan%d_%s", adcChan, gpName)) | |
| } else { | |
| g.adcPinctrls = append(g.adcPinctrls, fmt.Sprintf("&adc0_ch%d_%s_default", adcChan, gpName)) | |
| } | |
| } | |
| return true, nil | |
| } | |
| func (g *dtsiGenerator) tryProcessPWM(cfg PinConfig, port string, pinNum int, gpName string) (bool, error) { | |
| isPWM := cfg.IO == "PWM" || cfg.PinTypeOverride == "PWM" || cfg.PinTypeOverride == "PWM_INVERT" | |
| if !isPWM { | |
| return false, nil | |
| } | |
| pwmChan, err := g.chip.ResolvePWM(cfg.PinNumber) | |
| if err != nil { | |
| return false, fmt.Errorf("failed to resolve PWM for pin %s: %v", cfg.PinNumber, err) | |
| } | |
| if gpName != "" { | |
| pinctrl := fmt.Sprintf("&pwm%d_gp%s%d", pwmChan, port, pinNum) | |
| if !g.chip.IsNPCX() { | |
| pinctrl = fmt.Sprintf("&pwm%d_%s_default", pwmChan, gpName) | |
| } | |
| g.pwmEnables[pwmChan] = pinctrl | |
| } | |
| return true, nil | |
| } | |
| func (g *dtsiGenerator) tryProcessI2C(cfg PinConfig, netName string, port string, pinNum int, gpName string) (bool, error) { | |
| isI2C := cfg.IO == "OD" && (cfg.PinTypeOverride == "I2C_CLOCK" || cfg.PinTypeOverride == "I2C_DATA" || cfg.PinTypeOverride == "I2C") | |
| if !isI2C { | |
| return false, nil | |
| } | |
| i2cChan, isScl, err := g.chip.ResolveI2C(cfg.PinNumber) | |
| if err != nil { | |
| return false, fmt.Errorf("failed to resolve I2C for pin %s: %v", cfg.PinNumber, err) | |
| } | |
| if !isScl { | |
| return true, nil | |
| } | |
| baseName := strings.ToLower(netName) | |
| baseName = strings.TrimSuffix(baseName, "_scl") | |
| label := fmt.Sprintf("i2c_%s", baseName) | |
| portName := baseName | |
| var enums []string | |
| if cfg.Enum != "" { | |
| enums = append(enums, cfg.Enum) | |
| } | |
| portNode := fmt.Sprintf("i2c%s", i2cChan) | |
| if g.chip.IsNPCX() { | |
| pinDef := g.chip.PinMap[cfg.PinNumber] | |
| idx := extractNPCXI2CIndex(pinDef) | |
| portNode = fmt.Sprintf("i2c%s_%d", i2cChan, idx) | |
| } | |
| g.i2cPorts = append(g.i2cPorts, i2cPort{ | |
| label: label, | |
| pin: portName, | |
| channel: i2cChan, | |
| portNode: portNode, | |
| enums: enums, | |
| }) | |
| if g.chip.IsNPCX() { | |
| sdaGP, err := findNPCXSDAGPIO(g.chip, cfg.PinNumber) | |
| if err != nil { | |
| return false, fmt.Errorf("failed to find NPCX SDA GPIO for pin %s: %v", cfg.PinNumber, err) | |
| } | |
| pinDef := g.chip.PinMap[cfg.PinNumber] | |
| idx := extractNPCXI2CIndex(pinDef) | |
| sclGP := fmt.Sprintf("gp%s%d", port, pinNum) | |
| gpios := []string{sdaGP, sclGP} | |
| slices.Sort(gpios) | |
| first := gpios[0] | |
| second := strings.TrimPrefix(gpios[1], "gp") | |
| pinctrl := fmt.Sprintf("&i2c%s_%d_sda_scl_%s_%s", i2cChan, idx, first, second) | |
| nodeName := fmt.Sprintf("i2c%s_%d", i2cChan, idx) | |
| g.i2cEnables[nodeName] = []string{pinctrl} | |
| } else { | |
| // ITE | |
| sdaGP, err := findITESDAGPIO(g.chip, cfg.PinNumber) | |
| if err != nil { | |
| return false, fmt.Errorf("failed to find ITE SDA GPIO for pin %s: %v", cfg.PinNumber, err) | |
| } | |
| i2cNodeTrim := "i2c" + strings.TrimSuffix(i2cChan, "ALT") | |
| clkPinctrl := fmt.Sprintf("&%s_clk_%s_default", i2cNodeTrim, gpName) | |
| sdaPinctrl := fmt.Sprintf("&%s_data_%s_default", i2cNodeTrim, sdaGP) | |
| nodeName := "i2c" + i2cChan | |
| g.i2cEnables[nodeName] = append(g.i2cEnables[nodeName], clkPinctrl, sdaPinctrl) | |
| } | |
| return true, nil | |
| } | |
| func (g *dtsiGenerator) processGPIO(cfg PinConfig, netName, port string, pinNum int, gpName string) { | |
| if gpName != "" { | |
| label := fmt.Sprintf("gpio_%s", strings.ToLower(netName)) | |
| flags := determineGPIOFlags(cfg) | |
| g.gpios = append(g.gpios, gpioPin{ | |
| label: label, | |
| pin: strings.ToLower(netName), | |
| port: port, | |
| pinNum: pinNum, | |
| flags: flags, | |
| enum: cfg.Enum, | |
| }) | |
| } | |
| } | |
| func (g *dtsiGenerator) write() error { | |
| ew := &errWriter{w: g.w} | |
| g.w = ew | |
| // Sort lists deterministically | |
| slices.SortStableFunc(g.gpios, func(a, b gpioPin) int { return cmp.Compare(a.pin, b.pin) }) | |
| slices.SortStableFunc(g.adcs, func(a, b adcPin) int { return cmp.Compare(a.pin, b.pin) }) | |
| slices.SortStableFunc(g.i2cPorts, func(a, b i2cPort) int { return cmp.Compare(a.pin, b.pin) }) | |
| slices.Sort(g.adcPinctrls) | |
| // Write header | |
| fmt.Fprintln(g.w, "/* Copyright 2026 The ChromiumOS Authors") | |
| fmt.Fprintln(g.w, " * Use of this source code is governed by a BSD-style license that can be") | |
| fmt.Fprintln(g.w, " * found in the LICENSE file.") | |
| fmt.Fprintln(g.w, " *") | |
| fmt.Fprintln(g.w, " * This file is auto-generated - do not edit!") | |
| fmt.Fprintln(g.w, " */") | |
| fmt.Fprintln(g.w) | |
| fmt.Fprintln(g.w, "/ {") | |
| fmt.Fprintln(g.w) | |
| // Write ADC channels | |
| if len(g.adcs) > 0 { | |
| fmt.Fprintln(g.w, "\tnamed-adc-channels {") | |
| fmt.Fprintln(g.w, "\t\tcompatible = \"named-adc-channels\";") | |
| fmt.Fprintln(g.w) | |
| for _, adc := range g.adcs { | |
| fmt.Fprintf(g.w, "\t\t%s: %s {\n", adc.label, adc.pin) | |
| if adc.enum != "" { | |
| fmt.Fprintf(g.w, "\t\t\tenum-name = \"%s\";\n", adc.enum) | |
| } | |
| fmt.Fprintf(g.w, "\t\t\tio-channels = <&adc0 %d>;\n", adc.channel) | |
| fmt.Fprintln(g.w, "\t\t};") | |
| } | |
| fmt.Fprintln(g.w, "\t};") | |
| fmt.Fprintln(g.w) | |
| } | |
| // Write Named GPIOs | |
| if len(g.gpios) > 0 { | |
| fmt.Fprintln(g.w, "\tnamed-gpios {") | |
| fmt.Fprintln(g.w, "\t\tcompatible = \"named-gpios\";") | |
| fmt.Fprintln(g.w) | |
| for _, gp := range g.gpios { | |
| fmt.Fprintf(g.w, "\t\t%s: %s {\n", gp.label, gp.pin) | |
| fmt.Fprintf(g.w, "\t\t\tgpios = <&gpio%s %d %s>;\n", gp.port, gp.pinNum, gp.flags) | |
| if gp.enum != "" { | |
| fmt.Fprintf(g.w, "\t\t\tenum-name = \"%s\";\n", gp.enum) | |
| } | |
| fmt.Fprintln(g.w, "\t\t};") | |
| } | |
| fmt.Fprintln(g.w, "\t};") | |
| fmt.Fprintln(g.w) | |
| } | |
| // Write Named I2C ports | |
| if len(g.i2cPorts) > 0 { | |
| fmt.Fprintln(g.w, "\tnamed-i2c-ports {") | |
| fmt.Fprintln(g.w, "\t\tcompatible = \"named-i2c-ports\";") | |
| fmt.Fprintln(g.w) | |
| for _, i2c := range g.i2cPorts { | |
| fmt.Fprintf(g.w, "\t\t%s: %s {\n", i2c.label, i2c.pin) | |
| fmt.Fprintf(g.w, "\t\t\ti2c-port = <&%s>;\n", strings.TrimSuffix(i2c.portNode, "ALT")) | |
| if len(i2c.enums) > 0 { | |
| fmt.Fprintf(g.w, "\t\t\tenum-names = \"%s\";\n", strings.Join(i2c.enums, "\", \"")) | |
| } | |
| fmt.Fprintln(g.w, "\t\t};") | |
| } | |
| fmt.Fprintln(g.w, "\t};") | |
| fmt.Fprintln(g.w) | |
| } | |
| fmt.Fprintln(g.w, "};") | |
| fmt.Fprintln(g.w) | |
| // Write ADC pinctrl | |
| if len(g.adcPinctrls) > 0 { | |
| fmt.Fprintln(g.w, "&adc0 {") | |
| fmt.Fprintln(g.w, "\tstatus = \"okay\";") | |
| if len(g.adcPinctrls) == 1 { | |
| fmt.Fprintf(g.w, "\tpinctrl-0 = <%s>;\n", g.adcPinctrls[0]) | |
| } else { | |
| fmt.Fprintln(g.w, "\tpinctrl-0 = <") | |
| for _, p := range g.adcPinctrls { | |
| fmt.Fprintf(g.w, "\t\t%s\n", p) | |
| } | |
| fmt.Fprintln(g.w, "\t>;") | |
| } | |
| fmt.Fprintln(g.w, "\tpinctrl-names = \"default\";") | |
| fmt.Fprintln(g.w, "};") | |
| fmt.Fprintln(g.w) | |
| } | |
| // Write I2C pinctrl | |
| i2cNodes := slices.Collect(maps.Keys(g.i2cEnables)) | |
| slices.Sort(i2cNodes) | |
| for _, nodeName := range i2cNodes { | |
| pinctrls := g.i2cEnables[nodeName] | |
| fmt.Fprintf(g.w, "&%s {\n", nodeName) | |
| fmt.Fprintln(g.w, "\tstatus = \"okay\";") | |
| if len(pinctrls) == 1 { | |
| fmt.Fprintf(g.w, "\tpinctrl-0 = <%s>;\n", pinctrls[0]) | |
| } else { | |
| fmt.Fprintln(g.w, "\tpinctrl-0 = <") | |
| for _, p := range pinctrls { | |
| fmt.Fprintf(g.w, "\t\t%s\n", p) | |
| } | |
| fmt.Fprintln(g.w, "\t>;") | |
| } | |
| fmt.Fprintln(g.w, "\tpinctrl-names = \"default\";") | |
| fmt.Fprintln(g.w, "};") | |
| fmt.Fprintln(g.w) | |
| } | |
| // Write PWM pinctrl | |
| pwmChans := slices.Collect(maps.Keys(g.pwmEnables)) | |
| slices.Sort(pwmChans) | |
| for _, ch := range pwmChans { | |
| fmt.Fprintf(g.w, "&pwm%d {\n", ch) | |
| fmt.Fprintln(g.w, "\tstatus = \"okay\";") | |
| fmt.Fprintf(g.w, "\tpinctrl-0 = <%s>;\n", g.pwmEnables[ch]) | |
| fmt.Fprintln(g.w, "\tpinctrl-names = \"default\";") | |
| fmt.Fprintln(g.w, "};") | |
| fmt.Fprintln(g.w) | |
| } | |
| // Write Tach pinctrl | |
| tachChans := slices.Collect(maps.Keys(g.tachEnables)) | |
| slices.Sort(tachChans) | |
| for _, ch := range tachChans { | |
| if g.chip.IsNPCX() { | |
| portLetter := g.tachPorts[ch] | |
| fmt.Fprintf(g.w, "&tach%d {\n", ch) | |
| fmt.Fprintln(g.w, "\tstatus = \"okay\";") | |
| fmt.Fprintf(g.w, "\tpinctrl-0 = <%s>;\n", g.tachEnables[ch]) | |
| fmt.Fprintln(g.w, "\tpinctrl-names = \"default\";") | |
| fmt.Fprintf(g.w, "\tport = <&gpio%s>;\n", strings.ToLower(portLetter)) | |
| fmt.Fprintln(g.w, "};") | |
| fmt.Fprintln(g.w) | |
| } else { | |
| fmt.Fprintf(g.w, "&tach%d {\n", ch) | |
| fmt.Fprintln(g.w, "\tstatus = \"okay\";") | |
| fmt.Fprintf(g.w, "\tpinctrl-0 = <%s>;\n", g.tachEnables[ch]) | |
| fmt.Fprintln(g.w, "\tpinctrl-names = \"default\";") | |
| fmt.Fprintln(g.w, "};") | |
| fmt.Fprintln(g.w) | |
| } | |
| } | |
| // Write Keyboard matrix pinctrl | |
| if len(g.kbPins) > 0 { | |
| slices.Sort(g.kbPins) | |
| fmt.Fprintln(g.w, "&kbd {") | |
| fmt.Fprintln(g.w, "\tstatus = \"okay\";") | |
| fmt.Fprintln(g.w, "\tpinctrl-0 = <") | |
| for _, p := range g.kbPins { | |
| fmt.Fprintf(g.w, "\t\t%s\n", p) | |
| } | |
| fmt.Fprintln(g.w, "\t>;") | |
| fmt.Fprintln(g.w, "\tpinctrl-names = \"default\";") | |
| rowSize := 0 | |
| if g.maxKsi >= 0 { | |
| rowSize = g.maxKsi + 1 | |
| } | |
| colSize := 0 | |
| if g.maxKso >= 0 { | |
| colSize = g.maxKso + 1 | |
| } | |
| fmt.Fprintf(g.w, "\trow-size = <%d>;\n", rowSize) | |
| fmt.Fprintf(g.w, "\tcol-size = <%d>;\n", colSize) | |
| fmt.Fprintln(g.w, "};") | |
| } | |
| return ew.err | |
| } | |
| func findSDAGPIO(chip *Chip, sdaFunc string) (string, error) { | |
| var pins []string | |
| if len(chip.PkgPins) > 0 { | |
| pins = slices.Clone(chip.PkgPins) | |
| } else { | |
| pins = slices.Collect(maps.Keys(chip.PinMap)) | |
| slices.Sort(pins) | |
| } | |
| for _, pinNum := range pins { | |
| def := chip.PinMap[pinNum] | |
| if hasFunction(def, sdaFunc) { | |
| port, pin, err := chip.ResolveGPIO(pinNum) | |
| if err != nil { | |
| return "", err | |
| } | |
| return fmt.Sprintf("gp%s%d", port, pin), nil | |
| } | |
| } | |
| return "", fmt.Errorf("matching SDA pin function %s not found in chip map", sdaFunc) | |
| } | |
| func findNPCXSDAGPIO(chip *Chip, sclPinNum string) (string, error) { | |
| sclDef := chip.PinMap[sclPinNum] | |
| m := npcxSclPinRegex.FindStringSubmatch(sclDef) | |
| if m == nil { | |
| return "", fmt.Errorf("not an NPCX SCL pin definition: %s", sclDef) | |
| } | |
| sdaFunc := fmt.Sprintf("I2C%s_SDA%s", m[1], m[2]) | |
| return findSDAGPIO(chip, sdaFunc) | |
| } | |
| func findITESDAGPIO(chip *Chip, sclPinNum string) (string, error) { | |
| sclDef := chip.PinMap[sclPinNum] | |
| m := iteScl.FindStringSubmatch(sclDef) | |
| if m == nil { | |
| return "", fmt.Errorf("not an ITE SCL pin definition: %s", sclDef) | |
| } | |
| sdaFunc := fmt.Sprintf("SMDAT%s", m[1]) | |
| return findSDAGPIO(chip, sdaFunc) | |
| } | |
| func extractNPCXI2CIndex(pinDef string) int { | |
| m := npcxI2cIndexRegex.FindStringSubmatch(pinDef) | |
| if m != nil { | |
| idx, err := strconv.Atoi(m[2]) | |
| if err == nil { | |
| return idx | |
| } | |
| } | |
| return 0 | |
| } | |
| func cleanNetName(name string) string { | |
| return netNameReplacer.Replace(name) | |
| } | |
| func isGPIO(cfg PinConfig) bool { | |
| if cfg.Dir == "IN" || cfg.Dir == "OUT" { | |
| return true | |
| } | |
| switch cfg.PinTypeOverride { | |
| case "INPUT", "INPUT_L", "OUTPUT", "OUTPUT_L", "OUTPUT_ACTIVE_L", "OUTPUT_ACTIVE_H", "ODR_ACTIVE_L", "ODR_ACTIVE_H", "OUTPUT_ODL", "OUTPUT_ODR": | |
| return true | |
| } | |
| return false | |
| } | |
| func hasFunction(pinDef, function string) bool { | |
| parts := strings.Split(pinDef, "/") | |
| for _, part := range parts { | |
| if strings.TrimSpace(part) == function { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| func findDigits(s string) string { | |
| return digitsRegex.FindString(s) | |
| } | |
| func determineGPIOFlags(cfg PinConfig) string { | |
| var flags []string | |
| isOutput := cfg.Dir == "OUT" || | |
| cfg.PinTypeOverride == "OUTPUT" || | |
| cfg.PinTypeOverride == "OUTPUT_L" || | |
| cfg.PinTypeOverride == "OUTPUT_H" || | |
| cfg.PinTypeOverride == "OUTPUT_ACTIVE_L" || | |
| cfg.PinTypeOverride == "OUTPUT_ACTIVE_H" || | |
| cfg.PinTypeOverride == "OUTPUT_ODL" || | |
| cfg.PinTypeOverride == "OUTPUT_ODR" || | |
| cfg.PinTypeOverride == "ODR_ACTIVE_L" || | |
| cfg.PinTypeOverride == "ODR_ACTIVE_H" | |
| if isOutput { | |
| flags = append(flags, "GPIO_OUTPUT") | |
| } else { | |
| flags = append(flags, "GPIO_INPUT") | |
| } | |
| isOD := cfg.IO == "OD" || | |
| cfg.PinTypeOverride == "OUTPUT_ODL" || | |
| cfg.PinTypeOverride == "OUTPUT_ODR" || | |
| cfg.PinTypeOverride == "ODR_ACTIVE_L" || | |
| cfg.PinTypeOverride == "ODR_ACTIVE_H" | |
| if isOD && (isOutput || cfg.Dir == "IO") { | |
| flags = append(flags, "GPIO_OPEN_DRAIN") | |
| } | |
| isActiveLow := cfg.PinTypeOverride == "INPUT_L" || | |
| cfg.PinTypeOverride == "OUTPUT_L" || | |
| cfg.PinTypeOverride == "OUTPUT_ACTIVE_L" || | |
| cfg.PinTypeOverride == "OUTPUT_ODL" || | |
| cfg.PinTypeOverride == "ODR_ACTIVE_L" | |
| if !isActiveLow && (cfg.PinTypeOverride == "" || cfg.PinTypeOverride == "INPUT" || cfg.PinTypeOverride == "OUTPUT") { | |
| cleanName := strings.ToLower(cfg.NetName) | |
| if strings.HasSuffix(cleanName, "_l") || strings.HasSuffix(cleanName, "_odl") || strings.HasSuffix(cleanName, "_low") { | |
| isActiveLow = true | |
| } | |
| } | |
| if isActiveLow { | |
| flags = append(flags, "GPIO_ACTIVE_LOW") | |
| } else { | |
| flags = append(flags, "GPIO_ACTIVE_HIGH") | |
| } | |
| if cfg.Pull == "UP" { | |
| flags = append(flags, "GPIO_PULL_UP") | |
| } else if cfg.Pull == "DOWN" { | |
| flags = append(flags, "GPIO_PULL_DOWN") | |
| } | |
| if isOutput { | |
| if cfg.InitialCondition == "HIGH" { | |
| flags = append(flags, "GPIO_OUTPUT_INIT_HIGH") | |
| } else if cfg.InitialCondition == "LOW" { | |
| flags = append(flags, "GPIO_OUTPUT_INIT_LOW") | |
| } | |
| } | |
| if cfg.Voltage == "1.8V" { | |
| flags = append(flags, "GPIO_VOLTAGE_1P8") | |
| } | |
| return "(" + strings.Join(flags, " | ") + ")" | |
| } | |
| type errWriter struct { | |
| w io.Writer | |
| err error | |
| } | |
| func (ew *errWriter) Write(p []byte) (int, error) { | |
| if ew.err != nil { | |
| return 0, ew.err | |
| } | |
| var n int | |
| n, ew.err = ew.w.Write(p) | |
| return n, ew.err | |
| } |