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