aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/misc/Kconfig26
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/ucsi.c478
-rw-r--r--drivers/usb/misc/ucsi.h215
-rw-r--r--drivers/usb/typec/ucsi/Kconfig16
-rw-r--r--drivers/usb/typec/ucsi/Makefile2
-rw-r--r--drivers/usb/typec/ucsi/ucsi_acpi.c158
7 files changed, 176 insertions, 720 deletions
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 1d1d70d62a19..0f9f25db9163 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -275,29 +275,3 @@ config USB_CHAOSKEY
275 275
276 To compile this driver as a module, choose M here: the 276 To compile this driver as a module, choose M here: the
277 module will be called chaoskey. 277 module will be called chaoskey.
278
279config UCSI
280 tristate "USB Type-C Connector System Software Interface driver"
281 depends on ACPI
282 help
283 UCSI driver is meant to be used as a convenience tool for desktop and
284 server systems that are not equipped to handle USB in device mode. It
285 will always select USB host role for the USB Type-C ports on systems
286 that provide UCSI interface.
287
288 USB Type-C Connector System Software Interface (UCSI) is a
289 specification for an interface that allows the Operating System to
290 control the USB Type-C ports on a system. Things the need controlling
291 include the USB Data Role (host or device), and when USB Power
292 Delivery is supported, the Power Role (source or sink). With USB
293 Type-C connectors, when two dual role capable devices are attached
294 together, the data role is selected randomly. Therefore it is
295 important to give the OS a way to select the role. Otherwise the user
296 would have to unplug and replug in order in order to attempt to swap
297 the data and power roles.
298
299 The UCSI specification can be downloaded from:
300 http://www.intel.com/content/www/us/en/io/universal-serial-bus/usb-type-c-ucsi-spec.html
301
302 To compile the driver as a module, choose M here: the module will be
303 called ucsi.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index f6ac6c99a6e6..7fdb45fc976f 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -27,7 +27,6 @@ obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o
27obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o 27obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o
28obj-$(CONFIG_USB_HSIC_USB4604) += usb4604.o 28obj-$(CONFIG_USB_HSIC_USB4604) += usb4604.o
29obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o 29obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o
30obj-$(CONFIG_UCSI) += ucsi.o
31 30
32obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ 31obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
33obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o 32obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o
diff --git a/drivers/usb/misc/ucsi.c b/drivers/usb/misc/ucsi.c
deleted file mode 100644
index 07397bddefa3..000000000000
--- a/drivers/usb/misc/ucsi.c
+++ /dev/null
@@ -1,478 +0,0 @@
1/*
2 * USB Type-C Connector System Software Interface driver
3 *
4 * Copyright (C) 2016, Intel Corporation
5 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/platform_device.h>
13#include <linux/module.h>
14#include <linux/delay.h>
15#include <linux/acpi.h>
16
17#include "ucsi.h"
18
19/* Double the time defined by MIN_TIME_TO_RESPOND_WITH_BUSY */
20#define UCSI_TIMEOUT_MS 20
21
22enum ucsi_status {
23 UCSI_IDLE = 0,
24 UCSI_BUSY,
25 UCSI_ERROR,
26};
27
28struct ucsi_connector {
29 int num;
30 struct ucsi *ucsi;
31 struct work_struct work;
32 struct ucsi_connector_capability cap;
33};
34
35struct ucsi {
36 struct device *dev;
37 struct ucsi_data __iomem *data;
38
39 enum ucsi_status status;
40 struct completion complete;
41 struct ucsi_capability cap;
42 struct ucsi_connector *connector;
43
44 /* device lock */
45 spinlock_t dev_lock;
46
47 /* PPM Communication lock */
48 struct mutex ppm_lock;
49
50 /* PPM communication flags */
51 unsigned long flags;
52#define EVENT_PENDING 0
53#define COMMAND_PENDING 1
54};
55
56static int ucsi_acpi_cmd(struct ucsi *ucsi, struct ucsi_control *ctrl)
57{
58 uuid_le uuid = UUID_LE(0x6f8398c2, 0x7ca4, 0x11e4,
59 0xad, 0x36, 0x63, 0x10, 0x42, 0xb5, 0x00, 0x8f);
60 union acpi_object *obj;
61
62 ucsi->data->ctrl.raw_cmd = ctrl->raw_cmd;
63
64 obj = acpi_evaluate_dsm(ACPI_HANDLE(ucsi->dev), uuid.b, 1, 1, NULL);
65 if (!obj) {
66 dev_err(ucsi->dev, "%s: failed to evaluate _DSM\n", __func__);
67 return -EIO;
68 }
69
70 ACPI_FREE(obj);
71 return 0;
72}
73
74static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
75{
76 struct ucsi *ucsi = data;
77 struct ucsi_cci *cci;
78
79 spin_lock(&ucsi->dev_lock);
80
81 ucsi->status = UCSI_IDLE;
82 cci = &ucsi->data->cci;
83
84 /*
85 * REVISIT: This is not documented behavior, but all known PPMs ACK
86 * asynchronous events by sending notification with cleared CCI.
87 */
88 if (!ucsi->data->raw_cci) {
89 if (test_bit(EVENT_PENDING, &ucsi->flags))
90 complete(&ucsi->complete);
91 else
92 dev_WARN(ucsi->dev, "spurious notification\n");
93 goto out_unlock;
94 }
95
96 if (test_bit(COMMAND_PENDING, &ucsi->flags)) {
97 if (cci->busy) {
98 ucsi->status = UCSI_BUSY;
99 complete(&ucsi->complete);
100
101 goto out_unlock;
102 } else if (cci->ack_complete || cci->cmd_complete) {
103 /* Error Indication is only valid with commands */
104 if (cci->error && cci->cmd_complete)
105 ucsi->status = UCSI_ERROR;
106
107 ucsi->data->ctrl.raw_cmd = 0;
108 complete(&ucsi->complete);
109 }
110 }
111
112 if (cci->connector_change) {
113 struct ucsi_connector *con;
114
115 /*
116 * This is workaround for buggy PPMs that create asynchronous
117 * event notifications before OPM has enabled them.
118 */
119 if (!ucsi->connector)
120 goto out_unlock;
121
122 con = ucsi->connector + (cci->connector_change - 1);
123
124 /*
125 * PPM will not clear the connector specific bit in Connector
126 * Change Indication field of CCI until the driver has ACK it,
127 * and the driver can not ACK it before it has been processed.
128 * The PPM will not generate new events before the first has
129 * been acknowledged, even if they are for an other connector.
130 * So only one event at a time.
131 */
132 if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
133 schedule_work(&con->work);
134 }
135out_unlock:
136 spin_unlock(&ucsi->dev_lock);
137}
138
139static int ucsi_ack(struct ucsi *ucsi, u8 cmd)
140{
141 struct ucsi_control ctrl;
142 int ret;
143
144 ctrl.cmd.cmd = UCSI_ACK_CC_CI;
145 ctrl.cmd.length = 0;
146 ctrl.cmd.data = cmd;
147 ret = ucsi_acpi_cmd(ucsi, &ctrl);
148 if (ret)
149 return ret;
150
151 /* Waiting for ACK also with ACK CMD for now */
152 ret = wait_for_completion_timeout(&ucsi->complete,
153 msecs_to_jiffies(UCSI_TIMEOUT_MS));
154 if (!ret)
155 return -ETIMEDOUT;
156 return 0;
157}
158
159static int ucsi_run_cmd(struct ucsi *ucsi, struct ucsi_control *ctrl,
160 void *data, size_t size)
161{
162 u16 err_value = 0;
163 int ret;
164
165 set_bit(COMMAND_PENDING, &ucsi->flags);
166
167 ret = ucsi_acpi_cmd(ucsi, ctrl);
168 if (ret)
169 goto err_clear_flag;
170
171 ret = wait_for_completion_timeout(&ucsi->complete,
172 msecs_to_jiffies(UCSI_TIMEOUT_MS));
173 if (!ret) {
174 ret = -ETIMEDOUT;
175 goto err_clear_flag;
176 }
177
178 switch (ucsi->status) {
179 case UCSI_IDLE:
180 if (data)
181 memcpy(data, ucsi->data->message_in, size);
182
183 ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
184 break;
185 case UCSI_BUSY:
186 /* The caller decides whether to cancel or not */
187 ret = -EBUSY;
188 goto err_clear_flag;
189 case UCSI_ERROR:
190 ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
191 if (ret)
192 goto err_clear_flag;
193
194 ctrl->cmd.cmd = UCSI_GET_ERROR_STATUS;
195 ctrl->cmd.length = 0;
196 ctrl->cmd.data = 0;
197 ret = ucsi_acpi_cmd(ucsi, ctrl);
198 if (ret)
199 goto err_clear_flag;
200
201 ret = wait_for_completion_timeout(&ucsi->complete,
202 msecs_to_jiffies(UCSI_TIMEOUT_MS));
203 if (!ret) {
204 ret = -ETIMEDOUT;
205 goto err_clear_flag;
206 }
207
208 memcpy(&err_value, ucsi->data->message_in, sizeof(err_value));
209
210 /* Something has really gone wrong */
211 if (WARN_ON(ucsi->status == UCSI_ERROR)) {
212 ret = -ENODEV;
213 goto err_clear_flag;
214 }
215
216 ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
217 if (ret)
218 goto err_clear_flag;
219
220 switch (err_value) {
221 case UCSI_ERROR_INCOMPATIBLE_PARTNER:
222 ret = -EOPNOTSUPP;
223 break;
224 case UCSI_ERROR_CC_COMMUNICATION_ERR:
225 ret = -ECOMM;
226 break;
227 case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL:
228 ret = -EIO;
229 break;
230 case UCSI_ERROR_DEAD_BATTERY:
231 dev_warn(ucsi->dev, "Dead battery condition!\n");
232 ret = -EPERM;
233 break;
234 /* The following mean a bug in this driver */
235 case UCSI_ERROR_INVALID_CON_NUM:
236 case UCSI_ERROR_UNREGONIZED_CMD:
237 case UCSI_ERROR_INVALID_CMD_ARGUMENT:
238 default:
239 dev_warn(ucsi->dev,
240 "%s: possible UCSI driver bug - error %hu\n",
241 __func__, err_value);
242 ret = -EINVAL;
243 break;
244 }
245 break;
246 }
247 ctrl->raw_cmd = 0;
248err_clear_flag:
249 clear_bit(COMMAND_PENDING, &ucsi->flags);
250 return ret;
251}
252
253static void ucsi_connector_change(struct work_struct *work)
254{
255 struct ucsi_connector *con = container_of(work, struct ucsi_connector,
256 work);
257 struct ucsi_connector_status constat;
258 struct ucsi *ucsi = con->ucsi;
259 struct ucsi_control ctrl;
260 int ret;
261
262 mutex_lock(&ucsi->ppm_lock);
263
264 ctrl.cmd.cmd = UCSI_GET_CONNECTOR_STATUS;
265 ctrl.cmd.length = 0;
266 ctrl.cmd.data = con->num;
267 ret = ucsi_run_cmd(con->ucsi, &ctrl, &constat, sizeof(constat));
268 if (ret) {
269 dev_err(ucsi->dev, "%s: failed to read connector status (%d)\n",
270 __func__, ret);
271 goto out_ack_event;
272 }
273
274 /* Ignoring disconnections and Alternate Modes */
275 if (!constat.connected || !(constat.change &
276 (UCSI_CONSTAT_PARTNER_CHANGE | UCSI_CONSTAT_CONNECT_CHANGE)) ||
277 constat.partner_flags & UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE)
278 goto out_ack_event;
279
280 /* If the partner got USB Host role, attempting swap */
281 if (constat.partner_type & UCSI_CONSTAT_PARTNER_TYPE_DFP) {
282 ctrl.uor.cmd = UCSI_SET_UOR;
283 ctrl.uor.con_num = con->num;
284 ctrl.uor.role = UCSI_UOR_ROLE_DFP;
285
286 ret = ucsi_run_cmd(con->ucsi, &ctrl, NULL, 0);
287 if (ret)
288 dev_err(ucsi->dev, "%s: failed to swap role (%d)\n",
289 __func__, ret);
290 }
291out_ack_event:
292 ucsi_ack(ucsi, UCSI_ACK_EVENT);
293 clear_bit(EVENT_PENDING, &ucsi->flags);
294 mutex_unlock(&ucsi->ppm_lock);
295}
296
297static int ucsi_reset_ppm(struct ucsi *ucsi)
298{
299 int timeout = UCSI_TIMEOUT_MS;
300 struct ucsi_control ctrl;
301 int ret;
302
303 memset(&ctrl, 0, sizeof(ctrl));
304 ctrl.cmd.cmd = UCSI_PPM_RESET;
305 ret = ucsi_acpi_cmd(ucsi, &ctrl);
306 if (ret)
307 return ret;
308
309 /* There is no quarantee the PPM will ever set the RESET_COMPLETE bit */
310 while (!ucsi->data->cci.reset_complete && timeout--)
311 usleep_range(1000, 2000);
312 return 0;
313}
314
315static int ucsi_init(struct ucsi *ucsi)
316{
317 struct ucsi_connector *con;
318 struct ucsi_control ctrl;
319 int ret;
320 int i;
321
322 init_completion(&ucsi->complete);
323 spin_lock_init(&ucsi->dev_lock);
324 mutex_init(&ucsi->ppm_lock);
325
326 /* Reset the PPM */
327 ret = ucsi_reset_ppm(ucsi);
328 if (ret)
329 return ret;
330
331 /*
332 * REVISIT: Executing second reset to WA an issue seen on some of the
333 * Broxton based platforms, where the first reset puts the PPM into a
334 * state where it's unable to recognise some of the commands.
335 */
336 ret = ucsi_reset_ppm(ucsi);
337 if (ret)
338 return ret;
339
340 mutex_lock(&ucsi->ppm_lock);
341
342 /* Enable basic notifications */
343 ctrl.cmd.cmd = UCSI_SET_NOTIFICATION_ENABLE;
344 ctrl.cmd.length = 0;
345 ctrl.cmd.data = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
346 ret = ucsi_run_cmd(ucsi, &ctrl, NULL, 0);
347 if (ret)
348 goto err_reset;
349
350 /* Get PPM capabilities */
351 ctrl.cmd.cmd = UCSI_GET_CAPABILITY;
352 ret = ucsi_run_cmd(ucsi, &ctrl, &ucsi->cap, sizeof(ucsi->cap));
353 if (ret)
354 goto err_reset;
355
356 if (!ucsi->cap.num_connectors) {
357 ret = -ENODEV;
358 goto err_reset;
359 }
360
361 ucsi->connector = devm_kcalloc(ucsi->dev, ucsi->cap.num_connectors,
362 sizeof(*ucsi->connector), GFP_KERNEL);
363 if (!ucsi->connector) {
364 ret = -ENOMEM;
365 goto err_reset;
366 }
367
368 for (i = 1, con = ucsi->connector; i < ucsi->cap.num_connectors + 1;
369 i++, con++) {
370 /* Get connector capability */
371 ctrl.cmd.cmd = UCSI_GET_CONNECTOR_CAPABILITY;
372 ctrl.cmd.data = i;
373 ret = ucsi_run_cmd(ucsi, &ctrl, &con->cap, sizeof(con->cap));
374 if (ret)
375 goto err_reset;
376
377 con->num = i;
378 con->ucsi = ucsi;
379 INIT_WORK(&con->work, ucsi_connector_change);
380 }
381
382 /* Enable all notifications */
383 ctrl.cmd.cmd = UCSI_SET_NOTIFICATION_ENABLE;
384 ctrl.cmd.data = UCSI_ENABLE_NTFY_ALL;
385 ret = ucsi_run_cmd(ucsi, &ctrl, NULL, 0);
386 if (ret < 0)
387 goto err_reset;
388
389 mutex_unlock(&ucsi->ppm_lock);
390 return 0;
391err_reset:
392 ucsi_reset_ppm(ucsi);
393 mutex_unlock(&ucsi->ppm_lock);
394 return ret;
395}
396
397static int ucsi_acpi_probe(struct platform_device *pdev)
398{
399 struct resource *res;
400 acpi_status status;
401 struct ucsi *ucsi;
402 int ret;
403
404 ucsi = devm_kzalloc(&pdev->dev, sizeof(*ucsi), GFP_KERNEL);
405 if (!ucsi)
406 return -ENOMEM;
407
408 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
409 if (!res) {
410 dev_err(&pdev->dev, "missing memory resource\n");
411 return -ENODEV;
412 }
413
414 /*
415 * NOTE: ACPI has claimed the memory region as it's also an Operation
416 * Region. It's not possible to request it in the driver.
417 */
418 ucsi->data = devm_ioremap(&pdev->dev, res->start, resource_size(res));
419 if (!ucsi->data)
420 return -ENOMEM;
421
422 ucsi->dev = &pdev->dev;
423
424 status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
425 ACPI_ALL_NOTIFY,
426 ucsi_acpi_notify, ucsi);
427 if (ACPI_FAILURE(status))
428 return -ENODEV;
429
430 ret = ucsi_init(ucsi);
431 if (ret) {
432 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
433 ACPI_ALL_NOTIFY,
434 ucsi_acpi_notify);
435 return ret;
436 }
437
438 platform_set_drvdata(pdev, ucsi);
439 return 0;
440}
441
442static int ucsi_acpi_remove(struct platform_device *pdev)
443{
444 struct ucsi *ucsi = platform_get_drvdata(pdev);
445
446 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
447 ACPI_ALL_NOTIFY, ucsi_acpi_notify);
448
449 /* Make sure there are no events in the middle of being processed */
450 if (wait_on_bit_timeout(&ucsi->flags, EVENT_PENDING,
451 TASK_UNINTERRUPTIBLE,
452 msecs_to_jiffies(UCSI_TIMEOUT_MS)))
453 dev_WARN(ucsi->dev, "%s: Events still pending\n", __func__);
454
455 ucsi_reset_ppm(ucsi);
456 return 0;
457}
458
459static const struct acpi_device_id ucsi_acpi_match[] = {
460 { "PNP0CA0", 0 },
461 { },
462};
463MODULE_DEVICE_TABLE(acpi, ucsi_acpi_match);
464
465static struct platform_driver ucsi_acpi_platform_driver = {
466 .driver = {
467 .name = "ucsi_acpi",
468 .acpi_match_table = ACPI_PTR(ucsi_acpi_match),
469 },
470 .probe = ucsi_acpi_probe,
471 .remove = ucsi_acpi_remove,
472};
473
474module_platform_driver(ucsi_acpi_platform_driver);
475
476MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
477MODULE_LICENSE("GPL v2");
478MODULE_DESCRIPTION("USB Type-C System Software Interface (UCSI) driver");
diff --git a/drivers/usb/misc/ucsi.h b/drivers/usb/misc/ucsi.h
deleted file mode 100644
index 6dd11d1fe225..000000000000
--- a/drivers/usb/misc/ucsi.h
+++ /dev/null
@@ -1,215 +0,0 @@
1
2#include <linux/types.h>
3
4/* -------------------------------------------------------------------------- */
5
6/* Command Status and Connector Change Indication (CCI) data structure */
7struct ucsi_cci {
8 unsigned int RESERVED1:1;
9 unsigned int connector_change:7;
10 u8 data_length;
11 unsigned int RESERVED9:9;
12 unsigned int not_supported:1;
13 unsigned int cancel_complete:1;
14 unsigned int reset_complete:1;
15 unsigned int busy:1;
16 unsigned int ack_complete:1;
17 unsigned int error:1;
18 unsigned int cmd_complete:1;
19} __packed;
20
21/* Default fields in CONTROL data structure */
22struct ucsi_command {
23 u8 cmd;
24 u8 length;
25 u64 data:48;
26} __packed;
27
28/* Set USB Operation Mode Command structure */
29struct ucsi_uor_cmd {
30 u8 cmd;
31 u8 length;
32 u64 con_num:7;
33 u64 role:3;
34#define UCSI_UOR_ROLE_DFP BIT(0)
35#define UCSI_UOR_ROLE_UFP BIT(1)
36#define UCSI_UOR_ROLE_DRP BIT(2)
37 u64 data:38;
38} __packed;
39
40struct ucsi_control {
41 union {
42 u64 raw_cmd;
43 struct ucsi_command cmd;
44 struct ucsi_uor_cmd uor;
45 };
46};
47
48struct ucsi_data {
49 u16 version;
50 u16 RESERVED;
51 union {
52 u32 raw_cci;
53 struct ucsi_cci cci;
54 };
55 struct ucsi_control ctrl;
56 u32 message_in[4];
57 u32 message_out[4];
58} __packed;
59
60/* Commands */
61#define UCSI_PPM_RESET 0x01
62#define UCSI_CANCEL 0x02
63#define UCSI_CONNECTOR_RESET 0x03
64#define UCSI_ACK_CC_CI 0x04
65#define UCSI_SET_NOTIFICATION_ENABLE 0x05
66#define UCSI_GET_CAPABILITY 0x06
67#define UCSI_GET_CONNECTOR_CAPABILITY 0x07
68#define UCSI_SET_UOM 0x08
69#define UCSI_SET_UOR 0x09
70#define UCSI_SET_PDM 0x0A
71#define UCSI_SET_PDR 0x0B
72#define UCSI_GET_ALTERNATE_MODES 0x0C
73#define UCSI_GET_CAM_SUPPORTED 0x0D
74#define UCSI_GET_CURRENT_CAM 0x0E
75#define UCSI_SET_NEW_CAM 0x0F
76#define UCSI_GET_PDOS 0x10
77#define UCSI_GET_CABLE_PROPERTY 0x11
78#define UCSI_GET_CONNECTOR_STATUS 0x12
79#define UCSI_GET_ERROR_STATUS 0x13
80
81/* ACK_CC_CI commands */
82#define UCSI_ACK_EVENT 1
83#define UCSI_ACK_CMD 2
84
85/* Bits for SET_NOTIFICATION_ENABLE command */
86#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(0)
87#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(1)
88#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(2)
89#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(5)
90#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(6)
91#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(7)
92#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(8)
93#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(9)
94#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(11)
95#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(12)
96#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(14)
97#define UCSI_ENABLE_NTFY_ERROR BIT(15)
98#define UCSI_ENABLE_NTFY_ALL 0xdbe7
99
100/* Error information returned by PPM in response to GET_ERROR_STATUS command. */
101#define UCSI_ERROR_UNREGONIZED_CMD BIT(0)
102#define UCSI_ERROR_INVALID_CON_NUM BIT(1)
103#define UCSI_ERROR_INVALID_CMD_ARGUMENT BIT(2)
104#define UCSI_ERROR_INCOMPATIBLE_PARTNER BIT(3)
105#define UCSI_ERROR_CC_COMMUNICATION_ERR BIT(4)
106#define UCSI_ERROR_DEAD_BATTERY BIT(5)
107#define UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL BIT(6)
108
109/* Data structure filled by PPM in response to GET_CAPABILITY command. */
110struct ucsi_capability {
111 u32 attributes;
112#define UCSI_CAP_ATTR_DISABLE_STATE BIT(0)
113#define UCSI_CAP_ATTR_BATTERY_CHARGING BIT(1)
114#define UCSI_CAP_ATTR_USB_PD BIT(2)
115#define UCSI_CAP_ATTR_TYPEC_CURRENT BIT(6)
116#define UCSI_CAP_ATTR_POWER_AC_SUPPLY BIT(8)
117#define UCSI_CAP_ATTR_POWER_OTHER BIT(10)
118#define UCSI_CAP_ATTR_POWER_VBUS BIT(14)
119 u8 num_connectors;
120 u32 features:24;
121#define UCSI_CAP_SET_UOM BIT(0)
122#define UCSI_CAP_SET_PDM BIT(1)
123#define UCSI_CAP_ALT_MODE_DETAILS BIT(2)
124#define UCSI_CAP_ALT_MODE_OVERRIDE BIT(3)
125#define UCSI_CAP_PDO_DETAILS BIT(4)
126#define UCSI_CAP_CABLE_DETAILS BIT(5)
127#define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6)
128#define UCSI_CAP_PD_RESET BIT(7)
129 u8 num_alt_modes;
130 u8 RESERVED;
131 u16 bc_version;
132 u16 pd_version;
133 u16 typec_version;
134} __packed;
135
136/* Data structure filled by PPM in response to GET_CONNECTOR_CAPABILITY cmd. */
137struct ucsi_connector_capability {
138 u8 op_mode;
139#define UCSI_CONCAP_OPMODE_DFP BIT(0)
140#define UCSI_CONCAP_OPMODE_UFP BIT(1)
141#define UCSI_CONCAP_OPMODE_DRP BIT(2)
142#define UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY BIT(3)
143#define UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY BIT(4)
144#define UCSI_CONCAP_OPMODE_USB2 BIT(5)
145#define UCSI_CONCAP_OPMODE_USB3 BIT(6)
146#define UCSI_CONCAP_OPMODE_ALT_MODE BIT(7)
147 u8 provider:1;
148 u8 consumer:1;
149} __packed;
150
151/* Data structure filled by PPM in response to GET_CABLE_PROPERTY command. */
152struct ucsi_cable_property {
153 u16 speed_supported;
154 u8 current_capability;
155 u8 vbus_in_cable:1;
156 u8 active_cable:1;
157 u8 directionality:1;
158 u8 plug_type:2;
159#define UCSI_CABLE_PROPERTY_PLUG_TYPE_A 0
160#define UCSI_CABLE_PROPERTY_PLUG_TYPE_B 1
161#define UCSI_CABLE_PROPERTY_PLUG_TYPE_C 2
162#define UCSI_CABLE_PROPERTY_PLUG_OTHER 3
163 u8 mode_support:1;
164 u8 RESERVED_2:2;
165 u8 latency:4;
166 u8 RESERVED_4:4;
167} __packed;
168
169/* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */
170struct ucsi_connector_status {
171 u16 change;
172#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1)
173#define UCSI_CONSTAT_POWER_OPMODE_CHANGE BIT(2)
174#define UCSI_CONSTAT_PDOS_CHANGE BIT(5)
175#define UCSI_CONSTAT_POWER_LEVEL_CHANGE BIT(6)
176#define UCSI_CONSTAT_PD_RESET_COMPLETE BIT(7)
177#define UCSI_CONSTAT_CAM_CHANGE BIT(8)
178#define UCSI_CONSTAT_BC_CHANGE BIT(9)
179#define UCSI_CONSTAT_PARTNER_CHANGE BIT(11)
180#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12)
181#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14)
182#define UCSI_CONSTAT_ERROR BIT(15)
183 u16 pwr_op_mode:3;
184#define UCSI_CONSTAT_PWR_OPMODE_NONE 0
185#define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1
186#define UCSI_CONSTAT_PWR_OPMODE_BC 2
187#define UCSI_CONSTAT_PWR_OPMODE_PD 3
188#define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_3 4
189#define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5
190 u16 connected:1;
191 u16 pwr_dir:1;
192 u16 partner_flags:8;
193#define UCSI_CONSTAT_PARTNER_FLAG_USB BIT(0)
194#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE BIT(1)
195 u16 partner_type:3;
196#define UCSI_CONSTAT_PARTNER_TYPE_DFP 1
197#define UCSI_CONSTAT_PARTNER_TYPE_UFP 2
198#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_NO_UFP 3 /* Powered Cable */
199#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */
200#define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5
201#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6
202 u32 request_data_obj;
203 u8 bc_status:2;
204#define UCSI_CONSTAT_BC_NOT_CHARGING 0
205#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1
206#define UCSI_CONSTAT_BC_SLOW_CHARGING 2
207#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3
208 u8 provider_cap_limit_reason:4;
209#define UCSI_CONSTAT_CAP_PWR_LOWERED 0
210#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1
211 u8 RESERVED:2;
212} __packed;
213
214/* -------------------------------------------------------------------------- */
215
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index da4c5c3d8870..d0c31cee4720 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -21,3 +21,19 @@ config TYPEC_UCSI
21 21
22 To compile the driver as a module, choose M here: the module will be 22 To compile the driver as a module, choose M here: the module will be
23 called typec_ucsi. 23 called typec_ucsi.
24
25if TYPEC_UCSI
26
27config UCSI_ACPI
28 tristate "UCSI ACPI Interface Driver"
29 depends on ACPI
30 help
31 This driver enables UCSI support on platforms that expose UCSI
32 interface as ACPI device. On new Intel Atom based platforms starting
33 from Broxton SoCs and Core platforms stating from Skylake, UCSI is an
34 ACPI enumerated device.
35
36 To compile the driver as a module, choose M here: the module will be
37 called ucsi_acpi
38
39endif
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index 87dd6ee6c9f3..8372fc22f9b3 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -5,3 +5,5 @@ obj-$(CONFIG_TYPEC_UCSI) += typec_ucsi.o
5typec_ucsi-y := ucsi.o 5typec_ucsi-y := ucsi.o
6 6
7typec_ucsi-$(CONFIG_FTRACE) += trace.o 7typec_ucsi-$(CONFIG_FTRACE) += trace.o
8
9obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
new file mode 100644
index 000000000000..3fb2e48e1c91
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -0,0 +1,158 @@
1/*
2 * UCSI ACPI driver
3 *
4 * Copyright (C) 2017, Intel Corporation
5 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/platform_device.h>
13#include <linux/module.h>
14#include <linux/acpi.h>
15
16#include "ucsi.h"
17
18#define UCSI_DSM_UUID "6f8398c2-7ca4-11e4-ad36-631042b5008f"
19#define UCSI_DSM_FUNC_WRITE 1
20#define UCSI_DSM_FUNC_READ 2
21
22struct ucsi_acpi {
23 struct device *dev;
24 struct ucsi *ucsi;
25 struct ucsi_ppm ppm;
26 uuid_le uuid;
27};
28
29static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
30{
31 union acpi_object *obj;
32
33 obj = acpi_evaluate_dsm(ACPI_HANDLE(ua->dev), ua->uuid.b, 1, func,
34 NULL);
35 if (!obj) {
36 dev_err(ua->dev, "%s: failed to evaluate _DSM %d\n",
37 __func__, func);
38 return -EIO;
39 }
40
41 ACPI_FREE(obj);
42 return 0;
43}
44
45static int ucsi_acpi_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
46{
47 struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm);
48
49 ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
50
51 return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
52}
53
54static int ucsi_acpi_sync(struct ucsi_ppm *ppm)
55{
56 struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm);
57
58 return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
59}
60
61static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
62{
63 struct ucsi_acpi *ua = data;
64
65 ucsi_notify(ua->ucsi);
66}
67
68static int ucsi_acpi_probe(struct platform_device *pdev)
69{
70 struct ucsi_acpi *ua;
71 struct resource *res;
72 acpi_status status;
73 int ret;
74
75 ua = devm_kzalloc(&pdev->dev, sizeof(*ua), GFP_KERNEL);
76 if (!ua)
77 return -ENOMEM;
78
79 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
80 if (!res) {
81 dev_err(&pdev->dev, "missing memory resource\n");
82 return -ENODEV;
83 }
84
85 /*
86 * NOTE: The memory region for the data structures is used also in an
87 * operation region, which means ACPI has already reserved it. Therefore
88 * it can not be requested here, and we can not use
89 * devm_ioremap_resource().
90 */
91 ua->ppm.data = devm_ioremap(&pdev->dev, res->start, resource_size(res));
92 if (!ua->ppm.data)
93 return -ENOMEM;
94
95 if (!ua->ppm.data->version)
96 return -ENODEV;
97
98 ret = uuid_le_to_bin(UCSI_DSM_UUID, &ua->uuid);
99 if (ret)
100 return ret;
101
102 ua->ppm.cmd = ucsi_acpi_cmd;
103 ua->ppm.sync = ucsi_acpi_sync;
104 ua->dev = &pdev->dev;
105
106 status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
107 ACPI_DEVICE_NOTIFY,
108 ucsi_acpi_notify, ua);
109 if (ACPI_FAILURE(status)) {
110 dev_err(&pdev->dev, "failed to install notify handler\n");
111 return -ENODEV;
112 }
113
114 ua->ucsi = ucsi_register_ppm(&pdev->dev, &ua->ppm);
115 if (IS_ERR(ua->ucsi)) {
116 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
117 ACPI_DEVICE_NOTIFY,
118 ucsi_acpi_notify);
119 return PTR_ERR(ua->ucsi);
120 }
121
122 platform_set_drvdata(pdev, ua);
123
124 return 0;
125}
126
127static int ucsi_acpi_remove(struct platform_device *pdev)
128{
129 struct ucsi_acpi *ua = platform_get_drvdata(pdev);
130
131 ucsi_unregister_ppm(ua->ucsi);
132
133 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY,
134 ucsi_acpi_notify);
135
136 return 0;
137}
138
139static const struct acpi_device_id ucsi_acpi_match[] = {
140 { "PNP0CA0", 0 },
141 { },
142};
143MODULE_DEVICE_TABLE(acpi, ucsi_acpi_match);
144
145static struct platform_driver ucsi_acpi_platform_driver = {
146 .driver = {
147 .name = "ucsi_acpi",
148 .acpi_match_table = ACPI_PTR(ucsi_acpi_match),
149 },
150 .probe = ucsi_acpi_probe,
151 .remove = ucsi_acpi_remove,
152};
153
154module_platform_driver(ucsi_acpi_platform_driver);
155
156MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
157MODULE_LICENSE("GPL v2");
158MODULE_DESCRIPTION("UCSI ACPI driver");