blob: b29c32f3e1961ba4308ba041f5a9816de0123d9b [file] [log] [blame]
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -07001/*
2 * This file is part of the coreboot project.
3 *
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -07004 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
Furquan Shaikh76cedd22020-05-02 10:24:23 -07008#include <acpi/acpi.h>
9#include <acpi/acpi_device.h>
10#include <acpi/acpigen.h>
11#include <acpi/acpigen_ps2_keybd.h>
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -070012#include <console/console.h>
13#include <drivers/usb/acpi/chip.h>
14#include <stdlib.h>
15
16#include "chip.h"
17#include "ec.h"
18#include "ec_commands.h"
19
Karthikeyan Ramasubramaniana95907b2020-04-13 18:03:29 -060020#define GOOGLE_CHROMEEC_USBC_DEVICE_HID "GOOG0014"
21#define GOOGLE_CHROMEEC_USBC_DEVICE_NAME "USBC"
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -070022
23const char *google_chromeec_acpi_name(const struct device *dev)
24{
Furquan Shaikheec30f72020-04-20 16:38:21 -070025 /*
26 * Chrome EC device (CREC - GOOG0004) is really a child of EC device (EC - PNP0C09) in
27 * ACPI tables. However, in coreboot device tree, there is no separate chip/device for
28 * EC0. Thus, Chrome EC device needs to return "EC0.CREC" as the ACPI name so that the
29 * callers can get the correct acpi device path/scope for this device.
30 *
31 * If we ever enable a separate driver for generating AML for EC0 device, then this
32 * function needs to be updated to return "CREC".
33 */
34 return "EC0.CREC";
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -070035}
36
37static const char *power_role_to_str(enum ec_pd_power_role_caps power_role)
38{
39 switch (power_role) {
40 case EC_PD_POWER_ROLE_SOURCE:
41 return "source";
42 case EC_PD_POWER_ROLE_SINK:
43 return "sink";
44 case EC_PD_POWER_ROLE_DUAL:
45 return "dual";
46 default:
47 return "unknown";
48 }
49}
50
51static const char *try_power_role_to_str(enum ec_pd_try_power_role_caps try_power_role)
52{
53 switch (try_power_role) {
54 case EC_PD_TRY_POWER_ROLE_NONE:
55 /*
56 * This should never get returned; if there is no try-power role for a device,
57 * then the try-power-role field is not added to the DSD. Thus, this is just
58 * for completeness.
59 */
60 return "none";
61 case EC_PD_TRY_POWER_ROLE_SINK:
62 return "sink";
63 case EC_PD_TRY_POWER_ROLE_SOURCE:
64 return "source";
65 default:
66 return "unknown";
67 }
68}
69
70static const char *data_role_to_str(enum ec_pd_data_role_caps data_role)
71{
72 switch (data_role) {
73 case EC_PD_DATA_ROLE_DFP:
74 return "host";
75 case EC_PD_DATA_ROLE_UFP:
76 return "device";
77 case EC_PD_DATA_ROLE_DUAL:
78 return "dual";
79 default:
80 return "unknown";
81 }
82}
83
84/*
85 * Apparently these are supposed to be uppercase, in contrast to the other
86 * lowercase fields.
87 */
88static const char *port_location_to_str(enum ec_pd_port_location port_location)
89{
90 switch (port_location) {
91 case EC_PD_PORT_LOCATION_LEFT:
92 return "LEFT";
93 case EC_PD_PORT_LOCATION_RIGHT:
94 return "RIGHT";
95 case EC_PD_PORT_LOCATION_BACK:
96 return "BACK";
97 case EC_PD_PORT_LOCATION_FRONT:
98 return "FRONT";
99 case EC_PD_PORT_LOCATION_LEFT_FRONT:
100 return "LEFT_FRONT";
101 case EC_PD_PORT_LOCATION_LEFT_BACK:
102 return "LEFT_BACK";
103 case EC_PD_PORT_LOCATION_RIGHT_FRONT:
104 return "RIGHT_FRONT";
105 case EC_PD_PORT_LOCATION_RIGHT_BACK:
106 return "RIGHT_BACK";
107 case EC_PD_PORT_LOCATION_BACK_LEFT:
108 return "BACK_LEFT";
109 case EC_PD_PORT_LOCATION_BACK_RIGHT:
110 return "BACK_RIGHT";
111 case EC_PD_PORT_LOCATION_UNKNOWN: /* intentional fallthrough */
112 default:
113 return "UNKNOWN";
114 }
115}
116
117/* Add port capabilities as DP properties */
118static void add_port_caps(struct acpi_dp *dsd, const struct usb_pd_port_caps *port_caps)
119{
120 acpi_dp_add_string(dsd, "power-role", power_role_to_str(port_caps->power_role_cap));
121
122 if (port_caps->try_power_role_cap != EC_PD_TRY_POWER_ROLE_NONE)
123 acpi_dp_add_string(dsd, "try-power-role",
124 try_power_role_to_str(port_caps->try_power_role_cap));
125
126 acpi_dp_add_string(dsd, "data-role", data_role_to_str(port_caps->data_role_cap));
127 acpi_dp_add_string(dsd, "port-location", port_location_to_str(
128 port_caps->port_location));
129}
130
131/*
132 * Helper for fill_ssdt_generator. This adds references to the USB
133 * port objects so that the consumer of this information can know
134 * whether the port supports USB2 and/or USB3.
135 */
136static void add_usb_port_references(struct acpi_dp *dsd, int port_number)
137{
138 static const char usb2_port[] = "usb2-port";
139 static const char usb3_port[] = "usb3-port";
140 struct device *port = NULL;
141 const char *path;
142 const char *usb_port_type;
143 struct drivers_usb_acpi_config *config;
144
145 /*
146 * Unfortunately, the acpi_dp_* API doesn't write out the data immediately, thus we need
147 * different storage areas for all of the strings, so strdup() is used for that. It is
148 * safe to use strdup() here, because the strings are generated at build-time and are
149 * guaranteed to be NUL-terminated (they come from the devicetree).
150 */
151 while ((port = dev_find_path(port, DEVICE_PATH_USB)) != NULL) {
152 if (!port->enabled || port->path.type != DEVICE_PATH_USB)
153 continue;
154
155 /* Looking for USB 2 & 3 port devices only */
156 if (port->path.usb.port_type == 2)
157 usb_port_type = usb2_port;
158 else if (port->path.usb.port_type == 3)
159 usb_port_type = usb3_port;
160 else
161 continue;
162
163 config = port->chip_info;
164
165 /*
166 * Look at only USB Type-C ports, making sure they match the
167 * port number we're looking for (the 'token' field in 'group').
168 * Also note that 'port_number' is 0-based, whereas the 'token'
169 * field is 1-based.
170 */
171 if ((config->type != UPC_TYPE_C_USB2_ONLY) &&
172 (config->type != UPC_TYPE_C_USB2_SS_SWITCH) &&
173 (config->type != UPC_TYPE_C_USB2_SS))
174 continue;
175
176 if (config->group.token != (port_number + 1))
177 continue;
178
179 path = acpi_device_path(port);
180 if (path) {
181 path = strdup(path);
182 if (!path)
183 continue;
184
185 acpi_dp_add_reference(dsd, usb_port_type, path);
186 }
187 }
188}
189
Furquan Shaikh7536a392020-04-24 21:59:21 -0700190static void fill_ssdt_typec_device(const struct device *dev)
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -0700191{
192 struct usb_pd_port_caps port_caps;
193 char con_name[] = "CONx";
194 struct acpi_dp *dsd;
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -0700195 int rv;
Rajat Jain962c7882020-04-01 17:24:28 -0700196 int i, num_ports;
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -0700197
Rajat Jain962c7882020-04-01 17:24:28 -0700198 if (google_chromeec_get_num_pd_ports(&num_ports))
199 return;
200
Furquan Shaikheec30f72020-04-20 16:38:21 -0700201 acpigen_write_scope(acpi_device_path(dev));
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -0700202 acpigen_write_device(GOOGLE_CHROMEEC_USBC_DEVICE_NAME);
203 acpigen_write_name_string("_HID", GOOGLE_CHROMEEC_USBC_DEVICE_HID);
204 acpigen_write_name_string("_DDN", "ChromeOS EC Embedded Controller "
205 "USB Type-C Control");
206
207 for (i = 0; i < num_ports; ++i) {
208 rv = google_chromeec_get_pd_port_caps(i, &port_caps);
209 if (rv)
210 continue;
211
212 con_name[3] = (char)i + '0';
213 acpigen_write_device(con_name);
214 acpigen_write_name_integer("_ADR", i);
215
216 /* _DSD, Device-Specific Data */
217 dsd = acpi_dp_new_table("_DSD");
218
219 acpi_dp_add_integer(dsd, "port-number", i);
220 add_port_caps(dsd, &port_caps);
221 add_usb_port_references(dsd, i);
222
223 acpi_dp_write(dsd);
224 acpigen_pop_len(); /* Device CONx */
225 }
226
227 acpigen_pop_len(); /* Device GOOGLE_CHROMEEC_USBC_DEVICE_NAME */
Rajat Jain962c7882020-04-01 17:24:28 -0700228 acpigen_pop_len(); /* Scope */
229}
230
231static const enum ps2_action_key ps2_enum_val[] = {
232 [TK_ABSENT] = PS2_KEY_ABSENT,
233 [TK_BACK] = PS2_KEY_BACK,
234 [TK_FORWARD] = PS2_KEY_FORWARD,
235 [TK_REFRESH] = PS2_KEY_REFRESH,
236 [TK_FULLSCREEN] = PS2_KEY_FULLSCREEN,
237 [TK_OVERVIEW] = PS2_KEY_OVERVIEW,
238 [TK_BRIGHTNESS_DOWN] = PS2_KEY_BRIGHTNESS_DOWN,
239 [TK_BRIGHTNESS_UP] = PS2_KEY_BRIGHTNESS_UP,
240 [TK_VOL_MUTE] = PS2_KEY_VOL_MUTE,
241 [TK_VOL_DOWN] = PS2_KEY_VOL_DOWN,
242 [TK_VOL_UP] = PS2_KEY_VOL_UP,
243 [TK_SNAPSHOT] = PS2_KEY_SNAPSHOT,
244 [TK_PRIVACY_SCRN_TOGGLE] = PS2_KEY_PRIVACY_SCRN_TOGGLE,
245 [TK_KBD_BKLIGHT_DOWN] = PS2_KEY_KBD_BKLIGHT_DOWN,
246 [TK_KBD_BKLIGHT_UP] = PS2_KEY_KBD_BKLIGHT_UP,
247 [TK_PLAY_PAUSE] = PS2_KEY_PLAY_PAUSE,
248 [TK_NEXT_TRACK] = PS2_KEY_NEXT_TRACK,
249 [TK_PREV_TRACK] = PS2_KEY_PREV_TRACK,
250};
251
Furquan Shaikh7536a392020-04-24 21:59:21 -0700252static void fill_ssdt_ps2_keyboard(const struct device *dev)
Rajat Jain962c7882020-04-01 17:24:28 -0700253{
254 uint8_t i;
255 struct ec_response_keybd_config keybd = {};
256 enum ps2_action_key ps2_action_keys[MAX_TOP_ROW_KEYS] = {};
257
258 if (google_chromeec_get_keybd_config(&keybd) ||
259 !keybd.num_top_row_keys ||
260 keybd.num_top_row_keys > MAX_TOP_ROW_KEYS) {
261 printk(BIOS_ERR, "PS2K: Bad resp from EC. Vivaldi disabled!\n");
262 return;
263 }
264
265 /* Convert enum action_key values to enum ps2_action_key values */
266 for (i = 0; i < keybd.num_top_row_keys; i++)
267 ps2_action_keys[i] = ps2_enum_val[keybd.action_keys[i]];
268
269 acpigen_ps2_keyboard_dsd("_SB.PCI0.PS2K", keybd.num_top_row_keys,
270 ps2_action_keys,
271 !!(keybd.capabilities & KEYBD_CAP_FUNCTION_KEYS),
272 !!(keybd.capabilities & KEYBD_CAP_NUMERIC_KEYPAD),
273 !!(keybd.capabilities & KEYBD_CAP_SCRNLOCK_KEY));
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -0700274}
275
Furquan Shaikh7536a392020-04-24 21:59:21 -0700276void google_chromeec_fill_ssdt_generator(const struct device *dev)
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -0700277{
Rajat Jain962c7882020-04-01 17:24:28 -0700278 if (!dev->enabled)
Matt DeVillier70ea3b92020-03-18 23:45:32 -0500279 return;
280
Rajat Jain962c7882020-04-01 17:24:28 -0700281 fill_ssdt_typec_device(dev);
282 fill_ssdt_ps2_keyboard(dev);
Tim Wawrzynczakeb3cd852020-01-22 16:52:13 -0700283}