Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | # Copyright (c) 2021 Nordic Semiconductor ASA |
| 4 | # SPDX-License-Identifier: Apache-2.0 |
| 5 | |
| 6 | """ |
| 7 | Pinctrl Migration Utility Script for nRF Boards |
| 8 | ############################################### |
| 9 | |
| 10 | This script can be used to automatically migrate the Devicetree files of |
| 11 | nRF-based boards using the old <signal>-pin properties to select peripheral |
| 12 | pins. The script will parse a board Devicetree file and will first adjust that |
| 13 | file by removing old pin-related properties replacing them with pinctrl states. |
| 14 | A board-pinctrl.dtsi file will be generated containing the configuration for |
| 15 | all pinctrl states. Note that script will also work on files that have been |
| 16 | partially ported. |
| 17 | |
| 18 | .. warning:: |
| 19 | This script uses a basic line based parser, therefore not all valid |
| 20 | Devicetree files will be converted correctly. **ADJUSTED/GENERATED FILES |
| 21 | MUST BE MANUALLY REVIEWED**. |
| 22 | |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 23 | Known limitations: All SPI nodes will be assumed to be a master device. |
| 24 | |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 25 | Usage:: |
| 26 | |
| 27 | python3 pinctrl_nrf_migrate.py |
| 28 | -i path/to/board.dts |
| 29 | [--no-backup] |
| 30 | [--skip-nrf-check] |
| 31 | [--header ""] |
| 32 | |
| 33 | Example: |
| 34 | |
| 35 | .. code-block:: devicetree |
| 36 | |
| 37 | /* Old board.dts */ |
| 38 | ... |
| 39 | &uart0 { |
| 40 | ... |
| 41 | tx-pin = <5>; |
| 42 | rx-pin = <33>; |
| 43 | rx-pull-up; |
| 44 | ... |
| 45 | }; |
| 46 | |
| 47 | /* Adjusted board.dts */ |
| 48 | ... |
| 49 | #include "board-pinctrl.dtsi" |
| 50 | ... |
| 51 | &uart0 { |
| 52 | ... |
| 53 | pinctrl-0 = <&uart0_default>; |
| 54 | pinctrl-1 = <&uart0_sleep>; |
| 55 | pinctrl-names = "default", "sleep"; |
| 56 | ... |
| 57 | }; |
| 58 | |
| 59 | /* Generated board-pinctrl.dtsi */ |
| 60 | &pinctrl { |
| 61 | uart0_default: uart0_default { |
| 62 | group1 { |
| 63 | psels = <NRF_PSEL(UART_TX, 0, 5); |
| 64 | }; |
| 65 | group2 { |
| 66 | psels = <NRF_PSEL(UART_RX, 1, 1)>; |
| 67 | bias-pull-up; |
| 68 | }; |
| 69 | }; |
| 70 | |
| 71 | uart0_sleep: uart0_sleep { |
| 72 | group1 { |
| 73 | psels = <NRF_PSEL(UART_TX, 0, 5)>, |
| 74 | <NRF_PSEL(UART_RX, 1, 1)>; |
| 75 | low-power-enable; |
| 76 | }; |
| 77 | }; |
| 78 | }; |
| 79 | """ |
| 80 | |
| 81 | import argparse |
| 82 | import enum |
| 83 | from pathlib import Path |
| 84 | import re |
| 85 | import shutil |
| 86 | from typing import Callable, Optional, Dict, List |
| 87 | |
| 88 | |
| 89 | # |
| 90 | # Data types and containers |
| 91 | # |
| 92 | |
| 93 | |
| 94 | class PIN_CONFIG(enum.Enum): |
| 95 | """Pin configuration attributes""" |
| 96 | |
| 97 | PULL_UP = "bias-pull-up" |
| 98 | PULL_DOWN = "bias-pull-down" |
| 99 | LOW_POWER = "low-power-enable" |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 100 | NORDIC_INVERT = "nordic,invert" |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 101 | |
| 102 | |
| 103 | class Device(object): |
| 104 | """Device configuration class""" |
| 105 | |
| 106 | def __init__( |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 107 | self, |
| 108 | pattern: str, |
| 109 | callback: Callable, |
| 110 | signals: Dict[str, str], |
| 111 | needs_sleep: bool, |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 112 | ) -> None: |
| 113 | self.pattern = pattern |
| 114 | self.callback = callback |
| 115 | self.signals = signals |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 116 | self.needs_sleep = needs_sleep |
| 117 | self.attrs = {} |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 118 | |
| 119 | |
| 120 | class SignalMapping(object): |
| 121 | """Signal mapping (signal<>pin)""" |
| 122 | |
| 123 | def __init__(self, signal: str, pin: int) -> None: |
| 124 | self.signal = signal |
| 125 | self.pin = pin |
| 126 | |
| 127 | |
| 128 | class PinGroup(object): |
| 129 | """Pin group""" |
| 130 | |
| 131 | def __init__(self, pins: List[SignalMapping], config: List[PIN_CONFIG]) -> None: |
| 132 | self.pins = pins |
| 133 | self.config = config |
| 134 | |
| 135 | |
| 136 | class PinConfiguration(object): |
| 137 | """Pin configuration (mapping and configuration)""" |
| 138 | |
| 139 | def __init__(self, mapping: SignalMapping, config: List[PIN_CONFIG]) -> None: |
| 140 | self.mapping = mapping |
| 141 | self.config = config |
| 142 | |
| 143 | |
| 144 | class DeviceConfiguration(object): |
| 145 | """Device configuration""" |
| 146 | |
| 147 | def __init__(self, name: str, pins: List[PinConfiguration]) -> None: |
| 148 | self.name = name |
| 149 | self.pins = pins |
| 150 | |
| 151 | def add_signal_config(self, signal: str, config: PIN_CONFIG) -> None: |
| 152 | """Add configuration to signal""" |
| 153 | for pin in self.pins: |
| 154 | if signal == pin.mapping.signal: |
| 155 | pin.config.append(config) |
| 156 | return |
| 157 | |
| 158 | self.pins.append(PinConfiguration(SignalMapping(signal, -1), [config])) |
| 159 | |
| 160 | def set_signal_pin(self, signal: str, pin: int) -> None: |
| 161 | """Set signal pin""" |
| 162 | for pin_ in self.pins: |
| 163 | if signal == pin_.mapping.signal: |
| 164 | pin_.mapping.pin = pin |
| 165 | return |
| 166 | |
| 167 | self.pins.append(PinConfiguration(SignalMapping(signal, pin), [])) |
| 168 | |
| 169 | |
| 170 | # |
| 171 | # Content formatters and writers |
| 172 | # |
| 173 | |
| 174 | |
| 175 | def gen_pinctrl( |
| 176 | configs: List[DeviceConfiguration], input_file: Path, header: str |
| 177 | ) -> None: |
| 178 | """Generate board-pinctrl.dtsi file |
| 179 | |
| 180 | Args: |
| 181 | configs: Board configs. |
| 182 | input_file: Board DTS file. |
| 183 | """ |
| 184 | |
| 185 | last_line = 0 |
| 186 | |
| 187 | pinctrl_file = input_file.parent / (input_file.stem + "-pinctrl.dtsi") |
| 188 | # append content before last node closing |
| 189 | if pinctrl_file.exists(): |
| 190 | content = open(pinctrl_file).readlines() |
| 191 | for i, line in enumerate(content[::-1]): |
| 192 | if re.match(r"\s*};.*", line): |
| 193 | last_line = len(content) - (i + 1) |
| 194 | break |
| 195 | |
| 196 | out = open(pinctrl_file, "w") |
| 197 | |
| 198 | if not last_line: |
| 199 | out.write(header) |
| 200 | out.write("&pinctrl {\n") |
| 201 | else: |
| 202 | for line in content[:last_line]: |
| 203 | out.write(line) |
| 204 | |
| 205 | for config in configs: |
| 206 | # create pin groups with common configuration (default state) |
| 207 | default_groups: List[PinGroup] = [] |
| 208 | for pin in config.pins: |
| 209 | merged = False |
| 210 | for group in default_groups: |
| 211 | if group.config == pin.config: |
| 212 | group.pins.append(pin.mapping) |
| 213 | merged = True |
| 214 | break |
| 215 | if not merged: |
| 216 | default_groups.append(PinGroup([pin.mapping], pin.config)) |
| 217 | |
| 218 | # create pin group for low power state |
| 219 | group = PinGroup([], [PIN_CONFIG.LOW_POWER]) |
| 220 | for pin in config.pins: |
| 221 | group.pins.append(pin.mapping) |
| 222 | sleep_groups = [group] |
| 223 | |
| 224 | # generate default and sleep state entries |
| 225 | out.write(f"\t{config.name}_default: {config.name}_default {{\n") |
| 226 | out.write(fmt_pinctrl_groups(default_groups)) |
| 227 | out.write("\t};\n\n") |
| 228 | |
| 229 | out.write(f"\t{config.name}_sleep: {config.name}_sleep {{\n") |
| 230 | out.write(fmt_pinctrl_groups(sleep_groups)) |
| 231 | out.write("\t};\n\n") |
| 232 | |
| 233 | if not last_line: |
| 234 | out.write("};\n") |
| 235 | else: |
| 236 | for line in content[last_line:]: |
| 237 | out.write(line) |
| 238 | |
| 239 | out.close() |
| 240 | |
| 241 | |
| 242 | def board_is_nrf(content: List[str]) -> bool: |
| 243 | """Check if board is nRF based. |
| 244 | |
| 245 | Args: |
| 246 | content: DT file content as list of lines. |
| 247 | |
| 248 | Returns: |
| 249 | True if board is nRF based, False otherwise. |
| 250 | """ |
| 251 | |
| 252 | for line in content: |
| 253 | m = re.match(r'^#include\s+(?:"|<).*nrf.*(?:>|").*', line) |
| 254 | if m: |
| 255 | return True |
| 256 | |
| 257 | return False |
| 258 | |
| 259 | |
| 260 | def fmt_pinctrl_groups(groups: List[PinGroup]) -> str: |
| 261 | """Format pinctrl groups. |
| 262 | |
| 263 | Example generated content:: |
| 264 | |
| 265 | group1 { |
| 266 | psels = <NRF_PSEL(UART_TX, 0, 5)>; |
| 267 | }; |
| 268 | group2 { |
| 269 | psels = <NRF_PSEL(UART_RX, 1, 1)>; |
| 270 | bias-pull-up; |
| 271 | }; |
| 272 | |
| 273 | Returns: |
| 274 | Generated groups. |
| 275 | """ |
| 276 | |
| 277 | content = "" |
| 278 | |
| 279 | for i, group in enumerate(groups): |
| 280 | content += f"\t\tgroup{i + 1} {{\n" |
| 281 | |
| 282 | # write psels entries |
| 283 | for i, mapping in enumerate(group.pins): |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 284 | prefix = "psels = " if i == 0 else "\t" |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 285 | suffix = ";" if i == len(group.pins) - 1 else "," |
| 286 | pin = mapping.pin |
| 287 | port = 0 if pin < 32 else 1 |
| 288 | if port == 1: |
| 289 | pin -= 32 |
| 290 | content += ( |
| 291 | f"\t\t\t{prefix}<NRF_PSEL({mapping.signal}, {port}, {pin})>{suffix}\n" |
| 292 | ) |
| 293 | |
| 294 | # write all pin configuration (bias, low-power, etc.) |
| 295 | for entry in group.config: |
| 296 | content += f"\t\t\t{entry.value};\n" |
| 297 | |
| 298 | content += "\t\t};\n" |
| 299 | |
| 300 | return content |
| 301 | |
| 302 | |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 303 | def fmt_states(device: str, indent: str, needs_sleep: bool) -> str: |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 304 | """Format state entries for the given device. |
| 305 | |
| 306 | Args: |
| 307 | device: Device name. |
Nazar Kazakov | f483b1b | 2022-03-16 21:07:43 +0000 | [diff] [blame] | 308 | indent: Indentation. |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 309 | needs_sleep: If sleep entry is needed. |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 310 | |
| 311 | Returns: |
| 312 | State entries to be appended to the device. |
| 313 | """ |
| 314 | |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 315 | if needs_sleep: |
| 316 | return "\n".join( |
| 317 | ( |
| 318 | f"{indent}pinctrl-0 = <&{device}_default>;", |
| 319 | f"{indent}pinctrl-1 = <&{device}_sleep>;", |
| 320 | f'{indent}pinctrl-names = "default", "sleep";\n', |
| 321 | ) |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 322 | ) |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 323 | else: |
| 324 | return "\n".join( |
| 325 | ( |
| 326 | f"{indent}pinctrl-0 = <&{device}_default>;", |
| 327 | f'{indent}pinctrl-names = "default";\n', |
| 328 | ) |
| 329 | ) |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 330 | |
| 331 | |
| 332 | def insert_pinctrl_include(content: List[str], board: str) -> None: |
| 333 | """Insert board pinctrl include if not present. |
| 334 | |
| 335 | Args: |
| 336 | content: DT file content as list of lines. |
| 337 | board: Board name |
| 338 | """ |
| 339 | |
| 340 | already = False |
| 341 | include_last_line = -1 |
| 342 | root_line = -1 |
| 343 | |
| 344 | for i, line in enumerate(content): |
| 345 | # check if file already includes a board pinctrl file |
| 346 | m = re.match(r'^#include\s+".*-pinctrl\.dtsi".*', line) |
| 347 | if m: |
| 348 | already = True |
| 349 | continue |
| 350 | |
| 351 | # check if including |
| 352 | m = re.match(r'^#include\s+(?:"|<)(.*)(?:>|").*', line) |
| 353 | if m: |
| 354 | include_last_line = i |
| 355 | continue |
| 356 | |
| 357 | # check for root entry |
| 358 | m = re.match(r"^\s*/\s*{.*", line) |
| 359 | if m: |
| 360 | root_line = i |
| 361 | break |
| 362 | |
| 363 | if include_last_line < 0 and root_line < 0: |
| 364 | raise ValueError("Unexpected DT file content") |
| 365 | |
| 366 | if not already: |
| 367 | if include_last_line >= 0: |
| 368 | line = include_last_line + 1 |
| 369 | else: |
| 370 | line = max(0, root_line - 1) |
| 371 | |
| 372 | content.insert(line, f'#include "{board}-pinctrl.dtsi"\n') |
| 373 | |
| 374 | |
| 375 | def adjust_content(content: List[str], board: str) -> List[DeviceConfiguration]: |
| 376 | """Adjust content |
| 377 | |
| 378 | Args: |
| 379 | content: File content to be adjusted. |
| 380 | board: Board name. |
| 381 | """ |
| 382 | |
| 383 | configs: List[DeviceConfiguration] = [] |
| 384 | level = 0 |
| 385 | in_device = False |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 386 | states_written = False |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 387 | |
| 388 | new_content = [] |
| 389 | |
| 390 | for line in content: |
| 391 | # look for a device reference node (e.g. &uart0) |
| 392 | if not in_device: |
| 393 | m = re.match(r"^[^&]*&([a-z0-9]+)\s*{[^}]*$", line) |
| 394 | if m: |
| 395 | # check if device requires processing |
| 396 | current_device = None |
| 397 | for device in DEVICES: |
| 398 | if re.match(device.pattern, m.group(1)): |
| 399 | current_device = device |
| 400 | indent = "" |
| 401 | config = DeviceConfiguration(m.group(1), []) |
| 402 | configs.append(config) |
| 403 | break |
| 404 | |
| 405 | # we are now inside a device node |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 406 | level = 1 |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 407 | in_device = True |
| 408 | states_written = False |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 409 | else: |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 410 | # entering subnode (must come after all properties) |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 411 | if re.match(r"[^\/\*]*{.*", line): |
| 412 | level += 1 |
| 413 | # exiting subnode (or device node) |
| 414 | elif re.match(r"[^\/\*]*}.*", line): |
| 415 | level -= 1 |
| 416 | in_device = level > 0 |
| 417 | elif current_device: |
| 418 | # device already ported, drop |
| 419 | if re.match(r"[^\/\*]*pinctrl-\d+.*", line): |
| 420 | current_device = None |
| 421 | configs.pop() |
| 422 | # determine indentation |
| 423 | elif not indent: |
| 424 | m = re.match(r"(\s+).*", line) |
| 425 | if m: |
| 426 | indent = m.group(1) |
| 427 | |
| 428 | # process each device line, append states at the end |
| 429 | if current_device: |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 430 | if level == 1: |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 431 | line = current_device.callback(config, current_device.signals, line) |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 432 | if (level == 2 or not in_device) and not states_written: |
| 433 | line = ( |
| 434 | fmt_states(config.name, indent, current_device.needs_sleep) |
| 435 | + line |
| 436 | ) |
| 437 | states_written = True |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 438 | current_device = None |
| 439 | |
| 440 | if line: |
| 441 | new_content.append(line) |
| 442 | |
| 443 | if configs: |
| 444 | insert_pinctrl_include(new_content, board) |
| 445 | |
| 446 | content[:] = new_content |
| 447 | |
| 448 | return configs |
| 449 | |
| 450 | |
| 451 | # |
| 452 | # Processing utilities |
| 453 | # |
| 454 | |
| 455 | |
| 456 | def match_and_store_pin( |
| 457 | config: DeviceConfiguration, signals: Dict[str, str], line: str |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 458 | ) -> Optional[str]: |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 459 | """Match and store a pin mapping. |
| 460 | |
| 461 | Args: |
| 462 | config: Device configuration. |
| 463 | signals: Signals name mapping. |
| 464 | line: Line containing potential pin mapping. |
| 465 | |
| 466 | Returns: |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 467 | Line if found a pin mapping, None otherwise. |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 468 | """ |
| 469 | |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 470 | # handle qspi special case for io-pins (array case) |
| 471 | m = re.match(r"\s*io-pins\s*=\s*([\s<>,0-9]+).*", line) |
| 472 | if m: |
| 473 | pins = re.sub(r"[<>,]", "", m.group(1)).split() |
| 474 | for i, pin in enumerate(pins): |
| 475 | config.set_signal_pin(signals[f"io{i}"], int(pin)) |
| 476 | return |
| 477 | |
| 478 | m = re.match(r"\s*([a-z]+\d?)-pins?\s*=\s*<(\d+)>.*", line) |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 479 | if m: |
| 480 | config.set_signal_pin(signals[m.group(1)], int(m.group(2))) |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 481 | return |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 482 | |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 483 | return line |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 484 | |
| 485 | |
| 486 | # |
| 487 | # Device processing callbacks |
| 488 | # |
| 489 | |
| 490 | |
| 491 | def process_uart(config: DeviceConfiguration, signals, line: str) -> Optional[str]: |
| 492 | """Process UART/UARTE devices.""" |
| 493 | |
| 494 | # check if line specifies a pin |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 495 | if not match_and_store_pin(config, signals, line): |
| 496 | return |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 497 | |
| 498 | # check if pull-up is specified |
| 499 | m = re.match(r"\s*([a-z]+)-pull-up.*", line) |
| 500 | if m: |
| 501 | config.add_signal_config(signals[m.group(1)], PIN_CONFIG.PULL_UP) |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 502 | return |
| 503 | |
| 504 | return line |
| 505 | |
| 506 | |
| 507 | def process_spi(config: DeviceConfiguration, signals, line: str) -> Optional[str]: |
| 508 | """Process SPI devices.""" |
| 509 | |
| 510 | # check if line specifies a pin |
| 511 | if not match_and_store_pin(config, signals, line): |
| 512 | return |
| 513 | |
| 514 | # check if pull-up is specified |
| 515 | m = re.match(r"\s*miso-pull-up.*", line) |
| 516 | if m: |
| 517 | config.add_signal_config(signals["miso"], PIN_CONFIG.PULL_UP) |
| 518 | return |
| 519 | |
| 520 | # check if pull-down is specified |
| 521 | m = re.match(r"\s*miso-pull-down.*", line) |
| 522 | if m: |
| 523 | config.add_signal_config(signals["miso"], PIN_CONFIG.PULL_DOWN) |
| 524 | return |
| 525 | |
| 526 | return line |
| 527 | |
| 528 | |
| 529 | def process_pwm(config: DeviceConfiguration, signals, line: str) -> Optional[str]: |
| 530 | """Process PWM devices.""" |
| 531 | |
| 532 | # check if line specifies a pin |
| 533 | if not match_and_store_pin(config, signals, line): |
| 534 | return |
| 535 | |
| 536 | # check if channel inversion is specified |
| 537 | m = re.match(r"\s*([a-z0-9]+)-inverted.*", line) |
| 538 | if m: |
| 539 | config.add_signal_config(signals[m.group(1)], PIN_CONFIG.NORDIC_INVERT) |
| 540 | return |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 541 | |
| 542 | return line |
| 543 | |
| 544 | |
| 545 | DEVICES = [ |
| 546 | Device( |
| 547 | r"uart\d", |
| 548 | process_uart, |
| 549 | { |
| 550 | "tx": "UART_TX", |
| 551 | "rx": "UART_RX", |
| 552 | "rts": "UART_RTS", |
| 553 | "cts": "UART_CTS", |
| 554 | }, |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 555 | needs_sleep=True, |
| 556 | ), |
| 557 | Device( |
| 558 | r"i2c\d", |
| 559 | match_and_store_pin, |
| 560 | { |
| 561 | "sda": "TWIM_SDA", |
| 562 | "scl": "TWIM_SCL", |
| 563 | }, |
| 564 | needs_sleep=True, |
| 565 | ), |
| 566 | Device( |
| 567 | r"spi\d", |
| 568 | process_spi, |
| 569 | { |
| 570 | "sck": "SPIM_SCK", |
| 571 | "miso": "SPIM_MISO", |
| 572 | "mosi": "SPIM_MOSI", |
| 573 | }, |
| 574 | needs_sleep=True, |
| 575 | ), |
| 576 | Device( |
| 577 | r"pdm\d", |
| 578 | match_and_store_pin, |
| 579 | { |
| 580 | "clk": "PDM_CLK", |
| 581 | "din": "PDM_DIN", |
| 582 | }, |
| 583 | needs_sleep=False, |
| 584 | ), |
| 585 | Device( |
| 586 | r"qdec", |
| 587 | match_and_store_pin, |
| 588 | { |
| 589 | "a": "QDEC_A", |
| 590 | "b": "QDEC_B", |
| 591 | "led": "QDEC_LED", |
| 592 | }, |
| 593 | needs_sleep=True, |
| 594 | ), |
| 595 | Device( |
| 596 | r"qspi", |
| 597 | match_and_store_pin, |
| 598 | { |
| 599 | "sck": "QSPI_SCK", |
| 600 | "io0": "QSPI_IO0", |
| 601 | "io1": "QSPI_IO1", |
| 602 | "io2": "QSPI_IO2", |
| 603 | "io3": "QSPI_IO3", |
| 604 | "csn": "QSPI_CSN", |
| 605 | }, |
| 606 | needs_sleep=True, |
| 607 | ), |
| 608 | Device( |
| 609 | r"pwm\d", |
| 610 | process_pwm, |
| 611 | { |
| 612 | "ch0": "PWM_OUT0", |
| 613 | "ch1": "PWM_OUT1", |
| 614 | "ch2": "PWM_OUT2", |
| 615 | "ch3": "PWM_OUT3", |
| 616 | }, |
| 617 | needs_sleep=True, |
| 618 | ), |
| 619 | Device( |
| 620 | r"i2s\d", |
| 621 | match_and_store_pin, |
| 622 | { |
| 623 | "sck": "I2S_SCK_M", |
| 624 | "lrck": "I2S_LRCK_M", |
| 625 | "sdout": "I2S_SDOUT", |
| 626 | "sdin": "I2S_SDIN", |
| 627 | "mck": "I2S_MCK", |
| 628 | }, |
| 629 | needs_sleep=False, |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 630 | ), |
| 631 | ] |
| 632 | """Supported devices and associated configuration""" |
| 633 | |
| 634 | |
| 635 | def main(input_file: Path, no_backup: bool, skip_nrf_check: bool, header: str) -> None: |
| 636 | """Entry point |
| 637 | |
| 638 | Args: |
| 639 | input_file: Input DTS file. |
| 640 | no_backup: Do not create backup files. |
| 641 | """ |
| 642 | |
| 643 | board_name = input_file.stem |
| 644 | content = open(input_file).readlines() |
| 645 | |
| 646 | if not skip_nrf_check and not board_is_nrf(content): |
| 647 | print(f"Board {board_name} is not nRF based, terminating") |
| 648 | return |
| 649 | |
| 650 | if not no_backup: |
| 651 | backup_file = input_file.parent / (board_name + ".bck" + input_file.suffix) |
| 652 | shutil.copy(input_file, backup_file) |
| 653 | |
| 654 | configs = adjust_content(content, board_name) |
| 655 | |
| 656 | if configs: |
| 657 | with open(input_file, "w") as f: |
| 658 | f.writelines(content) |
| 659 | |
| 660 | gen_pinctrl(configs, input_file, header) |
| 661 | |
| 662 | print(f"Board {board_name} Devicetree file has been converted") |
| 663 | else: |
| 664 | print(f"Nothing to be converted for {board_name}") |
| 665 | |
| 666 | |
| 667 | if __name__ == "__main__": |
Jamie McCrae | ec70444 | 2023-01-04 16:08:36 +0000 | [diff] [blame] | 668 | parser = argparse.ArgumentParser("pinctrl migration utility for nRF", allow_abbrev=False) |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 669 | parser.add_argument( |
| 670 | "-i", "--input", type=Path, required=True, help="Board DTS file" |
| 671 | ) |
| 672 | parser.add_argument( |
| 673 | "--no-backup", action="store_true", help="Do not create backup files" |
| 674 | ) |
| 675 | parser.add_argument( |
| 676 | "--skip-nrf-check", |
| 677 | action="store_true", |
| 678 | help="Skip checking if board is nRF-based", |
| 679 | ) |
| 680 | parser.add_argument( |
Gerard Marull-Paretas | 8b91cb0 | 2022-03-16 16:04:49 +0100 | [diff] [blame] | 681 | "--header", default="", type=str, help="Header to be prepended to pinctrl files" |
Gerard Marull-Paretas | d477001 | 2021-09-30 13:10:24 +0200 | [diff] [blame] | 682 | ) |
| 683 | args = parser.parse_args() |
| 684 | |
| 685 | main(args.input, args.no_backup, args.skip_nrf_check, args.header) |