Madhurima Paruchuri | fa738b0 | 2022-10-26 13:34:09 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | # Copyright (c) 2022 The Chromium OS Authors |
| 4 | # SPDX-License-Identifier: Apache-2.0 |
| 5 | |
| 6 | """This file contains a Python script which parses through Zephyr device tree using |
| 7 | EDT.pickle generated at build and generates a XML file containing USB VIF policies""" |
| 8 | |
| 9 | import argparse |
Keith Short | 52e6b4e | 2023-03-24 15:17:31 -0600 | [diff] [blame] | 10 | import inspect |
Madhurima Paruchuri | fa738b0 | 2022-10-26 13:34:09 +0000 | [diff] [blame] | 11 | import os |
| 12 | import pickle |
| 13 | import sys |
| 14 | import xml.etree.ElementTree as ET |
| 15 | |
| 16 | import constants |
| 17 | |
| 18 | SCRIPTS_DIR = os.path.join(os.path.dirname(__file__), "..") |
| 19 | sys.path.insert(0, os.path.join(SCRIPTS_DIR, 'dts', 'python-devicetree', 'src')) |
| 20 | |
Madhurima Paruchuri | fa738b0 | 2022-10-26 13:34:09 +0000 | [diff] [blame] | 21 | def main(): |
Keith Short | 52e6b4e | 2023-03-24 15:17:31 -0600 | [diff] [blame] | 22 | global edtlib |
| 23 | |
Madhurima Paruchuri | fa738b0 | 2022-10-26 13:34:09 +0000 | [diff] [blame] | 24 | args = parse_args() |
Keith Short | 52e6b4e | 2023-03-24 15:17:31 -0600 | [diff] [blame] | 25 | with open(args.edt_pickle, 'rb') as f: |
| 26 | edt = pickle.load(f) |
| 27 | edtlib = inspect.getmodule(edt) |
| 28 | |
Madhurima Paruchuri | fa738b0 | 2022-10-26 13:34:09 +0000 | [diff] [blame] | 29 | xml_root = get_root() |
| 30 | add_elements_to_xml(xml_root, constants.VIF_SPEC_ELEMENTS) |
| 31 | add_element_to_xml(xml_root, constants.MODEL_PART_NUMBER, args.board) |
| 32 | for node in edt.compat2nodes[args.compatible]: |
| 33 | xml_ele = add_element_to_xml(xml_root, constants.COMPONENT) |
| 34 | parse_and_add_node_to_xml(xml_ele, node) |
| 35 | tree = ET.ElementTree(xml_root) |
| 36 | tree.write(args.vif_out, xml_declaration=True, |
| 37 | encoding=constants.XML_ENCODING) |
| 38 | |
| 39 | |
| 40 | def is_vif_element(name): |
| 41 | if name in constants.VIF_ELEMENTS: |
| 42 | return True |
| 43 | return False |
| 44 | |
| 45 | |
| 46 | def get_vif_element_name(name): |
| 47 | return constants.XML_ELEMENT_NAME_PREFIX + ":" + constants.DT_VIF_ELEMENTS.get( |
| 48 | name, name) |
| 49 | |
| 50 | |
| 51 | def get_root(): |
| 52 | xml_root = ET.Element(get_vif_element_name(constants.XML_ROOT_ELEMENT_NAME)) |
| 53 | add_attributes_to_xml_element(xml_root, constants.XML_NAMESPACE_ATTRIBUTES) |
| 54 | return xml_root |
| 55 | |
| 56 | |
| 57 | def add_attributes_to_xml_element(xml_ele, attributes): |
| 58 | for key, value in attributes.items(): |
| 59 | xml_ele.set(key, value) |
| 60 | |
| 61 | |
| 62 | def add_element_to_xml(xml_ele, name, text=None, attributes=None): |
| 63 | if is_vif_element(name): |
| 64 | new_xml_ele = ET.SubElement(xml_ele, get_vif_element_name(name)) |
| 65 | if text: |
| 66 | new_xml_ele.text = str(text) |
| 67 | if attributes: |
| 68 | add_attributes_to_xml_element(new_xml_ele, attributes) |
| 69 | return new_xml_ele |
| 70 | return xml_ele |
| 71 | |
| 72 | |
| 73 | def add_elements_to_xml(xml_ele, elements): |
| 74 | for element_name in elements: |
| 75 | text = elements[element_name].get(constants.TEXT, None) |
| 76 | attributes = elements[element_name].get(constants.ATTRIBUTES, None) |
| 77 | new_xml_ele = add_element_to_xml(xml_ele, element_name, text, attributes) |
| 78 | if constants.CHILD in elements[element_name]: |
| 79 | add_elements_to_xml(new_xml_ele, elements[element_name][constants.CHILD]) |
| 80 | |
| 81 | |
| 82 | def is_simple_datatype(value): |
| 83 | if isinstance(value, (str, int, bool)): |
| 84 | return True |
| 85 | return False |
| 86 | |
| 87 | |
| 88 | def get_pdo_type(pdo_value): |
| 89 | return pdo_value >> 30 |
| 90 | |
| 91 | |
| 92 | def get_xml_bool_value(value): |
| 93 | if value: |
| 94 | return constants.TRUE |
| 95 | return constants.FALSE |
| 96 | |
| 97 | |
| 98 | def parse_and_add_sink_pdos_to_xml(xml_ele, sink_pdos): |
| 99 | new_xml_ele = add_element_to_xml(xml_ele, constants.SINK_PDOS) |
| 100 | pdos_info = dict() |
| 101 | snk_max_power = 0 |
| 102 | for pdo_value in sink_pdos: |
| 103 | power_mv = parse_and_add_sink_pdo_to_xml(new_xml_ele, pdo_value, pdos_info) |
| 104 | if power_mv > snk_max_power: |
| 105 | snk_max_power = power_mv |
| 106 | add_element_to_xml(xml_ele, constants.NUM_SINK_PDOS, None, |
| 107 | {constants.VALUE: str(len(sink_pdos))}) |
| 108 | add_element_to_xml(xml_ele, constants.EPR_SUPPORTED_AS_SINK, |
| 109 | attributes={constants.VALUE: constants.FALSE}) |
| 110 | add_element_to_xml(xml_ele, constants.NO_USB_SUSPEND_MAY_BE_SET, |
| 111 | attributes={constants.VALUE: constants.TRUE}) |
| 112 | add_element_to_xml(xml_ele, constants.HIGHER_CAPABILITY_SET, attributes={ |
| 113 | constants.VALUE: get_xml_bool_value(pdos_info.get(constants.HIGHER_CAPABILITY_SET, 0))}) |
| 114 | add_element_to_xml(xml_ele, constants.FR_SWAP_REQD_TYPE_C_CURRENT_AS_INITIAL_SOURCE, |
| 115 | "FR_Swap not supported", attributes={constants.VALUE: str( |
| 116 | pdos_info.get(constants.FR_SWAP_REQD_TYPE_C_CURRENT_AS_INITIAL_SOURCE, 0))}) |
| 117 | add_element_to_xml(xml_ele, constants.PD_POWER_AS_SINK, f'{snk_max_power} mW', |
| 118 | {constants.VALUE: str(snk_max_power)}) |
| 119 | |
| 120 | |
| 121 | def parse_and_add_sink_pdo_to_xml(xml_ele, pdo_value, pdos_info): |
| 122 | power_mw = 0 |
| 123 | xml_ele = add_element_to_xml(xml_ele, constants.SINK_PDO) |
| 124 | pdo_type = get_pdo_type(pdo_value) |
| 125 | if pdo_type == constants.PDO_TYPE_FIXED: |
| 126 | current = pdo_value & 0x3ff |
| 127 | current_ma = current * 10 |
| 128 | voltage = (pdo_value >> 10) & 0x3ff |
| 129 | voltage_mv = voltage * 50 |
| 130 | power_mw = (current_ma * voltage_mv) // 1000 |
| 131 | pdos_info[constants.HIGHER_CAPABILITY_SET] = pdo_value & (1 << 28) |
| 132 | pdos_info[constants.FR_SWAP_REQD_TYPE_C_CURRENT_AS_INITIAL_SOURCE] = pdo_value & (3 << 23) |
| 133 | add_element_to_xml(xml_ele, constants.SINK_PDO_VOLTAGE, f'{voltage_mv} mV', |
| 134 | {constants.VALUE: str(voltage)}) |
| 135 | add_element_to_xml(xml_ele, constants.SINK_PDO_OP_CURRENT, |
| 136 | f'{current_ma} mA', |
| 137 | {constants.VALUE: str(current)}) |
| 138 | elif pdo_type == constants.PDO_TYPE_BATTERY: |
| 139 | max_voltage = (pdo_value >> 20) & 0x3ff |
| 140 | max_voltage_mv = max_voltage * 50 |
| 141 | min_voltage = (pdo_value >> 10) & 0x3ff |
| 142 | min_voltage_mv = min_voltage * 50 |
| 143 | power = pdo_value & 0x3ff |
| 144 | power_mw = power * 250 |
| 145 | add_element_to_xml(xml_ele, constants.SINK_PDO_MIN_VOLTAGE, |
| 146 | f'{min_voltage_mv} mV', |
| 147 | {constants.VALUE: str(min_voltage)}) |
| 148 | add_element_to_xml(xml_ele, constants.SINK_PDO_MAX_VOLTAGE, |
| 149 | f'{max_voltage_mv} mV', |
| 150 | {constants.VALUE: str(max_voltage)}) |
| 151 | add_element_to_xml(xml_ele, constants.SINK_PDO_OP_POWER, f'{power_mw} mW', |
| 152 | {constants.VALUE: str(power)}) |
| 153 | elif pdo_type == constants.PDO_TYPE_VARIABLE: |
| 154 | max_voltage = (pdo_value >> 20) & 0x3ff |
| 155 | max_voltage_mv = max_voltage * 50 |
| 156 | min_voltage = (pdo_value >> 10) & 0x3ff |
| 157 | min_voltage_mv = min_voltage * 50 |
| 158 | current = pdo_value & 0x3ff |
| 159 | current_ma = current * 10 |
| 160 | power_mw = (current_ma * max_voltage_mv) // 1000 |
| 161 | add_element_to_xml(xml_ele, constants.SINK_PDO_MIN_VOLTAGE, |
| 162 | f'{min_voltage_mv} mV', |
| 163 | {constants.VALUE: str(min_voltage)}) |
| 164 | add_element_to_xml(xml_ele, constants.SINK_PDO_MAX_VOLTAGE, |
| 165 | f'{max_voltage_mv} mV', |
| 166 | {constants.VALUE: str(max_voltage)}) |
| 167 | add_element_to_xml(xml_ele, constants.SINK_PDO_OP_CURRENT, |
| 168 | f'{current_ma} mA', |
| 169 | {constants.VALUE: str(current)}) |
| 170 | elif pdo_type == constants.PDO_TYPE_AUGUMENTED: |
| 171 | pps = (pdo_value >> 28) & 0x03 |
| 172 | if pps: |
| 173 | raise ValueError(f'ERROR: Invalid PDO_TYPE {pdo_value}') |
| 174 | pps_max_voltage = (pdo_value >> 17) & 0xff |
| 175 | pps_max_voltage_mv = pps_max_voltage * 100 |
| 176 | pps_min_voltage = (pdo_value >> 8) & 0xff |
| 177 | pps_min_voltage_mv = pps_min_voltage * 100 |
| 178 | pps_current = pdo_value & 0x7f |
| 179 | pps_current_ma = pps_current * 50 |
| 180 | power_mw = (pps_current_ma * pps_max_voltage_mv) // 1000 |
| 181 | add_element_to_xml(xml_ele, constants.SINK_PDO_MIN_VOLTAGE, |
| 182 | f'{pps_min_voltage_mv} mV', |
| 183 | {constants.VALUE: str(pps_min_voltage)}) |
| 184 | add_element_to_xml(xml_ele, constants.SINK_PDO_MAX_VOLTAGE, |
| 185 | f'{pps_max_voltage_mv} mV', |
| 186 | {constants.VALUE: str(pps_max_voltage)}) |
| 187 | add_element_to_xml(xml_ele, constants.SINK_PDO_OP_CURRENT, |
| 188 | f'{pps_current_ma} mA', |
| 189 | {constants.VALUE: str(pps_current)}) |
| 190 | else: |
| 191 | raise ValueError(f'ERROR: Invalid PDO_TYPE {pdo_value}') |
| 192 | add_element_to_xml(xml_ele, constants.SINK_PDO_SUPPLY_TYPE, |
| 193 | constants.PDO_TYPES[pdo_type], {constants.VALUE: str(pdo_type)}) |
| 194 | return power_mw |
| 195 | |
| 196 | |
| 197 | def parse_and_add_controller_and_data_to_xml(xml_ele, cad): |
| 198 | xml_ele = add_element_to_xml(xml_ele, cad.basename) |
| 199 | for name in cad.data: |
| 200 | add_element_to_xml(xml_ele, name, str(cad.data[name])) |
| 201 | parse_and_add_node_to_xml(xml_ele, cad.controller) |
| 202 | |
| 203 | |
| 204 | def parse_and_add_array_to_xml(xml_ele, prop): |
| 205 | for member in prop.val: |
| 206 | if is_simple_datatype(member): |
| 207 | add_element_to_xml(xml_ele, prop.name, str(member)) |
| 208 | elif isinstance(member, list): |
| 209 | new_xml_ele = add_element_to_xml(xml_ele, prop.name) |
| 210 | parse_and_add_array_to_xml(new_xml_ele, member) |
| 211 | elif isinstance(member, edtlib.Node): |
| 212 | new_xml_ele = add_element_to_xml(xml_ele, prop.name) |
| 213 | parse_and_add_node_to_xml(new_xml_ele, member) |
| 214 | elif isinstance(member, edtlib.ControllerAndData): |
| 215 | new_xml_ele = add_element_to_xml(xml_ele, prop.name) |
| 216 | parse_and_add_controller_and_data_to_xml(new_xml_ele, member) |
| 217 | else: |
| 218 | ValueError( |
| 219 | f'Noticed undefined type : {str(type(member))}, with value {str(member)}') |
| 220 | |
| 221 | |
| 222 | def parse_and_add_node_to_xml(xml_ele, node): |
| 223 | if not isinstance(node, edtlib.Node): |
| 224 | return |
| 225 | xml_ele = add_element_to_xml(xml_ele, node.name) |
| 226 | for prop in node.props: |
| 227 | if is_simple_datatype(node.props[prop].val): |
| 228 | add_element_to_xml(xml_ele, node.props[prop].name, |
| 229 | str(node.props[prop].val)) |
| 230 | elif node.props[prop].name == constants.SINK_PDOS: |
| 231 | parse_and_add_sink_pdos_to_xml(xml_ele, node.props[prop].val) |
| 232 | elif isinstance(node.props[prop].val, list): |
| 233 | parse_and_add_array_to_xml(xml_ele, node.props[prop]) |
| 234 | elif isinstance(node.props[prop].val, edtlib.Node): |
| 235 | new_xml_ele = add_element_to_xml(xml_ele, node.props[prop].name) |
| 236 | parse_and_add_node_to_xml(new_xml_ele, node.props[prop].val) |
| 237 | elif isinstance(node.props[prop].val, edtlib.ControllerAndData): |
| 238 | new_xml_ele = add_element_to_xml(xml_ele, node.props[prop].name) |
| 239 | parse_and_add_controller_and_data_to_xml(new_xml_ele, node.props[prop].val) |
| 240 | else: |
| 241 | ValueError( |
| 242 | f'Noticed undefined type : {str(type(node.props[prop].val))}, ' |
| 243 | f'with value {str(node.props[prop].val)}') |
| 244 | for child in node.children: |
| 245 | new_xml_ele = add_element_to_xml(xml_ele, child) |
| 246 | parse_and_add_node_to_xml(new_xml_ele, node.children[child]) |
| 247 | |
| 248 | |
| 249 | def parse_args(): |
Jamie McCrae | ec70444 | 2023-01-04 16:08:36 +0000 | [diff] [blame] | 250 | parser = argparse.ArgumentParser(allow_abbrev=False) |
Madhurima Paruchuri | fa738b0 | 2022-10-26 13:34:09 +0000 | [diff] [blame] | 251 | parser.add_argument("--edt-pickle", required=True, |
| 252 | help="path to read the pickled edtlib.EDT object from") |
| 253 | parser.add_argument("--compatible", required=True, |
| 254 | help="device tree compatible to be parsed") |
| 255 | parser.add_argument("--vif-out", required=True, |
| 256 | help="path to write VIF policies to") |
| 257 | parser.add_argument("--board", required=True, help="board name") |
| 258 | return parser.parse_args() |
| 259 | |
| 260 | |
| 261 | if __name__ == "__main__": |
| 262 | main() |