1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 #include <acpi/acpi_device.h>
4 #include <acpi/acpi_pld.h>
5 #include <acpi/acpigen.h>
6 #include <acpi/acpigen_usb.h>
8 static const char *power_role_to_str(enum usb_typec_power_role power_role
)
11 case TYPEC_POWER_ROLE_SOURCE
:
13 case TYPEC_POWER_ROLE_SINK
:
15 case TYPEC_POWER_ROLE_DUAL
:
22 static const char *try_power_role_to_str(enum usb_typec_try_power_role try_power_role
)
24 switch (try_power_role
) {
25 case TYPEC_TRY_POWER_ROLE_NONE
:
27 * This should never get returned; if there is no try-power role for a device,
28 * then the try-power-role field is not added to the DSD. Thus, this is just
32 case TYPEC_TRY_POWER_ROLE_SINK
:
34 case TYPEC_TRY_POWER_ROLE_SOURCE
:
41 static const char *data_role_to_str(enum usb_typec_data_role data_role
)
44 case TYPEC_DATA_ROLE_DFP
:
46 case TYPEC_DATA_ROLE_UFP
:
48 case TYPEC_DATA_ROLE_DUAL
:
55 /* Add port capabilities as DP properties */
56 static void add_port_caps(struct acpi_dp
*dsd
,
57 const struct typec_connector_class_config
*config
)
59 acpi_dp_add_string(dsd
, "power-role", power_role_to_str(config
->power_role
));
60 acpi_dp_add_string(dsd
, "data-role", data_role_to_str(config
->data_role
));
62 if (config
->try_power_role
!= TYPEC_TRY_POWER_ROLE_NONE
)
63 acpi_dp_add_string(dsd
, "try-power-role",
64 try_power_role_to_str(config
->try_power_role
));
67 static void add_device_ref(struct acpi_dp
*dsd
,
68 const char *prop_name
,
69 const struct device
*dev
)
74 if (!dev
|| !dev
->enabled
)
78 * Unfortunately, the acpi_dp_* API doesn't write out the data immediately, thus we need
79 * different storage areas for all of the strings, so strdup() is used for that. It is
80 * safe to use strdup() here, because the strings are generated at build-time and are
81 * guaranteed to be NUL-terminated (they come from the devicetree).
83 path
= acpi_device_path(dev
);
87 acpi_dp_add_reference(dsd
, prop_name
, fresh
);
91 static void add_device_references(struct acpi_dp
*dsd
,
92 const struct typec_connector_class_config
*config
)
95 * Add references to the USB port objects so that the consumer of this information can
96 * know whether the port supports USB2, USB3, and/or USB4.
98 add_device_ref(dsd
, "usb2-port", config
->usb2_port
);
99 add_device_ref(dsd
, "usb3-port", config
->usb3_port
);
100 add_device_ref(dsd
, "usb4-port", config
->usb4_port
);
103 * Add references to the ACPI device(s) which control the orientation, USB data role and
106 add_device_ref(dsd
, "orientation-switch", config
->orientation_switch
);
107 add_device_ref(dsd
, "usb-role-switch", config
->usb_role_switch
);
108 add_device_ref(dsd
, "mode-switch", config
->mode_switch
);
109 add_device_ref(dsd
, "retimer-switch", config
->retimer_switch
);
112 void acpigen_write_typec_connector(const struct typec_connector_class_config
*config
,
114 add_custom_dsd_property_cb add_custom_dsd_property
)
119 /* Create a CONx device */
120 snprintf(name
, sizeof(name
), "CON%1X", port_number
);
121 acpigen_write_device(name
);
122 acpigen_write_name_integer("_ADR", port_number
);
124 dsd
= acpi_dp_new_table("_DSD");
126 /* Write out the _DSD table */
127 acpi_dp_add_integer(dsd
, "port-number", port_number
);
128 add_port_caps(dsd
, config
);
129 add_device_references(dsd
, config
);
131 /* Allow client to add custom properties if desired */
132 if (add_custom_dsd_property
)
133 add_custom_dsd_property(dsd
, port_number
);
137 acpigen_write_pld(config
->pld
);
139 acpigen_pop_len(); /* Device */