Driver improvements (#535)

- New drivers:
  - SD SPI
  - spi_peripheral
  - touch_placeholder
  - display_placeholder
- Devicetree compiler:
  - Implement phandle-arrays
  - Implement device addresses
- Add placeholder drivers to all devices with a SPI display
- SPI driver: add `cs-pins` and set them to high on driver start
- FileSystem: add `file_system_set_owner()` and `file_system_get_owner()`
- File locking is now checking if the related `FileSystem` is part of a shared SPI bus
- Add `device_get_child_count()`
- SDMMC driver: Remove default of `slot` value
- Fix for Crowpanel Basic 3.5" display (add delay to booting)
- Fix for LilyGO T-HMI SD card mounting (delayed mounting by disabling it initially in the dts)
This commit is contained in:
Ken Van Hoeylandt
2026-06-27 17:32:05 +02:00
committed by GitHub
parent e50659a3fb
commit 599fa46766
222 changed files with 1713 additions and 2257 deletions
@@ -45,6 +45,7 @@ def parse_binding(file_path: str, binding_dirs: list[str]) -> Binding:
required=details.get('required', False),
description=details.get('description', '').strip(),
default=details.get('default', None),
element_type=details.get('element-type', None),
)
properties_dict[name] = prop
filename = os.path.basename(file_path)
@@ -80,7 +80,7 @@ def property_to_string(property: DeviceProperty, devices: list[Device]) -> str:
return "{ " + ",".join(value_list) + " }"
elif type == "phandle":
return find_phandle(devices, property.value)
elif type == "phandle-array":
elif type == "phandles":
value_list = list()
if isinstance(property.value, list):
for item in property.value:
@@ -93,10 +93,26 @@ def property_to_string(property: DeviceProperty, devices: list[Device]) -> str:
# If it's a string, assume it's a #define and show it as-is
return property.value
else:
raise Exception(f"Unsupported phandle-array type for {property.value}")
raise Exception(f"Unsupported phandles type for {property.name} with value {property.value} ")
else:
raise DevicetreeException(f"property_to_string() has an unsupported type: {type}")
def resolve_phandle_array_entries(device_property, devices):
"""Convert a phandle-array DTS property into a list of C initializer strings."""
entries = []
if device_property.type == "phandle-array":
items = device_property.value
elif device_property.type == "values":
items = [PropertyValue(type="values", value=device_property.value)]
else:
return []
for item in items:
if isinstance(item, PropertyValue):
entries.append(property_to_string(DeviceProperty(name="", type=item.type, value=item.value), devices))
else:
entries.append(str(item))
return entries
def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], devices: list[Device]) -> list:
compatible_property = find_device_property(device, "compatible")
if compatible_property is None:
@@ -119,11 +135,32 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
if device_property.name not in binding_property_names:
raise DevicetreeException(f"Device '{device.node_name}' has invalid property '{device_property.name}'")
# Allocate total expected configuration arguments
result = [0] * len(binding_properties)
for index, binding_property in enumerate(binding_properties):
node_name = get_device_node_name_safe(device)
result = []
phandle_arrays = []
for binding_property in binding_properties:
device_property = find_device_property(device, binding_property.name)
# No property specified in DTS, use binding defaults
if binding_property.type == "phandle-array":
if binding_property.element_type is None:
raise DevicetreeException(f"phandle-array property '{binding_property.name}' requires 'element-type' in binding")
prop_safe = binding_property.name.replace("-", "_")
array_var = f"{node_name}_{prop_safe}"
if device_property is not None:
entries = resolve_phandle_array_entries(device_property, devices)
phandle_arrays.append((array_var, binding_property.element_type, entries))
result.append(f"({binding_property.element_type}*){array_var}")
result.append(str(len(entries)))
elif binding_property.default is not None:
result.append("NULL")
result.append("0")
elif binding_property.required:
raise DevicetreeException(f"device {device.node_name} doesn't have property '{binding_property.name}'")
else:
result.append("NULL")
result.append("0")
continue
if device_property is None:
if binding_property.default is not None:
temp_prop = DeviceProperty(
@@ -131,30 +168,38 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
type=binding_property.type,
value=binding_property.default
)
result[index] = property_to_string(temp_prop, devices)
result.append(property_to_string(temp_prop, devices))
elif binding_property.required:
raise DevicetreeException(f"device {device.node_name} doesn't have property '{binding_property.name}'")
elif binding_property.type == "bool" or binding_property.type == "boolean":
if binding_property.default == "true" or binding_property.default == None:
result[index] = "true"
else: # Explicit or implied false
result[index] = "false"
result.append("true")
else:
result.append("false")
else:
raise DevicetreeException(f"Device {device.node_name} doesn't have property '{binding_property.name}' and no default value is set")
else:
result[index] = property_to_string(device_property, devices)
return result
result.append(property_to_string(device_property, devices))
return result, phandle_arrays
def write_config(file, device: Device, bindings: list[Binding], devices: list[Device], type_name: str):
node_name = get_device_node_name_safe(device)
config_type = f"{type_name}_config_dt"
config_variable_name = f"{node_name}_config"
config_params, phandle_arrays = resolve_parameters_from_bindings(device, bindings, devices)
# Write phandle-array variables before the config struct
for array_var, element_type, entries in phandle_arrays:
entries_str = ", ".join(entries)
file.write(f"static {element_type} {array_var}[] = {{ {entries_str} }};\n")
file.write(f"static const {config_type} {config_variable_name}" " = {\n")
config_params = resolve_parameters_from_bindings(device, bindings, devices)
# Indent all params
for index, config_param in enumerate(config_params):
config_params[index] = f"\t{config_param}"
# Join with command and newline
# Join with comma and newline
if len(config_params) > 0:
config_params_joined = ",\n".join(config_params)
file.write(f"{config_params_joined}\n")
@@ -178,7 +223,9 @@ def write_device_structs(file, device: Device, parent_device: Device, bindings:
# Write config struct
write_config(file, device, bindings, devices, type_name)
# Write device struct
address_value = device.node_address if device.node_address is not None else "0"
file.write(f"static struct Device {node_name}" " = {\n")
file.write(f"\t.address = {address_value},\n")
file.write(f"\t.name = \"{device.node_name}\",\n") # Use original name
file.write(f"\t.config = &{config_variable_name},\n")
file.write(f"\t.parent = {parent_value},\n")
@@ -37,13 +37,17 @@ value: VALUE
values: VALUE+
array: NUMBER+
property_value: quoted_text_array | QUOTED_TEXT | "<" value ">" | "<" values ">" | "[" array "]" | PHANDLE
phandle_array_entry: "<" values ">"
phandle_array: phandle_array_entry ("," phandle_array_entry)+
property_value: quoted_text_array | QUOTED_TEXT | phandle_array | "<" value ">" | "<" values ">" | "[" array "]" | PHANDLE
device_property: PROPERTY_NAME ["=" property_value] ";"
NODE_ALIAS: /[a-zA-Z0-9_\-\/@]+/
NODE_NAME: /[a-zA-Z0-9_\-\/@]+/
NODE_ALIAS: /[a-zA-Z0-9_\-\/]+/
NODE_NAME: /[a-zA-Z0-9_\-\/]+/
NODE_ADDRESS: /[0-9a-zA-Z_]+/
device: (NODE_ALIAS ":")? NODE_NAME "{" (device | device_property)* "};"
device: (NODE_ALIAS ":")? NODE_NAME ("@" NODE_ADDRESS)? "{" (device | device_property)* "};"
dts_version: /[0-9a-zA-Z\-]+/
@@ -8,6 +8,7 @@ class DtsVersion:
class Device:
node_name: str
node_alias: str
node_address: str
status: str
properties: list
devices: list
@@ -38,6 +39,7 @@ class BindingProperty:
required: bool
description: str
default: object = None
element_type: str = None
@dataclass
class Binding:
@@ -29,6 +29,7 @@ class DtsTransformer(Transformer):
def device(self, tokens: list):
node_name = None
node_alias = None
node_address = None
status = None
properties = list()
child_devices = list()
@@ -37,6 +38,8 @@ class DtsTransformer(Transformer):
node_name = item.value
elif type(item) is Token and item.type == 'NODE_ALIAS':
node_alias = item.value
elif type(item) is Token and item.type == 'NODE_ADDRESS':
node_address = item.value
elif type(item) is DeviceProperty:
if item.name == "status":
status = item.value
@@ -44,7 +47,7 @@ class DtsTransformer(Transformer):
properties.append(item)
elif type(item) is Device:
child_devices.append(item)
return Device(node_name, node_alias, status, properties, child_devices)
return Device(node_name, node_alias, node_address, status, properties, child_devices)
def device_property(self, objects: List[object]):
name = objects[0]
# Boolean property has no value as the value is implied to be true
@@ -67,6 +70,10 @@ class DtsTransformer(Transformer):
if isinstance(object[0], PropertyValue):
return object[0]
return PropertyValue(type="value", value=object[0])
def phandle_array_entry(self, tokens: list):
return tokens[0]
def phandle_array(self, tokens: list):
return PropertyValue(type="phandle-array", value=tokens)
def array(self, object):
return PropertyValue(type="array", value=object)
def VALUE(self, token: Token):
@@ -10,21 +10,23 @@ static const root_config_dt root_config = {
};
static struct Device root = {
.address = 0,
.name = "/",
.config = &root_config,
.parent = NULL,
.internal = NULL
};
static const generic_device_config_dt test_device@0_config = {
static const generic_device_config_dt test_device_config = {
0,
42,
"hello"
};
static struct Device test_device@0 = {
.name = "test-device@0",
.config = &test_device@0_config,
static struct Device test_device = {
.address = 0,
.name = "test-device",
.config = &test_device_config,
.parent = &root,
.internal = NULL
};
@@ -38,6 +40,7 @@ static const bool_device_config_dt bool_test_device_config = {
};
static struct Device bool_test_device = {
.address = 0,
.name = "bool-test-device",
.config = &bool_test_device_config,
.parent = &root,
@@ -46,7 +49,7 @@ static struct Device bool_test_device = {
struct DtsDevice dts_devices[] = {
{ &root, "test,root", DTS_DEVICE_STATUS_OKAY },
{ &test_device@0, "test,generic-device", DTS_DEVICE_STATUS_OKAY },
{ &test_device, "test,generic-device", DTS_DEVICE_STATUS_OKAY },
{ &bool_test_device, "test,bool-device", DTS_DEVICE_STATUS_OKAY },
DTS_DEVICE_TERMINATOR
};