1#!/usr/bin/env python3 2# 3# Copyright (c) 2024 GARDENA GmbH 4# 5# SPDX-License-Identifier: Apache-2.0 6 7"""Generate crossbar register values from devicetree pinctrl nodes. 8 9The SiM3U1xx SoCs do support pinmuxing, but with many limimitations. It's also non-optional, so we 10can't just not support it and rely on default values. 11 12The hardware doesn't allow us to properly implement the pinmux API, but we still want to use the 13standard pinctrl nodes inside the devicetree to define which peripheral should be muxed to which 14pins. To accomplish that, this script parses these nodes and generates code that applies all 15`default` nodes of all enabled devices within soc.c. 16 17There's two crossbars: 18- crossbar 0: Controls portbanks 0 and 1 19- crossbar 1: Controls portbanks 2 and 3 20 21Each crossbar has two configuration-values which reside in different registers: 22- config: A bitmask which tells the crossbar which peripherals should be muxed. Some peripherals 23 have multiple bits to prevent having to mux unused pins (like UART flow control). 24- enable: A bit which enables or disables the whole crossbar. When disabled, all pins of the 25 crossbar are disconnected. 26 27And each portbank has this related config: 28- pbskipen: A bitmask where value `1` means, that the pin will not be muxed. 29 The index of the bit refers to the pin number. 30 31A crossbar has a list of signals that it tries to mux to pins that it controls. That list has a 32fixed order and signals that belong to the same peripheral are next to each other. 33The crossbar hardware simply iterates the signal list and assigns each signal to the lowest 34available pin. That has a few implications: 35 36- Pins of a peripheral are always consecutive within all non-skipped pins. 37- Peripherals that appear first in the signal list will always use lower pin-numbers than later ones 38- There's no way to change the muxing without side effects. Writing to pbskipen while the crossbar 39 is enabled might temporarily cause unwanted muxing. Disabling the crossbar will disconnect all 40 peripherals for a short time. 41 42The last point is the reason why we don't implement Zephyrs runtime pinmuxing API. Applying the 43pinmuxing one by one as drivers get initialized would require disconnecting pins quite often. 44""" 45 46import argparse 47import enum 48import os 49import pickle 50import sys 51 52sys.path.insert( 53 0, os.path.join(os.environ["ZEPHYR_BASE"], "scripts/dts/python-devicetree/src") 54) 55 56 57class Signal(enum.Enum): 58 USART0_TX = 0 59 USART0_RX = 1 60 USART0_RTS = 2 61 USART0_CTS = 3 62 USART0_UCLK = 4 63 64 USART1_TX = 9 65 USART1_RX = 10 66 USART1_RTS = 11 67 USART1_CTS = 12 68 USART1_UCLK = 13 69 70 SPI2_SCK = 50 71 SPI2_MISO = 51 72 SPI2_MOSI = 52 73 SPI2_NSS = 53 74 75 76class PinMode(enum.Enum): 77 ANALOG = 0 78 DIGITAL_INPUT = 1 79 PUSH_PULL_OUTPUT = 2 80 81 82class Pinmux: 83 def __init__(self, value, props): 84 self.pin = (value & 0x7, (value >> 3) & 0xFF) 85 self.signal = Signal((value >> 22) & 0x7F) 86 87 output_low = props["output-low"].val 88 output_high = props["output-high"].val 89 output_enable = props["output-enable"].val or output_low or output_high 90 91 input_enable = props["input-enable"].val 92 93 if output_enable and input_enable: 94 raise Exception("can't enable both output and input") 95 if output_low and output_high: 96 raise Exception("can't define output as both low and high") 97 98 if input_enable: 99 self.pinmode = PinMode.DIGITAL_INPUT 100 elif output_enable: 101 self.pinmode = PinMode.PUSH_PULL_OUTPUT 102 103 if output_low: 104 self.output_value = True 105 elif output_high: 106 self.output_value = False 107 else: 108 self.output_value = None 109 else: 110 self.pinmode = None 111 112 def __repr__(self): 113 return self.__dict__.__repr__() 114 115 116def parse_args(): 117 parser = argparse.ArgumentParser(allow_abbrev=False) 118 parser.add_argument( 119 "edt_pickle", help="path to read the pickled edtlib.EDT object from" 120 ) 121 parser.add_argument("out", help="path to write the header file") 122 123 return parser.parse_args() 124 125 126class CrossbarBit: 127 def __init__(self, bit, signals, pin_first, pin_last): 128 assert pin_first <= pin_last 129 130 self.bit = bit 131 self.signals = signals 132 self.pin_first = pin_first 133 self.pin_last = pin_last 134 135 def __repr__(self): 136 return self.__dict__.__repr__() 137 138 139class Crossbar: 140 def __init__(self, number, bits, portbanks): 141 self.number = number 142 self.value = 0 143 self.bits = bits 144 self.first_configurable = (0, 0) 145 self.portbanks = portbanks 146 147 def enable_bit(self, bit, pins): 148 if len(pins) != len(bit.signals): 149 raise Exception( 150 f"pins({pins}) and signals({bit.signals}) must be the same length" 151 ) 152 153 for pin in pins: 154 if pin < self.first_configurable: 155 raise Exception( 156 "can't enable crossbar pin anymore", pin, self.first_configurable 157 ) 158 159 self.portbanks[pin[0]].unskip(pin[1]) 160 161 self.first_configurable = (pin[0], pin[1] + 1) 162 163 self.value |= 1 << bit.bit 164 165 def mux(self, muxs): 166 for bit in self.bits: 167 # collect the signals that are enabled by this bit 168 signal_muxs = {} 169 for mux in muxs: 170 if self.number == 0 and mux.pin[0] != 0 and mux.pin[0] != 1: 171 continue 172 if self.number == 1 and mux.pin[0] != 2 and mux.pin[0] != 3: 173 continue 174 175 if mux.signal not in bit.signals: 176 continue 177 if mux.signal in signal_muxs: 178 raise Exception("duplicate signal", mux) 179 if mux.pin < bit.pin_first or mux.pin > bit.pin_last: 180 raise Exception("can't mux signal to pin", mux, bit) 181 182 signal_muxs[mux.signal] = mux 183 184 # this bit is disabled 185 if len(signal_muxs.keys()) == 0: 186 continue 187 # we have to enable all the signals 188 if len(signal_muxs.keys()) != len(bit.signals): 189 raise Exception("missing signals for bit", bit, signal_muxs) 190 191 # build pin list for this bit in the required order 192 pins = [] 193 for _index, signal in enumerate(bit.signals): 194 mux = signal_muxs[signal] 195 pins.append(mux.pin) 196 197 self.enable_bit(bit, pins) 198 199 for mux in signal_muxs.values(): 200 self.portbanks[mux.pin[0]].apply_mux(mux.pin[1], mux) 201 202 def __repr__(self): 203 return self.__dict__.__repr__() 204 205 206class Portbank: 207 def __init__(self): 208 self.pins_high = 0 209 self.pins_low = 0 210 self.pins_push_pull_output = 0 211 self.pins_digital_input = 0 212 self.pins_analog = 0 213 214 # skip all pins by default 215 self.skip_enable = 0xFFFF 216 217 def unskip(self, pin): 218 self.skip_enable &= ~(1 << pin) 219 220 def apply_mux(self, pin, mux): 221 if mux.pinmode == PinMode.ANALOG: 222 self.pins_analog |= 1 << pin 223 elif mux.pinmode == PinMode.DIGITAL_INPUT: 224 self.pins_digital_input |= 1 << pin 225 elif mux.pinmode == PinMode.PUSH_PULL_OUTPUT: 226 self.pins_push_pull_output |= 1 << pin 227 228 if mux.output_value: 229 self.pins_high |= 1 << pin 230 elif not mux.output_value: 231 self.pins_low |= 1 << pin 232 elif mux.output_value is None: 233 pass 234 else: 235 raise Exception("unsupported output value", mux.output_value) 236 elif mux.pinmode is None: 237 pass 238 else: 239 raise Exception("unsupported pinmode", mux.pinmode) 240 241 def __repr__(self): 242 return self.__dict__.__repr__() 243 244 245def main(): 246 args = parse_args() 247 248 with open(args.edt_pickle, "rb") as f: 249 edt = pickle.load(f) 250 251 pinmux_table = [] 252 for node in edt.nodes: 253 if node.status != "okay": 254 continue 255 if not node.pinctrls: 256 continue 257 258 pinctrl = None 259 for p in node.pinctrls: 260 if p.name != "default": 261 continue 262 263 if pinctrl is not None: 264 raise Exception("multiple default nodes", node) 265 pinctrl = p 266 267 if pinctrl is None: 268 raise Exception("no default node", node) 269 270 for conf_node in pinctrl.conf_nodes: 271 for child in conf_node.children.values(): 272 for pin in child.props["pinmux"].val: 273 pinmux_table.append(Pinmux(pin, child.props)) 274 275 crossbar0_bits = [ 276 CrossbarBit(0, [Signal.USART0_TX, Signal.USART0_RX], (0, 0), (1, 15)), 277 CrossbarBit(1, [Signal.USART0_RTS, Signal.USART0_CTS], (0, 0), (1, 15)), 278 CrossbarBit(2, [Signal.USART0_UCLK], (0, 0), (1, 15)), 279 CrossbarBit(5, [Signal.USART1_TX, Signal.USART1_RX], (0, 0), (1, 15)), 280 CrossbarBit(6, [Signal.USART1_RTS, Signal.USART1_CTS], (0, 0), (1, 15)), 281 CrossbarBit(7, [Signal.USART1_UCLK], (0, 0), (1, 15)), 282 CrossbarBit( 283 32 + 3, 284 [Signal.SPI2_SCK, Signal.SPI2_MISO, Signal.SPI2_MOSI], 285 (0, 0), 286 (1, 15), 287 ), 288 CrossbarBit(32 + 4, [Signal.SPI2_NSS], (0, 0), (1, 15)), 289 ] 290 crossbar1_bits = [ 291 CrossbarBit( 292 7, [Signal.SPI2_SCK, Signal.SPI2_MISO, Signal.SPI2_MOSI], (2, 6), (3, 11) 293 ), 294 CrossbarBit(8, [Signal.SPI2_NSS], (2, 6), (3, 11)), 295 ] 296 297 portbanks = [Portbank(), Portbank(), Portbank(), Portbank()] 298 crossbars = [ 299 Crossbar(0, crossbar0_bits, portbanks), 300 Crossbar(1, crossbar1_bits, portbanks), 301 ] 302 303 for crossbar in crossbars: 304 crossbar.mux(pinmux_table) 305 306 with open(args.out, "w", encoding="utf-8") as f: 307 for index, crossbar in enumerate(crossbars): 308 print(f"#define CROSSBAR_{index}_CONFIG 0x{crossbar.value:08X}ULL", file=f) 309 310 for index, portbank in enumerate(portbanks): 311 print( 312 f"#define PORTBANK_{index}_SKIPEN_VALUE 0x{portbank.skip_enable:04X}", 313 file=f, 314 ) 315 316 print( 317 f"#define PORTBANK_{index}_PINS_HIGH 0x{portbank.pins_high:04X}", 318 file=f, 319 ) 320 print( 321 f"#define PORTBANK_{index}_PINS_LOW 0x{portbank.pins_low:04X}", 322 file=f, 323 ) 324 325 print( 326 f"#define PORTBANK_{index}_PINS_DIGITAL_INPUT 0x{portbank.pins_digital_input:04X}", 327 file=f, 328 ) 329 print( 330 f"#define PORTBANK_{index}_PINS_PUSH_PULL_OUTPUT 0x{portbank.pins_push_pull_output:04X}", 331 file=f, 332 ) 333 print( 334 f"#define PORTBANK_{index}_PINS_ANALOG 0x{portbank.pins_analog:04X}", 335 file=f, 336 ) 337 338 339if __name__ == "__main__": 340 main() 341