blob: 58fcf9c6a18860e86a2e2188d879bfda3165a106 [file] [log] [blame]
Kumar Gala87915cd2018-11-15 11:51:50 -06001# Copyright (c) 2018-2019 Linaro
Carles Cufifa26ef02019-01-30 17:54:21 +01002# Copyright (c) 2019 Nordic Semiconductor ASA
Kumar Gala87915cd2018-11-15 11:51:50 -06003#
4# SPDX-License-Identifier: Apache-2.0
5
Keith Shortdf0942f2023-03-24 15:22:42 -06006import inspect
Kumar Gala87915cd2018-11-15 11:51:50 -06007import os
Martí Bolívar269f3502020-07-01 12:17:02 -07008import pickle
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +02009import re
Kumar Gala57353972019-08-28 09:30:23 -050010import sys
Henrik Brix Andersenc6b30942022-05-18 17:35:23 +020011from pathlib import Path
Kumar Gala57353972019-08-28 09:30:23 -050012
Henrik Brix Andersenc6b30942022-05-18 17:35:23 +020013ZEPHYR_BASE = str(Path(__file__).resolve().parents[2])
Martí Bolívar53328472021-03-26 16:18:58 -070014sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts", "dts",
15 "python-devicetree", "src"))
Kumar Gala57353972019-08-28 09:30:23 -050016
Kumar Gala87915cd2018-11-15 11:51:50 -060017# Types we support
18# 'string', 'int', 'hex', 'bool'
19
Carles Cufifa26ef02019-01-30 17:54:21 +010020doc_mode = os.environ.get('KCONFIG_DOC_MODE') == "1"
Sebastian Bøecd435432019-02-11 15:57:34 +010021
Ulf Magnussonba312fe2019-03-20 19:30:29 +010022if not doc_mode:
Martí Bolívar269f3502020-07-01 12:17:02 -070023 EDT_PICKLE = os.environ.get("EDT_PICKLE")
Kumar Gala9aefdaf2020-04-15 10:32:41 -050024
Martí Bolívar269f3502020-07-01 12:17:02 -070025 # The "if" handles a missing dts.
26 if EDT_PICKLE is not None and os.path.isfile(EDT_PICKLE):
27 with open(EDT_PICKLE, 'rb') as f:
28 edt = pickle.load(f)
Keith Shortdf0942f2023-03-24 15:22:42 -060029 edtlib = inspect.getmodule(edt)
Kumar Gala57353972019-08-28 09:30:23 -050030 else:
31 edt = None
32
Kumar Gala1baf2f32019-09-04 14:14:26 -050033
34def _warn(kconf, msg):
35 print("{}:{}: WARNING: {}".format(kconf.filename, kconf.linenr, msg))
36
37
Kumar Gala87915cd2018-11-15 11:51:50 -060038def _dt_units_to_scale(unit):
39 if not unit:
40 return 0
41 if unit in {'k', 'K'}:
42 return 10
43 if unit in {'m', 'M'}:
44 return 20
45 if unit in {'g', 'G'}:
46 return 30
Daniel DeGrasse6064da82022-05-25 17:38:59 -050047 if unit in {'kb', 'Kb'}:
48 return 13
49 if unit in {'mb', 'Mb'}:
50 return 23
51 if unit in {'gb', 'Gb'}:
52 return 33
Kumar Gala87915cd2018-11-15 11:51:50 -060053
Kumar Gala57353972019-08-28 09:30:23 -050054
55def dt_chosen_label(kconf, _, chosen):
56 """
57 This function takes a 'chosen' property and treats that property as a path
Ulf Magnusson73ac1462019-09-23 05:14:18 +020058 to an EDT node. If it finds an EDT node, it will look to see if that node
Martí Bolívarb3151bc2022-08-18 16:13:50 -070059 has a "label" property and return the value of that "label". If not, we
60 return the node's name in the devicetree.
Kumar Gala57353972019-08-28 09:30:23 -050061 """
62 if doc_mode or edt is None:
63 return ""
64
Ulf Magnusson73ac1462019-09-23 05:14:18 +020065 node = edt.chosen_node(chosen)
66 if not node:
Kumar Gala57353972019-08-28 09:30:23 -050067 return ""
68
Ulf Magnusson73ac1462019-09-23 05:14:18 +020069 if "label" not in node.props:
Kumar Gala57538262022-08-04 11:23:13 -050070 return node.name
Kumar Gala57353972019-08-28 09:30:23 -050071
Ulf Magnusson73ac1462019-09-23 05:14:18 +020072 return node.props["label"].val
Kumar Gala57353972019-08-28 09:30:23 -050073
74
Kumar Gala07e5d892019-11-04 10:20:59 -060075def dt_chosen_enabled(kconf, _, chosen):
76 """
77 This function returns "y" if /chosen contains a property named 'chosen'
78 that points to an enabled node, and "n" otherwise
79 """
80 if doc_mode or edt is None:
81 return "n"
82
83 node = edt.chosen_node(chosen)
Martí Bolívar81650082020-10-05 20:02:13 -070084 return "y" if node and node.status == "okay" else "n"
Kumar Gala07e5d892019-11-04 10:20:59 -060085
Kumar Gala0353d222020-02-13 23:43:42 -060086
Martí Bolívar7ff3ebc2020-04-23 12:28:06 -070087def dt_chosen_path(kconf, _, chosen):
88 """
89 This function takes a /chosen node property and returns the path
90 to the node in the property value, or the empty string.
91 """
92 if doc_mode or edt is None:
93 return "n"
94
95 node = edt.chosen_node(chosen)
96
97 return node.path if node else ""
98
Daniel DeGrassefbefc502022-04-11 11:53:03 -050099def dt_chosen_has_compat(kconf, _, chosen, compat):
100 """
101 This function takes a /chosen node property and returns 'y' if the
102 chosen node has the provided compatible string 'compat'
103 """
104 if doc_mode or edt is None:
105 return "n"
106
107 node = edt.chosen_node(chosen)
108
109 if node is None:
110 return "n"
111
112 if compat in node.compats:
113 return "y"
114
115 return "n"
Martí Bolívar7ff3ebc2020-04-23 12:28:06 -0700116
Martí Bolívar24677f92020-04-28 16:52:35 -0700117def dt_node_enabled(kconf, name, node):
118 """
119 This function is used to test if a node is enabled (has status
120 'okay') or not.
121
122 The 'node' argument is a string which is either a path or an
123 alias, or both, depending on 'name'.
124
125 If 'name' is 'dt_path_enabled', 'node' is an alias or a path. If
126 'name' is 'dt_alias_enabled, 'node' is an alias.
127 """
128
129 if doc_mode or edt is None:
130 return "n"
131
132 if name == "dt_alias_enabled":
133 if node.startswith("/"):
134 # EDT.get_node() works with either aliases or paths. If we
135 # are specifically being asked about an alias, reject paths.
136 return "n"
137 else:
138 # Make sure this is being called appropriately.
139 assert name == "dt_path_enabled"
140
141 try:
142 node = edt.get_node(node)
143 except edtlib.EDTError:
144 return "n"
145
Martí Bolívar81650082020-10-05 20:02:13 -0700146 return "y" if node and node.status == "okay" else "n"
Martí Bolívar24677f92020-04-28 16:52:35 -0700147
148
Kumar Gala0353d222020-02-13 23:43:42 -0600149def dt_nodelabel_enabled(kconf, _, label):
150 """
Martí Bolívar24677f92020-04-28 16:52:35 -0700151 This function is like dt_node_enabled(), but the 'label' argument
152 should be a node label, like "foo" is here:
153
154 foo: some-node { ... };
Kumar Gala0353d222020-02-13 23:43:42 -0600155 """
156 if doc_mode or edt is None:
157 return "n"
158
Martí Bolívar0682c462020-04-22 17:59:23 -0700159 node = edt.label2node.get(label)
Kumar Gala0353d222020-02-13 23:43:42 -0600160
Martí Bolívar81650082020-10-05 20:02:13 -0700161 return "y" if node and node.status == "okay" else "n"
Kumar Gala0353d222020-02-13 23:43:42 -0600162
163
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200164def _node_reg_addr(node, index, unit):
165 if not node:
Kumar Gala22e74492019-10-23 15:15:59 -0500166 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500167
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200168 if not node.regs:
Kumar Gala22e74492019-10-23 15:15:59 -0500169 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500170
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200171 if int(index) >= len(node.regs):
Kumar Gala22e74492019-10-23 15:15:59 -0500172 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500173
Kumar Gala8af311a2020-03-13 12:43:56 -0500174 if node.regs[int(index)].addr is None:
175 return 0
176
Kumar Gala22e74492019-10-23 15:15:59 -0500177 return node.regs[int(index)].addr >> _dt_units_to_scale(unit)
Kumar Gala57353972019-08-28 09:30:23 -0500178
179
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200180def _node_reg_size(node, index, unit):
181 if not node:
Kumar Gala22e74492019-10-23 15:15:59 -0500182 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500183
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200184 if not node.regs:
Kumar Gala22e74492019-10-23 15:15:59 -0500185 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500186
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200187 if int(index) >= len(node.regs):
Kumar Gala22e74492019-10-23 15:15:59 -0500188 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500189
Kumar Gala8af311a2020-03-13 12:43:56 -0500190 if node.regs[int(index)].size is None:
191 return 0
192
Kumar Gala22e74492019-10-23 15:15:59 -0500193 return node.regs[int(index)].size >> _dt_units_to_scale(unit)
Kumar Gala57353972019-08-28 09:30:23 -0500194
195
David Leach1e5a8302021-11-08 23:24:22 -0600196def _node_int_prop(node, prop, unit=None):
197 """
198 This function takes a 'node' and will look to see if that 'node' has a
199 property called 'prop' and if that 'prop' is an integer type will return
200 the value of the property 'prop' as either a string int or string hex
201 value, if not we return 0.
202
203 The function will divide the value based on 'unit':
204 None No division
205 'k' or 'K' divide by 1024 (1 << 10)
206 'm' or 'M' divide by 1,048,576 (1 << 20)
207 'g' or 'G' divide by 1,073,741,824 (1 << 30)
Daniel DeGrasse6064da82022-05-25 17:38:59 -0500208 'kb' or 'Kb' divide by 8192 (1 << 13)
209 'mb' or 'Mb' divide by 8,388,608 (1 << 23)
210 'gb' or 'Gb' divide by 8,589,934,592 (1 << 33)
David Leach1e5a8302021-11-08 23:24:22 -0600211 """
Kumar Gala338b43192020-04-03 05:02:35 -0500212 if not node:
213 return 0
214
215 if prop not in node.props:
216 return 0
217
218 if node.props[prop].type != "int":
219 return 0
220
David Leach1e5a8302021-11-08 23:24:22 -0600221 return node.props[prop].val >> _dt_units_to_scale(unit)
Kumar Gala338b43192020-04-03 05:02:35 -0500222
223
Eivind Jølsgard3865e082022-03-18 10:50:00 +0100224def _node_array_prop(node, prop, index=0, unit=None):
225 """
226 This function takes a 'node' and will look to see if that 'node' has a
227 property called 'prop' and if that 'prop' is an array type will return
228 the value of the property 'prop' at the given 'index' as either a string int
229 or string hex value. If the property 'prop' is not found or the given 'index'
230 is out of range it will return 0.
231
232 The function will divide the value based on 'unit':
233 None No division
234 'k' or 'K' divide by 1024 (1 << 10)
235 'm' or 'M' divide by 1,048,576 (1 << 20)
236 'g' or 'G' divide by 1,073,741,824 (1 << 30)
237 """
238 if not node:
239 return 0
240
241 if prop not in node.props:
242 return 0
243 if node.props[prop].type != "array":
244 return 0
245 if int(index) >= len(node.props[prop].val):
246 return 0
247 return node.props[prop].val[int(index)] >> _dt_units_to_scale(unit)
248
Erwan Gourioua59e01d2023-12-18 12:03:43 +0100249def _node_ph_array_prop(node, prop, index, cell, unit=None):
250 """
251 This function takes a 'node', a property name ('prop'), index ('index') and
252 a cell ('cell') and it will look to see if that node has a property
253 called 'prop' and if that 'prop' is an phandle-array type.
254 Then it will check if that phandle array has a cell matching the given index
255 and then return the value of the cell named 'cell' in this array index.
256 If not found it will return 0.
257
258 The function will divide the value based on 'unit':
259 None No division
260 'k' or 'K' divide by 1024 (1 << 10)
261 'm' or 'M' divide by 1,048,576 (1 << 20)
262 'g' or 'G' divide by 1,073,741,824 (1 << 30)
263 """
264 if not node:
265 return 0
266
267 if prop not in node.props:
268 return 0
269 if node.props[prop].type != "phandle-array":
270 return 0
271 if int(index) >= len(node.props[prop].val):
272 return 0
273 if cell not in node.props[prop].val[int(index)].data.keys():
274 return 0
275 return node.props[prop].val[int(index)].data[cell] >> _dt_units_to_scale(unit)
Eivind Jølsgard3865e082022-03-18 10:50:00 +0100276
Kumar Gala22e74492019-10-23 15:15:59 -0500277def _dt_chosen_reg_addr(kconf, chosen, index=0, unit=None):
Kumar Gala57353972019-08-28 09:30:23 -0500278 """
279 This function takes a 'chosen' property and treats that property as a path
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200280 to an EDT node. If it finds an EDT node, it will look to see if that
Nazar Kazakovf483b1b2022-03-16 21:07:43 +0000281 node has a register at the given 'index' and return the address value of
Kumar Gala57353972019-08-28 09:30:23 -0500282 that reg, if not we return 0.
283
284 The function will divide the value based on 'unit':
285 None No division
286 'k' or 'K' divide by 1024 (1 << 10)
287 'm' or 'M' divide by 1,048,576 (1 << 20)
288 'g' or 'G' divide by 1,073,741,824 (1 << 30)
Daniel DeGrasse6064da82022-05-25 17:38:59 -0500289 'kb' or 'Kb' divide by 8192 (1 << 13)
290 'mb' or 'Mb' divide by 8,388,608 (1 << 23)
291 'gb' or 'Gb' divide by 8,589,934,592 (1 << 33)
Kumar Gala57353972019-08-28 09:30:23 -0500292 """
293 if doc_mode or edt is None:
Kumar Gala22e74492019-10-23 15:15:59 -0500294 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500295
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200296 node = edt.chosen_node(chosen)
Kumar Gala57353972019-08-28 09:30:23 -0500297
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200298 return _node_reg_addr(node, index, unit)
Kumar Gala57353972019-08-28 09:30:23 -0500299
300
Kumar Gala22e74492019-10-23 15:15:59 -0500301def _dt_chosen_reg_size(kconf, chosen, index=0, unit=None):
Kumar Gala57353972019-08-28 09:30:23 -0500302 """
303 This function takes a 'chosen' property and treats that property as a path
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200304 to an EDT node. If it finds an EDT node, it will look to see if that node
305 has a register at the given 'index' and return the size value of that reg,
306 if not we return 0.
Kumar Gala57353972019-08-28 09:30:23 -0500307
308 The function will divide the value based on 'unit':
309 None No division
310 'k' or 'K' divide by 1024 (1 << 10)
311 'm' or 'M' divide by 1,048,576 (1 << 20)
312 'g' or 'G' divide by 1,073,741,824 (1 << 30)
Daniel DeGrasse6064da82022-05-25 17:38:59 -0500313 'kb' or 'Kb' divide by 8192 (1 << 13)
314 'mb' or 'Mb' divide by 8,388,608 (1 << 23)
315 'gb' or 'Gb' divide by 8,589,934,592 (1 << 33)
Kumar Gala57353972019-08-28 09:30:23 -0500316 """
317 if doc_mode or edt is None:
Kumar Gala22e74492019-10-23 15:15:59 -0500318 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500319
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200320 node = edt.chosen_node(chosen)
Kumar Gala57353972019-08-28 09:30:23 -0500321
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200322 return _node_reg_size(node, index, unit)
Kumar Gala57353972019-08-28 09:30:23 -0500323
324
Kumar Gala22e74492019-10-23 15:15:59 -0500325def dt_chosen_reg(kconf, name, chosen, index=0, unit=None):
326 """
327 This function just routes to the proper function and converts
328 the result to either a string int or string hex value.
329 """
330 if name == "dt_chosen_reg_size_int":
331 return str(_dt_chosen_reg_size(kconf, chosen, index, unit))
332 if name == "dt_chosen_reg_size_hex":
333 return hex(_dt_chosen_reg_size(kconf, chosen, index, unit))
334 if name == "dt_chosen_reg_addr_int":
335 return str(_dt_chosen_reg_addr(kconf, chosen, index, unit))
336 if name == "dt_chosen_reg_addr_hex":
337 return hex(_dt_chosen_reg_addr(kconf, chosen, index, unit))
338
339
Gerard Marull-Paretas018cf082024-02-01 10:25:18 +0100340def _dt_chosen_partition_addr(kconf, chosen, index=0, unit=None):
341 """
342 This function takes a 'chosen' property and treats that property as a path
343 to an EDT node. If it finds an EDT node, it will look to see if that
344 node has a register, and if that node has a grandparent that has a register
345 at the given 'index'. The addition of both addresses will be returned, if
346 not, we return 0.
347
348 The function will divide the value based on 'unit':
349 None No division
350 'k' or 'K' divide by 1024 (1 << 10)
351 'm' or 'M' divide by 1,048,576 (1 << 20)
352 'g' or 'G' divide by 1,073,741,824 (1 << 30)
353 'kb' or 'Kb' divide by 8192 (1 << 13)
354 'mb' or 'Mb' divide by 8,388,608 (1 << 23)
355 'gb' or 'Gb' divide by 8,589,934,592 (1 << 33)
356 """
357 if doc_mode or edt is None:
358 return 0
359
360 node = edt.chosen_node(chosen)
361 if not node:
362 return 0
363
364 p_node = node.parent
365 if not p_node:
366 return 0
367
368 return _node_reg_addr(p_node.parent, index, unit) + _node_reg_addr(node, 0, unit)
369
370
371def dt_chosen_partition_addr(kconf, name, chosen, index=0, unit=None):
372 """
373 This function just routes to the proper function and converts
374 the result to either a string int or string hex value.
375 """
376 if name == "dt_chosen_partition_addr_int":
377 return str(_dt_chosen_partition_addr(kconf, chosen, index, unit))
378 if name == "dt_chosen_partition_addr_hex":
379 return hex(_dt_chosen_partition_addr(kconf, chosen, index, unit))
380
381
Kumar Gala22e74492019-10-23 15:15:59 -0500382def _dt_node_reg_addr(kconf, path, index=0, unit=None):
Kumar Gala57353972019-08-28 09:30:23 -0500383 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200384 This function takes a 'path' and looks for an EDT node at that path. If it
385 finds an EDT node, it will look to see if that node has a register at the
386 given 'index' and return the address value of that reg, if not we return 0.
Kumar Gala57353972019-08-28 09:30:23 -0500387
388 The function will divide the value based on 'unit':
389 None No division
390 'k' or 'K' divide by 1024 (1 << 10)
391 'm' or 'M' divide by 1,048,576 (1 << 20)
392 'g' or 'G' divide by 1,073,741,824 (1 << 30)
Daniel DeGrasse6064da82022-05-25 17:38:59 -0500393 'kb' or 'Kb' divide by 8192 (1 << 13)
394 'mb' or 'Mb' divide by 8,388,608 (1 << 23)
395 'gb' or 'Gb' divide by 8,589,934,592 (1 << 33)
Kumar Gala57353972019-08-28 09:30:23 -0500396 """
397 if doc_mode or edt is None:
Kumar Gala22e74492019-10-23 15:15:59 -0500398 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500399
400 try:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200401 node = edt.get_node(path)
Kumar Gala57353972019-08-28 09:30:23 -0500402 except edtlib.EDTError:
Kumar Gala22e74492019-10-23 15:15:59 -0500403 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500404
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200405 return _node_reg_addr(node, index, unit)
Kumar Gala57353972019-08-28 09:30:23 -0500406
407
Kumar Gala22e74492019-10-23 15:15:59 -0500408def _dt_node_reg_size(kconf, path, index=0, unit=None):
Kumar Gala57353972019-08-28 09:30:23 -0500409 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200410 This function takes a 'path' and looks for an EDT node at that path. If it
411 finds an EDT node, it will look to see if that node has a register at the
412 given 'index' and return the size value of that reg, if not we return 0.
Kumar Gala57353972019-08-28 09:30:23 -0500413
414 The function will divide the value based on 'unit':
415 None No division
416 'k' or 'K' divide by 1024 (1 << 10)
417 'm' or 'M' divide by 1,048,576 (1 << 20)
418 'g' or 'G' divide by 1,073,741,824 (1 << 30)
Daniel DeGrasse6064da82022-05-25 17:38:59 -0500419 'kb' or 'Kb' divide by 8192 (1 << 13)
420 'mb' or 'Mb' divide by 8,388,608 (1 << 23)
421 'gb' or 'Gb' divide by 8,589,934,592 (1 << 33)
Kumar Gala57353972019-08-28 09:30:23 -0500422 """
423 if doc_mode or edt is None:
Kumar Gala22e74492019-10-23 15:15:59 -0500424 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500425
426 try:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200427 node = edt.get_node(path)
Kumar Gala57353972019-08-28 09:30:23 -0500428 except edtlib.EDTError:
Kumar Gala22e74492019-10-23 15:15:59 -0500429 return 0
Kumar Gala57353972019-08-28 09:30:23 -0500430
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200431 return _node_reg_size(node, index, unit)
Kumar Gala57353972019-08-28 09:30:23 -0500432
433
Kumar Gala22e74492019-10-23 15:15:59 -0500434def dt_node_reg(kconf, name, path, index=0, unit=None):
435 """
436 This function just routes to the proper function and converts
437 the result to either a string int or string hex value.
438 """
439 if name == "dt_node_reg_size_int":
440 return str(_dt_node_reg_size(kconf, path, index, unit))
441 if name == "dt_node_reg_size_hex":
442 return hex(_dt_node_reg_size(kconf, path, index, unit))
443 if name == "dt_node_reg_addr_int":
444 return str(_dt_node_reg_addr(kconf, path, index, unit))
445 if name == "dt_node_reg_addr_hex":
446 return hex(_dt_node_reg_addr(kconf, path, index, unit))
447
Jordan Yates999afdc2023-07-30 13:33:33 +1000448def dt_nodelabel_reg(kconf, name, label, index=0, unit=None):
449 """
450 This function is like dt_node_reg(), but the 'label' argument
451 should be a node label, like "foo" is here:
452
453 foo: some-node { ... };
454 """
455 if doc_mode or edt is None:
456 node = None
457 else:
458 node = edt.label2node.get(label)
459
460 if name == "dt_nodelabel_reg_size_int":
461 return str(_dt_node_reg_size(kconf, node.path, index, unit)) if node else "0"
462 if name == "dt_nodelabel_reg_size_hex":
463 return hex(_dt_node_reg_size(kconf, node.path, index, unit)) if node else "0x0"
464 if name == "dt_nodelabel_reg_addr_int":
465 return str(_dt_node_reg_addr(kconf, node.path, index, unit)) if node else "0"
466 if name == "dt_nodelabel_reg_addr_hex":
467 return hex(_dt_node_reg_addr(kconf, node.path, index, unit)) if node else "0x0"
468
469
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100470def _dt_node_bool_prop_generic(node_search_function, search_arg, prop):
Kumar Gala57353972019-08-28 09:30:23 -0500471 """
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100472 This function takes the 'node_search_function' and uses it to search for
473 a node with 'search_arg' and if node exists, checks if 'prop' exists
474 inside the node and is a boolean, if it is true, returns "y".
475 Otherwise, it returns "n".
Kumar Gala57353972019-08-28 09:30:23 -0500476 """
Kumar Gala57353972019-08-28 09:30:23 -0500477 try:
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100478 node = node_search_function(search_arg)
Kumar Gala57353972019-08-28 09:30:23 -0500479 except edtlib.EDTError:
480 return "n"
481
Andrzej Głąbek23994332022-03-23 14:45:11 +0100482 if node is None:
483 return "n"
484
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200485 if prop not in node.props:
Kumar Gala57353972019-08-28 09:30:23 -0500486 return "n"
487
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200488 if node.props[prop].type != "boolean":
Kumar Gala57353972019-08-28 09:30:23 -0500489 return "n"
490
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200491 if node.props[prop].val:
Kumar Gala57353972019-08-28 09:30:23 -0500492 return "y"
493
494 return "n"
495
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100496def dt_node_bool_prop(kconf, _, path, prop):
Erwan Gourioub7110282021-01-07 12:06:31 +0100497 """
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100498 This function takes a 'path' and looks for an EDT node at that path. If it
499 finds an EDT node, it will look to see if that node has a boolean property
Erwan Gourioub7110282021-01-07 12:06:31 +0100500 by the name of 'prop'. If the 'prop' exists it will return "y" otherwise
501 we return "n".
502 """
Erwan Gourioub7110282021-01-07 12:06:31 +0100503 if doc_mode or edt is None:
504 return "n"
505
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100506 return _dt_node_bool_prop_generic(edt.get_node, path, prop)
507
508def dt_nodelabel_bool_prop(kconf, _, label, prop):
509 """
510 This function takes a 'label' and looks for an EDT node with that label.
511 If it finds an EDT node, it will look to see if that node has a boolean
512 property by the name of 'prop'. If the 'prop' exists it will return "y"
513 otherwise we return "n".
514 """
515 if doc_mode or edt is None:
516 return "n"
517
518 return _dt_node_bool_prop_generic(edt.label2node.get, label, prop)
519
Stephen Stautsd4b8db72022-12-27 11:40:17 +0100520def dt_chosen_bool_prop(kconf, _, chosen, prop):
521 """
522 This function takes a /chosen node property named 'chosen', and
523 looks for the chosen node. If that node exists and has a boolean
524 property 'prop', it returns "y". Otherwise, it returns "n".
525 """
526 if doc_mode or edt is None:
527 return "n"
528
529 return _dt_node_bool_prop_generic(edt.chosen_node, chosen, prop)
530
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100531def _dt_node_has_prop_generic(node_search_function, search_arg, prop):
532 """
533 This function takes the 'node_search_function' and uses it to search for
534 a node with 'search_arg' and if node exists, then checks if 'prop'
535 exists inside the node and returns "y". Otherwise, it returns "n".
536 """
Erwan Gourioub7110282021-01-07 12:06:31 +0100537 try:
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100538 node = node_search_function(search_arg)
Erwan Gourioub7110282021-01-07 12:06:31 +0100539 except edtlib.EDTError:
540 return "n"
541
542 if node is None:
543 return "n"
544
545 if prop in node.props:
546 return "y"
547
548 return "n"
Kumar Gala57353972019-08-28 09:30:23 -0500549
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100550def dt_node_has_prop(kconf, _, path, prop):
551 """
552 This function takes a 'path' and looks for an EDT node at that path. If it
553 finds an EDT node, it will look to see if that node has a property
554 by the name of 'prop'. If the 'prop' exists it will return "y" otherwise
555 it returns "n".
556 """
557 if doc_mode or edt is None:
558 return "n"
559
560 return _dt_node_has_prop_generic(edt.get_node, path, prop)
561
562def dt_nodelabel_has_prop(kconf, _, label, prop):
563 """
564 This function takes a 'label' and looks for an EDT node with that label.
565 If it finds an EDT node, it will look to see if that node has a property
566 by the name of 'prop'. If the 'prop' exists it will return "y" otherwise
567 it returns "n".
568 """
569 if doc_mode or edt is None:
570 return "n"
571
572 return _dt_node_has_prop_generic(edt.label2node.get, label, prop)
573
David Leach1e5a8302021-11-08 23:24:22 -0600574def dt_node_int_prop(kconf, name, path, prop, unit=None):
Kumar Gala338b43192020-04-03 05:02:35 -0500575 """
576 This function takes a 'path' and property name ('prop') looks for an EDT
577 node at that path. If it finds an EDT node, it will look to see if that
578 node has a property called 'prop' and if that 'prop' is an integer type
579 will return the value of the property 'prop' as either a string int or
580 string hex value, if not we return 0.
Kumar Gala338b43192020-04-03 05:02:35 -0500581
David Leach1e5a8302021-11-08 23:24:22 -0600582 The function will divide the value based on 'unit':
583 None No division
584 'k' or 'K' divide by 1024 (1 << 10)
585 'm' or 'M' divide by 1,048,576 (1 << 20)
586 'g' or 'G' divide by 1,073,741,824 (1 << 30)
Daniel DeGrasse6064da82022-05-25 17:38:59 -0500587 'kb' or 'Kb' divide by 8192 (1 << 13)
588 'mb' or 'Mb' divide by 8,388,608 (1 << 23)
589 'gb' or 'Gb' divide by 8,589,934,592 (1 << 33)
David Leach1e5a8302021-11-08 23:24:22 -0600590 """
Kumar Gala338b43192020-04-03 05:02:35 -0500591 if doc_mode or edt is None:
592 return "0"
593
594 try:
595 node = edt.get_node(path)
596 except edtlib.EDTError:
597 return "0"
598
599 if name == "dt_node_int_prop_int":
David Leach1e5a8302021-11-08 23:24:22 -0600600 return str(_node_int_prop(node, prop, unit))
Kumar Gala338b43192020-04-03 05:02:35 -0500601 if name == "dt_node_int_prop_hex":
David Leach1e5a8302021-11-08 23:24:22 -0600602 return hex(_node_int_prop(node, prop, unit))
Kumar Gala338b43192020-04-03 05:02:35 -0500603
Eivind Jølsgard3865e082022-03-18 10:50:00 +0100604
605def dt_node_array_prop(kconf, name, path, prop, index, unit=None):
606 """
607 This function takes a 'path', property name ('prop') and index ('index')
608 and looks for an EDT node at that path. If it finds an EDT node, it will
609 look to see if that node has a property called 'prop' and if that 'prop'
610 is an array type will return the value of the property 'prop' at the given
611 'index' as either a string int or string hex value. If not found we return 0.
612
613 The function will divide the value based on 'unit':
614 None No division
615 'k' or 'K' divide by 1024 (1 << 10)
616 'm' or 'M' divide by 1,048,576 (1 << 20)
617 'g' or 'G' divide by 1,073,741,824 (1 << 30)
618 """
619 if doc_mode or edt is None:
620 return "0"
621
622 try:
623 node = edt.get_node(path)
624 except edtlib.EDTError:
625 return "0"
626 if name == "dt_node_array_prop_int":
627 return str(_node_array_prop(node, prop, index, unit))
628 if name == "dt_node_array_prop_hex":
629 return hex(_node_array_prop(node, prop, index, unit))
630
631
Erwan Gourioua59e01d2023-12-18 12:03:43 +0100632def dt_node_ph_array_prop(kconf, name, path, prop, index, cell, unit=None):
633 """
634 This function takes a 'path', property name ('prop'), index ('index') and
635 a cell ('cell') and looks for an EDT node at that path.
636 If it finds an EDT node, it will look to see if that node has a property
637 called 'prop' and if that 'prop' is an phandle-array type.
638 Then it will check if that phandle array has a cell matching the given index
639 and ten return the value of the cell named 'cell' in this array index as
640 either a string int or string hex value. If not found we return 0.
641
642 The function will divide the value based on 'unit':
643 None No division
644 'k' or 'K' divide by 1024 (1 << 10)
645 'm' or 'M' divide by 1,048,576 (1 << 20)
646 'g' or 'G' divide by 1,073,741,824 (1 << 30)
647 """
648 if doc_mode or edt is None:
649 return "0"
650
651 try:
652 node = edt.get_node(path)
653 except edtlib.EDTError:
654 return "0"
655 if name == "dt_node_ph_array_prop_int":
656 return str(_node_ph_array_prop(node, prop, index, cell, unit))
657 if name == "dt_node_ph_array_prop_hex":
658 return hex(_node_ph_array_prop(node, prop, index, cell, unit))
659
Johann Fischer73199f22021-11-18 17:12:23 +0100660def dt_node_str_prop_equals(kconf, _, path, prop, val):
661 """
662 This function takes a 'path' and property name ('prop') looks for an EDT
663 node at that path. If it finds an EDT node, it will look to see if that
664 node has a property 'prop' of type string. If that 'prop' is equal to 'val'
665 it will return "y" otherwise return "n".
666 """
667
668 if doc_mode or edt is None:
669 return "n"
670
671 try:
672 node = edt.get_node(path)
673 except edtlib.EDTError:
674 return "n"
675
676 if prop not in node.props:
677 return "n"
678
679 if node.props[prop].type != "string":
680 return "n"
681
682 if node.props[prop].val == val:
683 return "y"
684
685 return "n"
Kumar Gala338b43192020-04-03 05:02:35 -0500686
Andrzej Głąbek9f2a27b2022-03-15 14:08:21 +0100687
688def dt_has_compat(kconf, _, compat):
689 """
690 This function takes a 'compat' and returns "y" if any compatible node
691 can be found in the EDT, otherwise it returns "n".
692 """
693 if doc_mode or edt is None:
694 return "n"
695
696 return "y" if compat in edt.compat2nodes else "n"
697
698
Kumar Gala57353972019-08-28 09:30:23 -0500699def dt_compat_enabled(kconf, _, compat):
700 """
Martí Bolívar81650082020-10-05 20:02:13 -0700701 This function takes a 'compat' and returns "y" if we find a status "okay"
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200702 compatible node in the EDT otherwise we return "n"
Kumar Gala57353972019-08-28 09:30:23 -0500703 """
704 if doc_mode or edt is None:
705 return "n"
706
Martí Bolívar81650082020-10-05 20:02:13 -0700707 return "y" if compat in edt.compat2okay else "n"
Kumar Gala57353972019-08-28 09:30:23 -0500708
709
Martí Bolívar3cd4f6c2020-04-10 15:37:58 -0700710def dt_compat_on_bus(kconf, _, compat, bus):
711 """
712 This function takes a 'compat' and returns "y" if we find an "enabled"
713 compatible node in the EDT which is on bus 'bus'. It returns "n" otherwise.
714 """
715 if doc_mode or edt is None:
716 return "n"
717
Andrzej Głąbekf9c1f892021-02-03 13:53:48 +0100718 if compat in edt.compat2okay:
719 for node in edt.compat2okay[compat]:
Daniel Leung418c9152022-08-26 10:52:32 -0700720 if node.on_buses is not None and bus in node.on_buses:
Andrzej Głąbekf9c1f892021-02-03 13:53:48 +0100721 return "y"
Martí Bolívar3cd4f6c2020-04-10 15:37:58 -0700722
723 return "n"
724
725
Andrzej Głąbek3331a202020-02-14 11:53:21 +0100726def dt_nodelabel_has_compat(kconf, _, label, compat):
727 """
Andrzej Głąbekcd00a3a2022-03-28 12:16:51 +0200728 This function takes a 'label' and looks for an EDT node with that label.
729 If it finds such node, it returns "y" if this node is compatible with
730 the provided 'compat'. Otherwise, it return "n" .
731 """
732 if doc_mode or edt is None:
733 return "n"
734
735 node = edt.label2node.get(label)
736
737 if node and compat in node.compats:
738 return "y"
739
740 return "n"
741
Daniel DeGrasse6aa71942022-08-26 12:12:24 -0500742def dt_node_has_compat(kconf, _, path, compat):
743 """
744 This function takes a 'path' and looks for an EDT node at that path. If it
745 finds an EDT node, it returns "y" if this node is compatible with
746 the provided 'compat'. Otherwise, it return "n" .
747 """
748
749 if doc_mode or edt is None:
750 return "n"
751
752 try:
753 node = edt.get_node(path)
754 except edtlib.EDTError:
755 return "n"
756
757 if node and compat in node.compats:
758 return "y"
759
760 return "n"
Andrzej Głąbekcd00a3a2022-03-28 12:16:51 +0200761
762def dt_nodelabel_enabled_with_compat(kconf, _, label, compat):
763 """
Andrzej Głąbek3331a202020-02-14 11:53:21 +0100764 This function takes a 'label' and returns "y" if an "enabled" node with
765 such label can be found in the EDT and that node is compatible with the
766 provided 'compat', otherwise it returns "n".
767 """
768 if doc_mode or edt is None:
769 return "n"
770
Andrzej Głąbekf9c1f892021-02-03 13:53:48 +0100771 if compat in edt.compat2okay:
772 for node in edt.compat2okay[compat]:
773 if label in node.labels:
774 return "y"
Andrzej Głąbek3331a202020-02-14 11:53:21 +0100775
776 return "n"
777
778
Stephen Stautsd4b8db72022-12-27 11:40:17 +0100779def dt_nodelabel_array_prop_has_val(kconf, _, label, prop, val):
780 """
781 This function looks for a node with node label 'label'.
782 If the node exists, it checks if the node node has a property
783 'prop' with type "array". If so, and the property contains
784 an element equal to the integer 'val', it returns "y".
785 Otherwise, it returns "n".
786 """
787 if doc_mode or edt is None:
788 return "n"
789
790 node = edt.label2node.get(label)
791
792 if not node or (prop not in node.props) or (node.props[prop].type != "array"):
793 return "n"
794 else:
795 return "y" if int(val, base=0) in node.props[prop].val else "n"
796
797
Martí Bolívar1fff2352020-04-22 18:06:18 -0700798def dt_nodelabel_path(kconf, _, label):
799 """
800 This function takes a node label (not a label property) and
801 returns the path to the node which has that label, or an empty
802 string if there is no such node.
803 """
804 if doc_mode or edt is None:
805 return ""
806
807 node = edt.label2node.get(label)
808
809 return node.path if node else ""
810
Daniel DeGrassedb9bcd92022-08-26 12:10:14 -0500811def dt_node_parent(kconf, _, path):
812 """
813 This function takes a 'path' and looks for an EDT node at that path. If it
814 finds an EDT node, it will look for the parent of that node. If the parent
815 exists, it will return the path to that parent. Otherwise, an empty string
816 will be returned.
817 """
818 if doc_mode or edt is None:
819 return ""
820
821 try:
822 node = edt.get_node(path)
823 except edtlib.EDTError:
824 return ""
825
826 if node is None:
827 return ""
828
829 return node.parent.path if node.parent else ""
Martí Bolívar1fff2352020-04-22 18:06:18 -0700830
Henrik Brix Andersenfba6c7f2023-01-18 17:43:02 +0100831def dt_gpio_hogs_enabled(kconf, _):
832 """
833 Return "y" if any GPIO hog node is enabled. Otherwise, return "n".
834 """
835 if doc_mode or edt is None:
836 return "n"
837
838 for node in edt.nodes:
839 if node.gpio_hogs and node.status == "okay":
840 return "y"
841
842 return "n"
843
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +0200844
Torsten Rasmussen732c5042024-03-19 15:26:59 +0100845def normalize_upper(kconf, _, string):
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +0200846 """
Torsten Rasmussen732c5042024-03-19 15:26:59 +0100847 Normalize the string, so that the string only contains alpha-numeric
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +0200848 characters or underscores. All non-alpha-numeric characters are replaced
849 with an underscore, '_'.
Torsten Rasmussen732c5042024-03-19 15:26:59 +0100850 When string has been normalized it will be converted into upper case.
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +0200851 """
852 return re.sub(r'[^a-zA-Z0-9_]', '_', string).upper()
853
854
Erwan Gouriou17537c72019-11-22 10:06:57 +0100855def shields_list_contains(kconf, _, shield):
856 """
857 Return "n" if cmake environment variable 'SHIELD_AS_LIST' doesn't exist.
858 Return "y" if 'shield' is present list obtained after 'SHIELD_AS_LIST'
859 has been split using ";" as a separator and "n" otherwise.
860 """
861 try:
862 list = os.environ['SHIELD_AS_LIST']
863 except KeyError:
864 return "n"
865
866 return "y" if shield in list.split(";") else "n"
867
868
Jamie McCraea4302942024-03-13 08:21:33 +0000869def substring(kconf, _, string, start, stop=None):
870 """
871 Extracts a portion of the string, removing characters from the front, back or both.
872 """
873 if stop is not None:
874 return string[int(start):int(stop)]
875 else:
876 return string[int(start):]
877
878
Martí Bolívar006319f2020-07-21 15:20:18 -0700879# Keys in this dict are the function names as they appear
880# in Kconfig files. The values are tuples in this form:
881#
882# (python_function, minimum_number_of_args, maximum_number_of_args)
883#
884# Each python function is given a kconf object and its name in the
885# Kconfig file, followed by arguments from the Kconfig file.
886#
887# See the kconfiglib documentation for more details.
Kumar Gala87915cd2018-11-15 11:51:50 -0600888functions = {
Andrzej Głąbek9f2a27b2022-03-15 14:08:21 +0100889 "dt_has_compat": (dt_has_compat, 1, 1),
Kumar Gala57353972019-08-28 09:30:23 -0500890 "dt_compat_enabled": (dt_compat_enabled, 1, 1),
Martí Bolívar3cd4f6c2020-04-10 15:37:58 -0700891 "dt_compat_on_bus": (dt_compat_on_bus, 2, 2),
Kumar Gala57353972019-08-28 09:30:23 -0500892 "dt_chosen_label": (dt_chosen_label, 1, 1),
Kumar Gala07e5d892019-11-04 10:20:59 -0600893 "dt_chosen_enabled": (dt_chosen_enabled, 1, 1),
Martí Bolívar7ff3ebc2020-04-23 12:28:06 -0700894 "dt_chosen_path": (dt_chosen_path, 1, 1),
Daniel DeGrassefbefc502022-04-11 11:53:03 -0500895 "dt_chosen_has_compat": (dt_chosen_has_compat, 2, 2),
Martí Bolívar24677f92020-04-28 16:52:35 -0700896 "dt_path_enabled": (dt_node_enabled, 1, 1),
897 "dt_alias_enabled": (dt_node_enabled, 1, 1),
Kumar Gala0353d222020-02-13 23:43:42 -0600898 "dt_nodelabel_enabled": (dt_nodelabel_enabled, 1, 1),
Andrzej Głąbekcd00a3a2022-03-28 12:16:51 +0200899 "dt_nodelabel_enabled_with_compat": (dt_nodelabel_enabled_with_compat, 2, 2),
Ulf Magnussonb4e18072019-12-23 11:07:27 +0100900 "dt_chosen_reg_addr_int": (dt_chosen_reg, 1, 3),
901 "dt_chosen_reg_addr_hex": (dt_chosen_reg, 1, 3),
902 "dt_chosen_reg_size_int": (dt_chosen_reg, 1, 3),
903 "dt_chosen_reg_size_hex": (dt_chosen_reg, 1, 3),
904 "dt_node_reg_addr_int": (dt_node_reg, 1, 3),
905 "dt_node_reg_addr_hex": (dt_node_reg, 1, 3),
906 "dt_node_reg_size_int": (dt_node_reg, 1, 3),
907 "dt_node_reg_size_hex": (dt_node_reg, 1, 3),
Jordan Yates999afdc2023-07-30 13:33:33 +1000908 "dt_nodelabel_reg_addr_int": (dt_nodelabel_reg, 1, 3),
909 "dt_nodelabel_reg_addr_hex": (dt_nodelabel_reg, 1, 3),
910 "dt_nodelabel_reg_size_int": (dt_nodelabel_reg, 1, 3),
911 "dt_nodelabel_reg_size_hex": (dt_nodelabel_reg, 1, 3),
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100912 "dt_node_bool_prop": (dt_node_bool_prop, 2, 2),
913 "dt_nodelabel_bool_prop": (dt_nodelabel_bool_prop, 2, 2),
Stephen Stautsd4b8db72022-12-27 11:40:17 +0100914 "dt_chosen_bool_prop": (dt_chosen_bool_prop, 2, 2),
Erwan Gourioub7110282021-01-07 12:06:31 +0100915 "dt_node_has_prop": (dt_node_has_prop, 2, 2),
Michał Barnaśa1ab8da2022-03-08 17:44:04 +0100916 "dt_nodelabel_has_prop": (dt_nodelabel_has_prop, 2, 2),
David Leach1e5a8302021-11-08 23:24:22 -0600917 "dt_node_int_prop_int": (dt_node_int_prop, 2, 3),
918 "dt_node_int_prop_hex": (dt_node_int_prop, 2, 3),
Eivind Jølsgard3865e082022-03-18 10:50:00 +0100919 "dt_node_array_prop_int": (dt_node_array_prop, 3, 4),
920 "dt_node_array_prop_hex": (dt_node_array_prop, 3, 4),
Erwan Gourioua59e01d2023-12-18 12:03:43 +0100921 "dt_node_ph_array_prop_int": (dt_node_ph_array_prop, 4, 5),
922 "dt_node_ph_array_prop_hex": (dt_node_ph_array_prop, 4, 5),
Johann Fischer73199f22021-11-18 17:12:23 +0100923 "dt_node_str_prop_equals": (dt_node_str_prop_equals, 3, 3),
Andrzej Głąbek3331a202020-02-14 11:53:21 +0100924 "dt_nodelabel_has_compat": (dt_nodelabel_has_compat, 2, 2),
Daniel DeGrasse6aa71942022-08-26 12:12:24 -0500925 "dt_node_has_compat": (dt_node_has_compat, 2, 2),
Martí Bolívar1fff2352020-04-22 18:06:18 -0700926 "dt_nodelabel_path": (dt_nodelabel_path, 1, 1),
Daniel DeGrassedb9bcd92022-08-26 12:10:14 -0500927 "dt_node_parent": (dt_node_parent, 1, 1),
Stephen Stautsd4b8db72022-12-27 11:40:17 +0100928 "dt_nodelabel_array_prop_has_val": (dt_nodelabel_array_prop_has_val, 3, 3),
Henrik Brix Andersenfba6c7f2023-01-18 17:43:02 +0100929 "dt_gpio_hogs_enabled": (dt_gpio_hogs_enabled, 0, 0),
Gerard Marull-Paretas018cf082024-02-01 10:25:18 +0100930 "dt_chosen_partition_addr_int": (dt_chosen_partition_addr, 1, 3),
931 "dt_chosen_partition_addr_hex": (dt_chosen_partition_addr, 1, 3),
Torsten Rasmussen732c5042024-03-19 15:26:59 +0100932 "normalize_upper": (normalize_upper, 1, 1),
Erwan Gouriou17537c72019-11-22 10:06:57 +0100933 "shields_list_contains": (shields_list_contains, 1, 1),
Jamie McCraea4302942024-03-13 08:21:33 +0000934 "substring": (substring, 2, 3),
Kumar Gala87915cd2018-11-15 11:51:50 -0600935}