Received: by 10.223.185.116 with SMTP id b49csp6298489wrg; Wed, 28 Feb 2018 07:11:05 -0800 (PST) X-Google-Smtp-Source: AH8x225gAArzNcWSZfnJRzXoUh4JD7eLDwEbsOIyn4kpjpKZP5TyxxzakLpyq7WnmL6Xq7jUBOqj X-Received: by 10.101.71.202 with SMTP id f10mr14260854pgs.91.1519830664877; Wed, 28 Feb 2018 07:11:04 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1519830664; cv=none; d=google.com; s=arc-20160816; b=vciX+/z0w4rQCAQGtLPRs/FRi2Wmh9jq18JGOQJm4LpQ3hk8B6CDtrYmv4/3Yl6Yrq O9c+ZR6PqVFOocFqaUom9wBgOM76Vq1IcP90D5zkm/rtdO11cBUTKk7eAle8910xTct6 4zDmrdXOrhoYtZDVfAyQSIrrr07fzK5ikFGSkeNMnKTK8XLI9gQcwveZ1C43ED4FrRl9 +IAmaTmVfcOmzkEU+f2ti4br/6rUP56fRIaZuPQBOVaR0ogQ/et+SorxhYT18+6VUMsn Z00f8R7L67YTRGzEnm7X002OO7gOgcMeHbEezxREX63TwZdb+dE8sUAH/X2LCOE2jcPV 2YDg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=qspz/SRja3kmYo6uP8Quca0hZ+hKxQFMqI2WLM+FrWA=; b=kKprE5p7J/CvJEJNgyeN0jp2MlhTzQlOvJBiNF/BtJIrFjb3t7IDLRIXxUlIK0vJSf DS1jyK+gi0dABkLPIrw6uCmo5yMNPvGq9HRPrFWiZ6KDYyDt0o4G029dDJAzSiOJoOy1 XC92Mq0NPWkThL3H4KKcCWCOlCFrp7iwmvdz/ZMT33DK7+FmJB8SpdbVW82CqzWi53ct 2tC5jIpvV7m/tWzUn/VY86PZqMoW+w7fw8mhi/A+/kOpNpm0vBNL1HLUFBtQ1zbFsDYa viagSxZAzdr2sv2sKteqA5mBChnMshQiP9bZg0E0amVdzqLI7IbAOyXr4GJIgm6JGn4L NCnw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id d18si1126068pgp.283.2018.02.28.07.10.47; Wed, 28 Feb 2018 07:11:04 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932513AbeB1PIQ (ORCPT + 99 others); Wed, 28 Feb 2018 10:08:16 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:43322 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S932422AbeB1PIO (ORCPT ); Wed, 28 Feb 2018 10:08:14 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3639040FB659; Wed, 28 Feb 2018 15:08:13 +0000 (UTC) Received: from shalem.localdomain.com (ovpn-116-106.ams2.redhat.com [10.36.116.106]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9E4AE9C042; Wed, 28 Feb 2018 15:08:11 +0000 (UTC) From: Hans de Goede To: Darren Hart , Andy Shevchenko , MyungJoo Ham , Chanwoo Choi , Mathias Nyman , Heikki Krogerus , Greg Kroah-Hartman , Guenter Roeck Cc: Hans de Goede , platform-driver-x86@vger.kernel.org, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Subject: [PATCH v5 09/12] usb: roles: Add Intel xHCI USB role switch driver Date: Wed, 28 Feb 2018 16:07:46 +0100 Message-Id: <20180228150749.26831-10-hdegoede@redhat.com> In-Reply-To: <20180228150749.26831-1-hdegoede@redhat.com> References: <20180228150749.26831-1-hdegoede@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Wed, 28 Feb 2018 15:08:13 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Wed, 28 Feb 2018 15:08:13 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'hdegoede@redhat.com' RCPT:'' Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Various Intel SoCs (Cherry Trail, Broxton and others) have an internal USB role switch for swiching the OTG USB data lines between the xHCI host controller and the dwc3 gadget controller. Note on some Cherry Trail systems there is ACPI/AML code listening to edge interrupts on the id-pin (through an _AIE ACPI method) and switching the role between ROLE_HOST and ROLE_NONE based on the id-pin. Note it does not set the role to ROLE_DEVICE, because device-mode is usually not used under Windows. The presence of AML code which modifies the cfg0 reg (on some systems) means that our read/write/modify of cfg0 may race with the AML code doing the same to avoid this we take the global ACPI lock while doing the read/write/modify. Reviewed-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Signed-off-by: Hans de Goede --- Changes in v4: -Add Andy's Reviewed-by -Add Heikki's Reviewed-by Changes in v2: -Drop unnecessary depends on EXTCON from Kconfig -Use BIT(), resource_size() -Various other small style fixes --- MAINTAINERS | 6 + drivers/usb/Kconfig | 2 + drivers/usb/Makefile | 2 + drivers/usb/roles/Kconfig | 14 ++ drivers/usb/roles/Makefile | 1 + drivers/usb/roles/intel-xhci-usb-role-switch.c | 192 +++++++++++++++++++++++++ 6 files changed, 217 insertions(+) create mode 100644 drivers/usb/roles/Kconfig create mode 100644 drivers/usb/roles/Makefile create mode 100644 drivers/usb/roles/intel-xhci-usb-role-switch.c diff --git a/MAINTAINERS b/MAINTAINERS index 0f66f044f988..523f14b4216d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14394,6 +14394,12 @@ S: Maintained F: Documentation/hid/hiddev.txt F: drivers/hid/usbhid/ +USB INTEL XHCI ROLE MUX DRIVER +M: Hans de Goede +L: linux-usb@vger.kernel.org +S: Maintained +F: drivers/usb/roles/intel-xhci-usb-role-switch.c + USB ISP116X DRIVER M: Olav Kongas L: linux-usb@vger.kernel.org diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index f278958e04ca..75f7fb151f71 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -171,6 +171,8 @@ source "drivers/usb/gadget/Kconfig" source "drivers/usb/typec/Kconfig" +source "drivers/usb/roles/Kconfig" + config USB_LED_TRIG bool "USB LED Triggers" depends on LEDS_CLASS && LEDS_TRIGGERS diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 060643a1b5c8..7d1b8c82b208 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -65,3 +65,5 @@ obj-$(CONFIG_USB_COMMON) += common/ obj-$(CONFIG_USBIP_CORE) += usbip/ obj-$(CONFIG_TYPEC) += typec/ + +obj-$(CONFIG_USB_ROLE_SWITCH) += roles/ diff --git a/drivers/usb/roles/Kconfig b/drivers/usb/roles/Kconfig new file mode 100644 index 000000000000..f5a5e6f79f1b --- /dev/null +++ b/drivers/usb/roles/Kconfig @@ -0,0 +1,14 @@ +if USB_ROLE_SWITCH + +config USB_ROLES_INTEL_XHCI + tristate "Intel XHCI USB Role Switch" + depends on ACPI && X86 + help + Driver for the internal USB role switch for switching the USB data + lines between the xHCI host controller and the dwc3 gadget controller + found on various Intel SoCs. + + To compile the driver as a module, choose M here: the module will + be called intel-xhci-usb-role-switch. + +endif # USB_ROLE_SWITCH diff --git a/drivers/usb/roles/Makefile b/drivers/usb/roles/Makefile new file mode 100644 index 000000000000..e44b179ba275 --- /dev/null +++ b/drivers/usb/roles/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_ROLES_INTEL_XHCI) += intel-xhci-usb-role-switch.o diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c new file mode 100644 index 000000000000..3d7be5eace49 --- /dev/null +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Intel XHCI (Cherry Trail, Broxton and others) USB OTG role switch driver + * + * Copyright (c) 2016-2017 Hans de Goede + * + * Loosely based on android x86 kernel code which is: + * + * Copyright (C) 2014 Intel Corp. + * + * Author: Wu, Hao + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* register definition */ +#define DUAL_ROLE_CFG0 0x68 +#define SW_VBUS_VALID BIT(24) +#define SW_IDPIN_EN BIT(21) +#define SW_IDPIN BIT(20) + +#define DUAL_ROLE_CFG1 0x6c +#define HOST_MODE BIT(29) + +#define DUAL_ROLE_CFG1_POLL_TIMEOUT 1000 + +#define DRV_NAME "intel_xhci_usb_sw" + +struct intel_xhci_usb_data { + struct usb_role_switch *role_sw; + void __iomem *base; +}; + +struct intel_xhci_acpi_match { + const char *hid; + int hrv; +}; + +/* + * ACPI IDs for PMICs which do not support separate data and power role + * detection (USB ACA detection for micro USB OTG), we allow userspace to + * change the role manually on these. + */ +static const struct intel_xhci_acpi_match allow_userspace_ctrl_ids[] = { + { "INT33F4", 3 }, /* X-Powers AXP288 PMIC */ +}; + +static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) +{ + struct intel_xhci_usb_data *data = dev_get_drvdata(dev); + unsigned long timeout; + acpi_status status; + u32 glk, val; + + /* + * On many CHT devices ACPI event (_AEI) handlers read / modify / + * write the cfg0 register, just like we do. Take the ACPI lock + * to avoid us racing with the AML code. + */ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + dev_err(dev, "Error could not acquire lock\n"); + return -EIO; + } + + /* Set idpin value as requested */ + val = readl(data->base + DUAL_ROLE_CFG0); + switch (role) { + case USB_ROLE_NONE: + val |= SW_IDPIN; + val &= ~SW_VBUS_VALID; + break; + case USB_ROLE_HOST: + val &= ~SW_IDPIN; + val &= ~SW_VBUS_VALID; + break; + case USB_ROLE_DEVICE: + val |= SW_IDPIN; + val |= SW_VBUS_VALID; + break; + } + val |= SW_IDPIN_EN; + + writel(val, data->base + DUAL_ROLE_CFG0); + + acpi_release_global_lock(glk); + + /* In most case it takes about 600ms to finish mode switching */ + timeout = jiffies + msecs_to_jiffies(DUAL_ROLE_CFG1_POLL_TIMEOUT); + + /* Polling on CFG1 register to confirm mode switch.*/ + do { + val = readl(data->base + DUAL_ROLE_CFG1); + if (!!(val & HOST_MODE) == (role == USB_ROLE_HOST)) + return 0; + + /* Interval for polling is set to about 5 - 10 ms */ + usleep_range(5000, 10000); + } while (time_before(jiffies, timeout)); + + dev_warn(dev, "Timeout waiting for role-switch\n"); + return -ETIMEDOUT; +} + +static enum usb_role intel_xhci_usb_get_role(struct device *dev) +{ + struct intel_xhci_usb_data *data = dev_get_drvdata(dev); + enum usb_role role; + u32 val; + + val = readl(data->base + DUAL_ROLE_CFG0); + + if (!(val & SW_IDPIN)) + role = USB_ROLE_HOST; + else if (val & SW_VBUS_VALID) + role = USB_ROLE_DEVICE; + else + role = USB_ROLE_NONE; + + return role; +} + +static struct usb_role_switch_desc sw_desc = { + .set = intel_xhci_usb_set_role, + .get = intel_xhci_usb_get_role, +}; + +static int intel_xhci_usb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_xhci_usb_data *data; + struct resource *res; + int i; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->base = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + for (i = 0; i < ARRAY_SIZE(allow_userspace_ctrl_ids); i++) + if (acpi_dev_present(allow_userspace_ctrl_ids[i].hid, "1", + allow_userspace_ctrl_ids[i].hrv)) + sw_desc.allow_userspace_control = true; + + platform_set_drvdata(pdev, data); + + data->role_sw = usb_role_switch_register(dev, &sw_desc); + if (IS_ERR(data->role_sw)) + return PTR_ERR(data->role_sw); + + return 0; +} + +int intel_xhci_usb_remove(struct platform_device *pdev) +{ + struct intel_xhci_usb_data *data = platform_get_drvdata(pdev); + + usb_role_switch_unregister(data->role_sw); + return 0; +} + +static const struct platform_device_id intel_xhci_usb_table[] = { + { .name = DRV_NAME }, + {} +}; +MODULE_DEVICE_TABLE(platform, intel_xhci_usb_table); + +static struct platform_driver intel_xhci_usb_driver = { + .driver = { + .name = DRV_NAME, + }, + .id_table = intel_xhci_usb_table, + .probe = intel_xhci_usb_probe, + .remove = intel_xhci_usb_remove, +}; + +module_platform_driver(intel_xhci_usb_driver); + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("Intel XHCI USB role switch driver"); +MODULE_LICENSE("GPL"); -- 2.14.3