diff options
-rw-r--r-- | src/acpi/Makefile.inc | 1 | ||||
-rw-r--r-- | src/acpi/acpigen_usb.c | 136 | ||||
-rw-r--r-- | src/include/acpi/acpigen_usb.h | 61 |
3 files changed, 198 insertions, 0 deletions
diff --git a/src/acpi/Makefile.inc b/src/acpi/Makefile.inc index 5e83bc5678..ffffb860c6 100644 --- a/src/acpi/Makefile.inc +++ b/src/acpi/Makefile.inc @@ -6,6 +6,7 @@ ramstage-y += acpi.c ramstage-y += acpigen.c ramstage-y += acpigen_dsm.c ramstage-y += acpigen_ps2_keybd.c +ramstage-y += acpigen_usb.c ramstage-y += device.c ramstage-y += pld.c ramstage-y += sata.c diff --git a/src/acpi/acpigen_usb.c b/src/acpi/acpigen_usb.c new file mode 100644 index 0000000000..90a9b77c60 --- /dev/null +++ b/src/acpi/acpigen_usb.c @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <acpi/acpi.h> +#include <acpi/acpi_device.h> +#include <acpi/acpigen.h> +#include <acpi/acpigen_usb.h> + +static const char *power_role_to_str(enum usb_typec_power_role power_role) +{ + switch (power_role) { + case TYPEC_POWER_ROLE_SOURCE: + return "source"; + case TYPEC_POWER_ROLE_SINK: + return "sink"; + case TYPEC_POWER_ROLE_DUAL: + return "dual"; + default: + return "unknown"; + } +} + +static const char *try_power_role_to_str(enum usb_typec_try_power_role try_power_role) +{ + switch (try_power_role) { + case TYPEC_TRY_POWER_ROLE_NONE: + /* + * This should never get returned; if there is no try-power role for a device, + * then the try-power-role field is not added to the DSD. Thus, this is just + * for completeness. + */ + return "none"; + case TYPEC_TRY_POWER_ROLE_SINK: + return "sink"; + case TYPEC_TRY_POWER_ROLE_SOURCE: + return "source"; + default: + return "unknown"; + } +} + +static const char *data_role_to_str(enum usb_typec_data_role data_role) +{ + switch (data_role) { + case TYPEC_DATA_ROLE_DFP: + return "host"; + case TYPEC_DATA_ROLE_UFP: + return "device"; + case TYPEC_DATA_ROLE_DUAL: + return "dual"; + default: + return "unknown"; + } +} + +/* Add port capabilities as DP properties */ +static void add_port_caps(struct acpi_dp *dsd, + const struct typec_connector_class_config *config) +{ + acpi_dp_add_string(dsd, "power-role", power_role_to_str(config->power_role)); + acpi_dp_add_string(dsd, "data-role", data_role_to_str(config->data_role)); + + if (config->try_power_role != TYPEC_TRY_POWER_ROLE_NONE) + acpi_dp_add_string(dsd, "try-power-role", + try_power_role_to_str(config->try_power_role)); +} + +static void add_device_ref(struct acpi_dp *dsd, + const char *prop_name, + const struct device *dev) +{ + const char *path; + char *fresh; + + if (!dev) + return; + + /* + * Unfortunately, the acpi_dp_* API doesn't write out the data immediately, thus we need + * different storage areas for all of the strings, so strdup() is used for that. It is + * safe to use strdup() here, because the strings are generated at build-time and are + * guaranteed to be NUL-terminated (they come from the devicetree). + */ + path = acpi_device_path(dev); + if (path) { + fresh = strdup(path); + if (fresh) + acpi_dp_add_reference(dsd, prop_name, fresh); + } +} + +static void add_device_references(struct acpi_dp *dsd, + const struct typec_connector_class_config *config) +{ + /* + * Add references to the USB port objects so that the consumer of this information can + * know whether the port supports USB2, USB3, and/or USB4. + */ + add_device_ref(dsd, "usb2-port", config->usb2_port); + add_device_ref(dsd, "usb3-port", config->usb3_port); + add_device_ref(dsd, "usb4-port", config->usb4_port); + + /* + * Add references to the ACPI device(s) which control the orientation, USB data role and + * data muxing. + */ + add_device_ref(dsd, "orientation-switch", config->orientation_switch); + add_device_ref(dsd, "usb-role-switch", config->usb_role_switch); + add_device_ref(dsd, "mode-switch", config->mode_switch); +} + +void acpigen_write_typec_connector(const struct typec_connector_class_config *config, + int port_number, + add_custom_dsd_property_cb add_custom_dsd_property) +{ + struct acpi_dp *dsd; + char name[5]; + + /* Create a CONx device */ + snprintf(name, sizeof(name), "CON%1X", port_number); + acpigen_write_device(name); + acpigen_write_name_integer("_ADR", port_number); + + dsd = acpi_dp_new_table("_DSD"); + + /* Write out the _DSD table */ + acpi_dp_add_integer(dsd, "port-number", port_number); + add_port_caps(dsd, config); + add_device_references(dsd, config); + + /* Allow client to add custom properties if desired */ + if (add_custom_dsd_property) + add_custom_dsd_property(dsd, port_number); + acpi_dp_write(dsd); + + acpigen_pop_len(); /* Device */ +} diff --git a/src/include/acpi/acpigen_usb.h b/src/include/acpi/acpigen_usb.h new file mode 100644 index 0000000000..efc31f349b --- /dev/null +++ b/src/include/acpi/acpigen_usb.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef ACPI_ACPIGEN_USB_H +#define ACPI_ACPIGEN_USB_H + +enum usb_typec_power_role { + TYPEC_POWER_ROLE_SOURCE, + TYPEC_POWER_ROLE_SINK, + TYPEC_POWER_ROLE_DUAL, +}; + +enum usb_typec_try_power_role { + TYPEC_TRY_POWER_ROLE_NONE, + TYPEC_TRY_POWER_ROLE_SINK, + TYPEC_TRY_POWER_ROLE_SOURCE, +}; + +enum usb_typec_data_role { + TYPEC_DATA_ROLE_DFP, + TYPEC_DATA_ROLE_UFP, + TYPEC_DATA_ROLE_DUAL, +}; + +/** + * Configuration required to write out a Type-C Connector ACPI object. + * + * @power_role: DUAL if device supports being both a source and a sink, otherwise choose + * the device's default power role + * @try_power_role: SINK if device supports Try.SNK, SOURCE if device supports Try.SRC, + * otherwise choose NONE + * @data_role: Choose DUAL if device can alternate between UFP (host) & DFP (device), + * otherwise specify UFP or DFP. + * @usb2_port: Reference to the ACPI device that represents the USB2 signals + * @usb3_port: Reference to the ACPI device that represents the USB3 signals + * @usb4_port: Reference to the ACPI device that represents the USB4 signals + * @orientation_switch: Reference to the ACPI device that controls the switching of + * the orientation/polarity for Data and SBU lines. + * @usb_role_switch: Reference to the ACPI device that can select the USB role, + * host or device, for the USB port + * @mode_switch: Reference to the ACPI device that controls routing of data lines to + * various endpoints (xHCI, DP, etc.) on the SoC. + */ +struct typec_connector_class_config { + enum usb_typec_power_role power_role; + enum usb_typec_try_power_role try_power_role; + enum usb_typec_data_role data_role; + const struct device *usb2_port; + const struct device *usb3_port; + const struct device *usb4_port; + const struct device *orientation_switch; + const struct device *usb_role_switch; + const struct device *mode_switch; +}; + +typedef void (*add_custom_dsd_property_cb)(struct acpi_dp *dsd, int port_number); + +void acpigen_write_typec_connector(const struct typec_connector_class_config *config, + int port_number, + add_custom_dsd_property_cb add_custom_dsd_property); + +#endif /* ACPI_ACPIGEN_USB_H */ |