[linux-joule][PATCH 9/9] Enable type-c HOST mode with S0iX suspend
Wen-chien Jesse Sung
jesse.sung at canonical.com
Tue Jun 20 13:35:16 UTC 2017
From: Jari Nippula <jari.nippula at intel.com>
BugLink: https://launchpad.net/bugs/1698051
Signed-off-by: Jari Nippula <jari.nippula at intel.com>
[jesse.sung at canonical.com: fixed conflicts in drivers/usb/dwc3/dwc3-pci.c]
Signed-off-by: Wen-chien Jesse Sung <jesse.sung at canonical.com>
---
drivers/usb/dwc3/core.c | 220 +++++++++++++++++++++++++++++++++++++---
drivers/usb/dwc3/core.h | 37 +++++++
drivers/usb/dwc3/dwc3-pci.c | 57 ++---------
drivers/usb/typec/typec_wcove.c | 132 +++++++++++++++++-------
4 files changed, 350 insertions(+), 96 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 5dceacb..0a6e701 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -35,6 +35,8 @@
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -48,7 +50,122 @@
#include "debug.h"
-#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
+#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 1000 /* ms */
+
+#define PCI_INTEL_BXT_DSM_UUID "732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511"
+
+static const struct dev_pm_ops dwc3_dev_pm_ops;
+static const struct dev_pm_ops dwc3_dummy_dev_pm_ops;
+
+static int dwc3_resume(struct device *dev);
+static int dwc3_runtime_resume(struct device *dev);
+
+static int dwc3_mux_mode_get(struct device *dev)
+{
+ u8 uuid[16];
+ acpi_status ret;
+ union acpi_object element;
+ struct acpi_buffer buf = { 0, NULL };
+ union acpi_object params[4];
+ struct acpi_object_list input = {
+ .count = 4,
+ .pointer = params,
+ };
+
+ buf.length = sizeof(union acpi_object);
+ buf.pointer = &element;
+
+ acpi_str_to_uuid(PCI_INTEL_BXT_DSM_UUID, uuid);
+
+ params[0].type = ACPI_TYPE_BUFFER;
+ params[0].buffer.length = 16;
+ params[0].buffer.pointer = uuid;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = 1;
+ params[2].type = ACPI_TYPE_INTEGER;
+ params[2].integer.value = PCI_INTEL_BXT_FUNC_GET_MODE;
+ params[3].type = ACPI_TYPE_PACKAGE;
+ params[3].package.count = 0;
+ params[3].package.elements = NULL;
+
+ ret = acpi_evaluate_object(ACPI_HANDLE(dev), "_DSM", &input, &buf);
+ if (ACPI_SUCCESS(ret)) {
+ if (element.type != ACPI_TYPE_INTEGER) {
+ dev_err(dev, "%s: acpi object not an integer, func:%lld, type:%d\n",
+ __func__, params[2].integer.value, element.type);
+ return -EIO;
+ } else {
+ return (int) (element.integer.value & 0xff);
+ }
+ } else {
+ dev_err(dev, "%s: failed to evaluate _DSM, func:%lld\n", __func__, params[2].integer.value);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int dwc3_dsm(struct device *dev, int func, int param)
+{
+ u8 uuid[16];
+ union acpi_object *obj;
+ union acpi_object tmp;
+ union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
+
+ tmp.type = ACPI_TYPE_INTEGER;
+ tmp.integer.value = param;
+
+ acpi_str_to_uuid(PCI_INTEL_BXT_DSM_UUID, uuid);
+
+ obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), uuid,
+ 1, func, &argv4);
+ if (!obj) {
+ dev_err(dev, "failed to evaluate _DSM\n");
+ return -EIO;
+ }
+
+ ACPI_FREE(obj);
+
+ return 0;
+}
+
+static void dwc3_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct platform_device *pdev = (struct platform_device *) data;
+ struct device *dev = &pdev->dev;
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct device_driver *drv = dev->driver;
+ switch (event)
+ {
+ case DWC3_ACPI_EVENT_MODE_HOST:
+ if (!dwc->host_mode) {
+ dwc->host_mode = true;
+ pm_runtime_get_sync(dev);
+ drv->pm = &dwc3_dummy_dev_pm_ops;
+ pm_runtime_put(dev);
+ dwc3_dsm(dev, PCI_INTEL_BXT_FUNC_XDCA, PCI_INTEL_BXT_DEV_PREPARED);
+ }
+ break;
+ case DWC3_ACPI_EVENT_MODE_DEVICE:
+ if (dwc->host_mode) {
+ dwc->host_mode = false;
+ dwc3_dsm(dev, PCI_INTEL_BXT_FUNC_XDCA, PCI_INTEL_BXT_DEV_NOT_PREPARED);
+ pm_runtime_get_sync(dev);
+ if (dwc->dummy_resumed == DWC3_DUMMY_TYPE_RESUME) {
+ pm_runtime_get_sync(dev);
+ dwc3_resume(dev);
+ } else if (dwc->dummy_resumed == DWC3_DUMMY_TYPE_RUNTIME_RESUME) {
+ pm_runtime_get_sync(dev);
+ dwc3_runtime_resume(dev);
+ }
+ drv->pm = &dwc3_dev_pm_ops;
+ pm_runtime_put(dev);
+ }
+ break;
+ default:
+ break;
+ }
+}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{
@@ -567,14 +684,20 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->maximum_speed = USB_SPEED_HIGH;
}
- /* issue device SoftReset too */
- ret = dwc3_soft_reset(dwc);
- if (ret)
- goto err0;
+ dwc3_dsm(dwc->dev, PCI_INTEL_BXT_FUNC_MUX_CTRL, PCI_INTEL_BXT_MUX_CTRL_DENY);
+ if (dwc3_mux_mode_get(dwc->dev) >= PCI_INTEL_BXT_MODE_DEVICE) {
+ /* issue device SoftReset too */
+ ret = dwc3_soft_reset(dwc);
+ if (ret) {
+ goto err0;
+ }
- ret = dwc3_core_soft_reset(dwc);
- if (ret)
- goto err0;
+ ret = dwc3_core_soft_reset(dwc);
+ if (ret) {
+ goto err0;
+ }
+ }
+ dwc3_dsm(dwc->dev, PCI_INTEL_BXT_FUNC_MUX_CTRL, PCI_INTEL_BXT_MUX_CTRL_ALLOW);
ret = dwc3_phy_setup(dwc);
if (ret)
@@ -831,6 +954,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct acpi_device *adev = ACPI_COMPANION(dev->parent);
struct dwc3_platform_data *pdata = dev_get_platdata(dev);
struct resource *res;
struct dwc3 *dwc;
@@ -888,6 +1012,13 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->regs = regs;
dwc->regs_size = resource_size(res);
+ /* Initialize "xDCI prepared for host mode" status for
+ USB MUX HOST mode transition */
+ dwc3_dsm(dev, PCI_INTEL_BXT_FUNC_XDCA, PCI_INTEL_BXT_DEV_NOT_PREPARED);
+
+ dwc->host_mode = false;
+
+
/* default to highest possible threshold */
lpm_nyet_threshold = 0xff;
@@ -1065,8 +1196,16 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret)
goto err3;
+ ret = acpi_install_notify_handler(adev->handle,
+ ACPI_DEVICE_NOTIFY,
+ dwc3_acpi_notify,
+ pdev);
+
dwc3_debugfs_init(dwc);
pm_runtime_put(dev);
+ pm_runtime_put(dev->parent);
+ dwc->dummy_resumed = DWC3_DUMMY_TYPE_NONE;
+ dwc3_dsm(dev, PCI_INTEL_BXT_FUNC_MUX_CTRL, PCI_INTEL_BXT_MUX_CTRL_ALLOW);
return 0;
@@ -1104,6 +1243,10 @@ static int dwc3_remove(struct platform_device *pdev)
*/
res->start -= DWC3_GLOBALS_REGS_START;
+ acpi_remove_notify_handler(ACPI_COMPANION(pdev->dev.parent)->handle,
+ ACPI_DEVICE_NOTIFY,
+ dwc3_acpi_notify);
+
dwc3_debugfs_exit(dwc);
dwc3_core_exit_mode(dwc);
@@ -1117,6 +1260,9 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_free_event_buffers(dwc);
dwc3_free_scratch_buffers(dwc);
+ // Allow typec mux switches after dwc3 driver is unloaded.
+ dwc3_dsm(&pdev->dev, PCI_INTEL_BXT_FUNC_XDCA, PCI_INTEL_BXT_DEV_PREPARED);
+
return 0;
}
@@ -1140,7 +1286,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc)
dwc3_core_exit(dwc);
- return 0;
+ return dwc3_dsm(dwc->dev, PCI_INTEL_BXT_FUNC_PMU_PWR, PCI_INTEL_BXT_STATE_D3);
}
static int dwc3_resume_common(struct dwc3 *dwc)
@@ -1148,6 +1294,10 @@ static int dwc3_resume_common(struct dwc3 *dwc)
unsigned long flags;
int ret;
+ ret = dwc3_dsm(dwc->dev, PCI_INTEL_BXT_FUNC_PMU_PWR, PCI_INTEL_BXT_STATE_D0);
+ if (ret)
+ return ret;
+
ret = dwc3_core_init(dwc);
if (ret)
return ret;
@@ -1190,13 +1340,21 @@ static int dwc3_runtime_suspend(struct device *dev)
struct dwc3 *dwc = dev_get_drvdata(dev);
int ret;
- if (dwc3_runtime_checks(dwc))
+
+ if (dwc->dummy_resumed != DWC3_DUMMY_TYPE_NONE) {
+ return 0;
+ }
+
+ if (dwc3_runtime_checks(dwc)) {
+ return -EBUSY;
+ } else if (!device_run_wake(dwc->dev->parent)) {
return -EBUSY;
+ }
ret = dwc3_suspend_common(dwc);
- if (ret)
+ if (ret) {
return ret;
-
+ }
device_init_wakeup(dev, true);
return 0;
@@ -1207,6 +1365,7 @@ static int dwc3_runtime_resume(struct device *dev)
struct dwc3 *dwc = dev_get_drvdata(dev);
int ret;
+
device_init_wakeup(dev, false);
ret = dwc3_resume_common(dwc);
@@ -1224,12 +1383,25 @@ static int dwc3_runtime_resume(struct device *dev)
break;
}
+ dwc->dummy_resumed = DWC3_DUMMY_TYPE_NONE;
pm_runtime_mark_last_busy(dev);
pm_runtime_put(dev);
return 0;
}
+static int dwc3_runtime_resume_dummy(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ device_init_wakeup(dev, false);
+ if (dwc->dummy_resumed == DWC3_DUMMY_TYPE_NONE)
+ dwc->dummy_resumed = DWC3_DUMMY_TYPE_RUNTIME_RESUME;
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put(dev);
+ return 0;
+}
+
static int dwc3_runtime_idle(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
@@ -1251,6 +1423,7 @@ static int dwc3_runtime_idle(struct device *dev)
return 0;
}
+
#endif /* CONFIG_PM */
#ifdef CONFIG_PM_SLEEP
@@ -1264,6 +1437,7 @@ static int dwc3_suspend(struct device *dev)
return ret;
pinctrl_pm_select_sleep_state(dev);
+ dwc3_dsm(dwc->dev, PCI_INTEL_BXT_FUNC_MUX_CTRL, PCI_INTEL_BXT_MUX_CTRL_DENY);
return 0;
}
@@ -1275,15 +1449,31 @@ static int dwc3_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
+ dwc3_dsm(dwc->dev, PCI_INTEL_BXT_FUNC_MUX_CTRL, PCI_INTEL_BXT_MUX_CTRL_ALLOW);
+
ret = dwc3_resume_common(dwc);
if (ret)
return ret;
+ dwc->dummy_resumed = DWC3_DUMMY_TYPE_NONE;
+
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_put(dev);
+ return 0;
+}
+static int dwc3_resume_dummy(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ dwc->dummy_resumed = DWC3_DUMMY_TYPE_RESUME;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_put(dev);
return 0;
}
#endif /* CONFIG_PM_SLEEP */
@@ -1294,6 +1484,12 @@ static const struct dev_pm_ops dwc3_dev_pm_ops = {
dwc3_runtime_idle)
};
+static const struct dev_pm_ops dwc3_dummy_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume_dummy)
+ SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume_dummy,
+ dwc3_runtime_idle)
+};
+
#ifdef CONFIG_OF
static const struct of_device_id of_dwc3_match[] = {
{
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index a48a3b8..f2a5da9 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -440,6 +440,28 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3
+/* _DSM functions */
+#define PCI_INTEL_BXT_FUNC_PMU_PWR 4
+#define PCI_INTEL_BXT_STATE_D0 0
+#define PCI_INTEL_BXT_STATE_D3 3
+
+#define PCI_INTEL_BXT_FUNC_XDCA 8
+#define PCI_INTEL_BXT_DEV_PREPARED 0
+#define PCI_INTEL_BXT_DEV_NOT_PREPARED 1
+
+#define PCI_INTEL_BXT_FUNC_GET_MODE 9
+#define PCI_INTEL_BXT_MODE_HOST 0
+#define PCI_INTEL_BXT_MODE_DEVICE 1
+
+#define PCI_INTEL_BXT_FUNC_MUX_CTRL 10
+#define PCI_INTEL_BXT_MUX_CTRL_DENY 0
+#define PCI_INTEL_BXT_MUX_CTRL_ALLOW 1
+
+
+/* ACPI Notifications */
+#define DWC3_ACPI_EVENT_MODE_HOST 0x81
+#define DWC3_ACPI_EVENT_MODE_DEVICE 0x82
+
/* Structures */
struct dwc3_trb;
@@ -584,6 +606,18 @@ enum dwc3_link_state {
DWC3_LINK_STATE_MASK = 0x0f,
};
+// enum suspend_type {
+// DWC3_SUSPEND_TYPE_NONE = 0,
+// DWC3_SUSPEND_TYPE_RUNTIME_SUSPEND,
+// DWC3_SUSPEND_TYPE_SUSPEND,
+// };
+
+enum dummy_resume_type {
+ DWC3_DUMMY_TYPE_NONE = 0,
+ DWC3_DUMMY_TYPE_RUNTIME_RESUME,
+ DWC3_DUMMY_TYPE_RESUME,
+};
+
/* TRB Length, PCM and Status */
#define DWC3_TRB_SIZE_MASK (0x00ffffff)
#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
@@ -838,6 +872,9 @@ struct dwc3 {
enum usb_dr_mode dr_mode;
+ bool host_mode;
+ enum dummy_resume_type dummy_resumed;
+
u32 fladj;
u32 irq_gadget;
u32 nr_scratch;
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index bf00c37..7898b2b 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -41,11 +41,6 @@
#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0
#define PCI_DEVICE_ID_INTEL_GLK 0x31aa
-#define PCI_INTEL_BXT_DSM_UUID "732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511"
-#define PCI_INTEL_BXT_FUNC_PMU_PWR 4
-#define PCI_INTEL_BXT_STATE_D0 0
-#define PCI_INTEL_BXT_STATE_D3 3
-
struct dwc3_pci {
struct platform_device *dwc3;
struct pci_dev *pci;
@@ -150,32 +145,6 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc_pci, struct platform_device *dwc
return 0;
}
-static int dwc3_pci_dsm(struct dwc3_pci *dwc_pci, int param)
-{
- union acpi_object *obj;
- union acpi_object tmp;
- union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
-
- if (!dwc_pci->has_dsm_for_pm)
- return 0;
-
- tmp.type = ACPI_TYPE_INTEGER;
- tmp.integer.value = param;
-
- acpi_str_to_uuid(PCI_INTEL_BXT_DSM_UUID, dwc_pci->uuid);
-
- obj = acpi_evaluate_dsm(ACPI_HANDLE(&dwc_pci->pci->dev), dwc_pci->uuid,
- 1, PCI_INTEL_BXT_FUNC_PMU_PWR, &argv4);
- if (!obj) {
- dev_err(&dwc_pci->pci->dev, "failed to evaluate _DSM\n");
- return -EIO;
- }
-
- ACPI_FREE(obj);
-
- return 0;
-}
-
static int dwc3_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
@@ -239,7 +208,6 @@ static int dwc3_pci_probe(struct pci_dev *pci,
device_init_wakeup(dev, true);
device_set_run_wake(dev, true);
pci_set_drvdata(pci, dwc_pci);
- pm_runtime_put(dev);
return 0;
err:
@@ -288,39 +256,32 @@ MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
#ifdef CONFIG_PM
static int dwc3_pci_runtime_suspend(struct device *dev)
{
- struct dwc3_pci *dwc_pci = dev_get_drvdata(dev);
-
- if (device_run_wake(dev))
- return dwc3_pci_dsm(dwc_pci, PCI_INTEL_BXT_STATE_D3);
-
- return -EBUSY;
+ return 0;
}
static int dwc3_pci_runtime_resume(struct device *dev)
{
struct dwc3_pci *dwc_pci = dev_get_drvdata(dev);
struct platform_device *dwc3 = dwc_pci->dwc3;
- int ret;
-
- ret = dwc3_pci_dsm(dwc_pci, PCI_INTEL_BXT_STATE_D0);
- if (ret)
- return ret;
- return pm_runtime_get(&dwc3->dev);
+ if (dwc3)
+ pm_runtime_get(&dwc3->dev);
+ return 0;
}
static int dwc3_pci_suspend(struct device *dev)
{
- struct dwc3_pci *dwc_pci = dev_get_drvdata(dev);
-
- return dwc3_pci_dsm(dwc_pci, PCI_INTEL_BXT_STATE_D3);
+ return 0;
}
static int dwc3_pci_resume(struct device *dev)
{
struct dwc3_pci *dwc_pci = dev_get_drvdata(dev);
+ struct platform_device *dwc3 = dwc_pci->dwc3;
- return dwc3_pci_dsm(dwc_pci, PCI_INTEL_BXT_STATE_D0);
+ if (dwc3)
+ pm_runtime_get(&dwc3->dev);
+ return 0;
}
#endif /* CONFIG_PM */
diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/typec_wcove.c
index b5ef768..1c9527a 100644
--- a/drivers/usb/typec/typec_wcove.c
+++ b/drivers/usb/typec/typec_wcove.c
@@ -126,7 +126,8 @@
#define USBC_PD_RX_BUF_LEN 30
#define USBC_PD_TX_BUF_LEN 30
-#define XHCI_MUX 0x924080d8
+#define PCI_DEVICE_ID_INTEL_XDCI 0x1aaa
+#define WCOVE_XDCI_PREPARE_TIMEOUT 500
struct wcove_typec {
int pd_port_num;
@@ -140,7 +141,6 @@ struct wcove_typec {
struct typec_connection con;
struct typec_partner partner;
struct pci_dev *xhci_dev;
- void __iomem *drd_mux;
};
enum wcove_typec_func {
@@ -148,6 +148,8 @@ enum wcove_typec_func {
WCOVE_FUNC_ORIENTATION,
WCOVE_FUNC_ROLE,
WCOVE_FUNC_DRIVE_VCONN,
+ WCOVE_FUNC_NOTIFY_XDCI,
+ WCOVE_FUNC_GET_XDCI_PREP_STATUS,
};
enum wcove_typec_orientation {
@@ -161,6 +163,17 @@ enum wcove_typec_role {
WCOVE_ROLE_DEVICE_NO_VBUS,
};
+enum wcove_typec_target {
+ WCOVE_TARGET_HOST,
+ WCOVE_TARGET_DEVICE,
+};
+
+
+struct dwc3_pci {
+ struct platform_device *dwc3;
+ struct pci_dev *pci;
+};
+
static struct sink_ps profiles[] = {
{
@@ -217,35 +230,69 @@ static int wcove_access_xhci(struct wcove_typec *wcove, bool enable)
pm_runtime_put(&wcove->xhci_dev->dev);
}
}
+
+ return 0;
+}
+
+static int wcove_mode_get(struct wcove_typec *wcove)
+{
+ acpi_status ret;
+ union acpi_object element;
+ struct acpi_buffer buf = { 0, NULL };
+ union acpi_object params[4];
+ struct acpi_object_list input = {
+ .count = 4,
+ .pointer = params,
+ };
+
+ buf.length = sizeof(union acpi_object);
+ buf.pointer = &element;
+
+ params[0].type = ACPI_TYPE_BUFFER;
+ params[0].buffer.length = 16;
+ params[0].buffer.pointer = uuid.b;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = 1;
+ params[2].type = ACPI_TYPE_INTEGER;
+ params[2].integer.value = WCOVE_FUNC_GET_XDCI_PREP_STATUS;
+ params[3].type = ACPI_TYPE_PACKAGE;
+ params[3].package.count = 0;
+ params[3].package.elements = NULL;
+
+ ret = acpi_evaluate_object(ACPI_HANDLE(wcove->dev), "_DSM", &input, &buf);
+ if (ACPI_SUCCESS(ret)) {
+ if (element.type != ACPI_TYPE_INTEGER) {
+ dev_err(wcove->dev, "%s: acpi object not an integer!! %d\n", __func__, element.type);
+ return -EIO;
+ } else {
+ return (int) (element.integer.value & 0xff);
+ }
+ } else {
+ dev_err(wcove->dev, "%s: failed to evaluate _DSM, func:%lld\n", __func__, params[2].integer.value);
+ return -EIO;
+ }
+
return 0;
}
static int wcove_typec_func(struct wcove_typec *wcove,
- enum wcove_typec_func func, int param)
+ enum wcove_typec_func func, int param)
{
union acpi_object *obj;
union acpi_object tmp;
union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
- if (func == WCOVE_FUNC_ROLE && param == WCOVE_ROLE_DEVICE_NO_VBUS)
- {
- /* Disable VBUS_VALID bit to indicate dwc3 about cable disconnection */
- writel(0x300000, wcove->drd_mux);
+ // Take func action
+ tmp.type = ACPI_TYPE_INTEGER;
+ tmp.integer.value = param;
+ obj = acpi_evaluate_dsm(ACPI_HANDLE(wcove->dev), uuid.b, 1, func,
+ &argv4);
+ if (!obj) {
+ dev_err(wcove->dev, "%s: failed to evaluate _DSM\n", __func__);
+ return -EIO;
}
- else
- {
- tmp.type = ACPI_TYPE_INTEGER;
- tmp.integer.value = param;
+ ACPI_FREE(obj);
- obj = acpi_evaluate_dsm(ACPI_HANDLE(wcove->dev), uuid.b, 1, func,
- &argv4);
- if (!obj) {
- dev_err(wcove->dev, "%s: failed to evaluate _DSM\n", __func__);
- return -EIO;
- }
-
- ACPI_FREE(obj);
- }
return 0;
}
@@ -255,6 +302,7 @@ static void wcove_typec_device_mode(struct wcove_typec *wcove)
wcove->con.partner = &wcove->partner;
wcove->con.pwr_role = TYPEC_SINK;
wcove->con.vconn_role = TYPEC_SINK;
+
wcove_access_xhci(wcove, true);
wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_DEVICE);
wcove_access_xhci(wcove, false);
@@ -376,6 +424,7 @@ static irqreturn_t wcove_typec_irq(int irq, void *data)
unsigned int status2;
unsigned int rx_status;
int ret;
+ int i;
mutex_lock(&wcove->lock);
ret = regmap_read(wcove->regmap, USBC_IRQ1, &cc_irq1);
@@ -417,8 +466,8 @@ static irqreturn_t wcove_typec_irq(int irq, void *data)
if (status1 & USBC_STATUS1_DET_ONGOING)
goto out;
- if (USBC_STATUS1_RSLT(status1) == USBC_RSLT_NOTHING ||
- USBC_STATUS1_RSLT(status1) == USBC_RSLT_SNK) {
+ if (USBC_STATUS1_RSLT(status1) == USBC_RSLT_NOTHING)
+ {
if (wcove->con.partner) {
typec_disconnect(wcove->port);
memset(&wcove->con, 0, sizeof(wcove->con));
@@ -431,6 +480,7 @@ static irqreturn_t wcove_typec_irq(int irq, void *data)
wcove_access_xhci(wcove, true);
wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_DEVICE_NO_VBUS);
wcove_access_xhci(wcove, false);
+ wcove_typec_func(wcove, WCOVE_FUNC_NOTIFY_XDCI, WCOVE_TARGET_DEVICE);
/* reset the pd sink state */
if (wcove->pd_port_num >= 0)
pd_sink_reset_state(wcove->pd_port_num);
@@ -488,18 +538,30 @@ static irqreturn_t wcove_typec_irq(int irq, void *data)
wcove->con.pwr_opmode = TYPEC_PWR_MODE_3_0A;
wcove_typec_device_mode(wcove);
break;
- // case USBC_RSLT_SNK:
- // wcove->partner.type = TYPEC_PARTNER_USB;
- // wcove->con.partner = &wcove->partner;
- // wcove->con.data_role = TYPEC_HOST;
- // wcove->con.pwr_role = TYPEC_SOURCE;
- // wcove->con.vconn_role = TYPEC_SOURCE;
- // wcove_access_xhci(wcove, true);
- // wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_HOST);
- // wcove_access_xhci(wcove, false);
-
- // typec_connect(wcove->port, &wcove->con);
- // break;
+ case USBC_RSLT_SNK:
+ wcove->partner.type = TYPEC_PARTNER_USB;
+ wcove->con.partner = &wcove->partner;
+ wcove->con.data_role = TYPEC_HOST;
+ wcove->con.pwr_role = TYPEC_SOURCE;
+ wcove->con.vconn_role = TYPEC_SOURCE;
+
+ wcove_typec_func(wcove, WCOVE_FUNC_NOTIFY_XDCI, WCOVE_TARGET_HOST);
+ for (i = 0; i < WCOVE_XDCI_PREPARE_TIMEOUT; i++) {
+ ret = wcove_mode_get(wcove);
+ if (ret == 0) {
+ break;
+ } else if (ret < 0) {
+ dev_err(wcove->dev, "_DSM method failed!\n");
+ goto out;
+ }
+ msleep(100);
+ }
+ wcove_access_xhci(wcove, true);
+ wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_HOST);
+ wcove_access_xhci(wcove, false);
+
+ typec_connect(wcove->port, &wcove->con);
+ break;
case USBC_RSLT_DEBUG_ACC:
wcove->partner.accessory = TYPEC_ACCESSORY_DEBUG;
wcove->partner.type = TYPEC_PARTNER_ACCESSORY;
@@ -562,8 +624,6 @@ static int wcove_typec_probe(struct platform_device *pdev)
if (IS_ERR(wcove->port))
return PTR_ERR(wcove->port);
- wcove->drd_mux = ioremap(XHCI_MUX, 0x0f);
-
/* PD receive packet handler */
wcove->pd_port_num = pd_sink_register_port(&profile,
wcove_typec_pd_tx_pkt_handler, wcove);
--
2.7.4
More information about the kernel-team
mailing list