blob: 221a215f0d03c45bcaa1fef31815a03db41a0b67 [file] [log] [blame]
Tim Wawrzynczak92d96e82020-05-19 12:38:43 -06001/* SPDX-License-Identifier: GPL-2.0-or-later */
2
Tim Wawrzynczak92d96e82020-05-19 12:38:43 -06003#include <acpi/acpi_device.h>
Won Chung667471b2021-11-15 21:19:51 +00004#include <acpi/acpi_pld.h>
Tim Wawrzynczak92d96e82020-05-19 12:38:43 -06005#include <acpi/acpigen.h>
6#include <acpi/acpigen_usb.h>
Felix Held4a9ed702023-12-05 22:09:14 +01007#include <device/device.h>
Tim Wawrzynczak92d96e82020-05-19 12:38:43 -06008
9static const char *power_role_to_str(enum usb_typec_power_role power_role)
10{
11 switch (power_role) {
12 case TYPEC_POWER_ROLE_SOURCE:
13 return "source";
14 case TYPEC_POWER_ROLE_SINK:
15 return "sink";
16 case TYPEC_POWER_ROLE_DUAL:
17 return "dual";
18 default:
19 return "unknown";
20 }
21}
22
23static const char *try_power_role_to_str(enum usb_typec_try_power_role try_power_role)
24{
25 switch (try_power_role) {
26 case TYPEC_TRY_POWER_ROLE_NONE:
27 /*
28 * This should never get returned; if there is no try-power role for a device,
29 * then the try-power-role field is not added to the DSD. Thus, this is just
30 * for completeness.
31 */
32 return "none";
33 case TYPEC_TRY_POWER_ROLE_SINK:
34 return "sink";
35 case TYPEC_TRY_POWER_ROLE_SOURCE:
36 return "source";
37 default:
38 return "unknown";
39 }
40}
41
42static const char *data_role_to_str(enum usb_typec_data_role data_role)
43{
44 switch (data_role) {
45 case TYPEC_DATA_ROLE_DFP:
46 return "host";
47 case TYPEC_DATA_ROLE_UFP:
48 return "device";
49 case TYPEC_DATA_ROLE_DUAL:
50 return "dual";
51 default:
52 return "unknown";
53 }
54}
55
56/* Add port capabilities as DP properties */
57static void add_port_caps(struct acpi_dp *dsd,
58 const struct typec_connector_class_config *config)
59{
60 acpi_dp_add_string(dsd, "power-role", power_role_to_str(config->power_role));
61 acpi_dp_add_string(dsd, "data-role", data_role_to_str(config->data_role));
62
63 if (config->try_power_role != TYPEC_TRY_POWER_ROLE_NONE)
64 acpi_dp_add_string(dsd, "try-power-role",
65 try_power_role_to_str(config->try_power_role));
66}
67
68static void add_device_ref(struct acpi_dp *dsd,
69 const char *prop_name,
70 const struct device *dev)
71{
72 const char *path;
73 char *fresh;
74
Tim Wawrzynczak9eabeb52020-09-30 13:18:20 -060075 if (!dev || !dev->enabled)
Tim Wawrzynczak92d96e82020-05-19 12:38:43 -060076 return;
77
78 /*
79 * Unfortunately, the acpi_dp_* API doesn't write out the data immediately, thus we need
80 * different storage areas for all of the strings, so strdup() is used for that. It is
81 * safe to use strdup() here, because the strings are generated at build-time and are
82 * guaranteed to be NUL-terminated (they come from the devicetree).
83 */
84 path = acpi_device_path(dev);
85 if (path) {
86 fresh = strdup(path);
87 if (fresh)
88 acpi_dp_add_reference(dsd, prop_name, fresh);
89 }
90}
91
92static void add_device_references(struct acpi_dp *dsd,
93 const struct typec_connector_class_config *config)
94{
95 /*
96 * Add references to the USB port objects so that the consumer of this information can
97 * know whether the port supports USB2, USB3, and/or USB4.
98 */
99 add_device_ref(dsd, "usb2-port", config->usb2_port);
100 add_device_ref(dsd, "usb3-port", config->usb3_port);
101 add_device_ref(dsd, "usb4-port", config->usb4_port);
102
103 /*
104 * Add references to the ACPI device(s) which control the orientation, USB data role and
105 * data muxing.
106 */
107 add_device_ref(dsd, "orientation-switch", config->orientation_switch);
108 add_device_ref(dsd, "usb-role-switch", config->usb_role_switch);
109 add_device_ref(dsd, "mode-switch", config->mode_switch);
Prashant Malanida6e9a02022-04-21 18:01:40 +0000110 add_device_ref(dsd, "retimer-switch", config->retimer_switch);
Tim Wawrzynczak92d96e82020-05-19 12:38:43 -0600111}
112
113void acpigen_write_typec_connector(const struct typec_connector_class_config *config,
114 int port_number,
115 add_custom_dsd_property_cb add_custom_dsd_property)
116{
117 struct acpi_dp *dsd;
118 char name[5];
119
120 /* Create a CONx device */
121 snprintf(name, sizeof(name), "CON%1X", port_number);
122 acpigen_write_device(name);
123 acpigen_write_name_integer("_ADR", port_number);
124
125 dsd = acpi_dp_new_table("_DSD");
126
127 /* Write out the _DSD table */
128 acpi_dp_add_integer(dsd, "port-number", port_number);
129 add_port_caps(dsd, config);
130 add_device_references(dsd, config);
131
132 /* Allow client to add custom properties if desired */
133 if (add_custom_dsd_property)
134 add_custom_dsd_property(dsd, port_number);
135 acpi_dp_write(dsd);
136
Won Chung667471b2021-11-15 21:19:51 +0000137 /* Add PLD */
138 acpigen_write_pld(config->pld);
139
Tim Wawrzynczak92d96e82020-05-19 12:38:43 -0600140 acpigen_pop_len(); /* Device */
141}