Received: by 2002:a25:8b91:0:0:0:0:0 with SMTP id j17csp14057351ybl; Mon, 30 Dec 2019 03:15:59 -0800 (PST) X-Google-Smtp-Source: APXvYqxm88uvesUgvHTHV9VkkCuO6dU8KlFH5WgZACJNnqh35093blC9OaSwFadi3Ko/Y6MYic8o X-Received: by 2002:a9d:6b17:: with SMTP id g23mr70941867otp.265.1577704559375; Mon, 30 Dec 2019 03:15:59 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1577704559; cv=none; d=google.com; s=arc-20160816; b=PDUMSWFvRoOgFDHUPasG8Sc1RLMihuD5XSkrPvXiBkWfsudQPBYbLF44dJbo0+Y/zP KctnZ0JHhBepWbtyYXNsVb453xri7uUDPR4Jv35xIB36c51ITqzgtUWBb3M+tw/nliKE kWOrg/obpkehFABGqaGJJDN2eA5FIDVk4oENyxREVT03omV/dM3Y8NXGjcGQ+1y7CfxO M6naNwyVv7L5Fc+RmTAV5Q/sJGMC5GcFhxc3xxysgjEBm9bivfwpaZnQTBj9CUJ5TacF NEN5MyF5cEYlqIJe8Xv15ms2j7CwBRp6BBWd9vbKiWCJ9qSlq4v+tLgMwG+Z9nEHAZwF fYjA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:dkim-signature:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from; bh=CWLWVkN0Jof9BSC/KWNkmy+1QH7B3d1pS9skdsKBiX4=; b=aNHzvMcXdNn2yk7J0WsjHGbz/J6nO1Al1XOa/PDCyFi1kHSUjWKDOrPQxF72LMDqwM zG3/FAo+FCyR6ZtM8T4Djeqd8M9ISOHmrSQWgu2kRoPoM/X24SK0rlYOAbx3YzNXIaZF yv4ZEqNdONPp5UK7AwMSiQ/tLKxx1lE8R90CgBxmg2NORSGciQ7ivNqb/EATnE3sCbHo b5c01+MFYrfsvisLdAoqG5jXZ20TyWIVWnsWdfkc63j+gfR6f+TIa0lgPiv2CAvrFfRJ 3yMlJVW3XfJWOIUHOdxQZ1q6Q1pSgWi+pYXQd6MNnW2P3DEkV6it8HcfCVKTWqeOkusK FpDA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@nvidia.com header.s=n1 header.b=OeQl7ZMy; 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=pass (p=NONE sp=NONE dis=NONE) header.from=nvidia.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id q126si20804280oia.8.2019.12.30.03.15.30; Mon, 30 Dec 2019 03:15:59 -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; dkim=pass header.i=@nvidia.com header.s=n1 header.b=OeQl7ZMy; 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=pass (p=NONE sp=NONE dis=NONE) header.from=nvidia.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727446AbfL3LOM (ORCPT + 99 others); Mon, 30 Dec 2019 06:14:12 -0500 Received: from hqnvemgate24.nvidia.com ([216.228.121.143]:15551 "EHLO hqnvemgate24.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727521AbfL3LOJ (ORCPT ); Mon, 30 Dec 2019 06:14:09 -0500 Received: from hqpgpgate102.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate24.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Mon, 30 Dec 2019 03:13:27 -0800 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate102.nvidia.com (PGP Universal service); Mon, 30 Dec 2019 03:14:07 -0800 X-PGP-Universal: processed; by hqpgpgate102.nvidia.com on Mon, 30 Dec 2019 03:14:07 -0800 Received: from HQMAIL107.nvidia.com (172.20.187.13) by HQMAIL101.nvidia.com (172.20.187.10) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Mon, 30 Dec 2019 11:14:06 +0000 Received: from hqnvemgw03.nvidia.com (10.124.88.68) by HQMAIL107.nvidia.com (172.20.187.13) with Microsoft SMTP Server (TLS) id 15.0.1473.3 via Frontend Transport; Mon, 30 Dec 2019 11:14:06 +0000 Received: from nkristam-ubuntu.nvidia.com (Not Verified[10.19.64.167]) by hqnvemgw03.nvidia.com with Trustwave SEG (v7,5,8,10121) id ; Mon, 30 Dec 2019 03:14:06 -0800 From: Nagarjuna Kristam To: , , , , , , CC: , , , , Nagarjuna Kristam Subject: [Patch V3 08/18] usb: xhci-tegra: Add OTG support Date: Mon, 30 Dec 2019 16:39:45 +0530 Message-ID: <1577704195-2535-9-git-send-email-nkristam@nvidia.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1577704195-2535-1-git-send-email-nkristam@nvidia.com> References: <1577704195-2535-1-git-send-email-nkristam@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 Content-Type: text/plain DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1577704407; bh=CWLWVkN0Jof9BSC/KWNkmy+1QH7B3d1pS9skdsKBiX4=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:X-NVConfidentiality:MIME-Version: Content-Type; b=OeQl7ZMyKxMrybSdfNKfzhqPrXhIUymEBhpWO0wwws68Er5UyEW5Mtl5NmdPhzwAK h7bb9K2uPsGjicmpzIfRvmAu4E6MEnJ5LEUU1lnPIq9r+TSuY2iVIywHr1cJ4V4ry0 Lim5BPQGFB1fFdAD3b5gGrhaj5PQGxUpQXrYbxZCMC2Vk9Ov430nyZASHsSD9kvdNC 5X3nUexwqXU0XQUusM3ev8rIwSOmj1mJzDZwRw2OSEr0pXKP3fED6Rkt+KnPUvtDWI ajfH4Sit0gqNlzDlWlqntfnGT2Ty7kX799e/LA1jfbxQcG08RzlYlZSZkvAfc1uEt5 B14uirLEcNj3g== Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Get usb-phy's for availbale USB 2 phys. Register id notifiers for available usb-phy's to receive role change notifications. Perform PP for the received role change usb ports. Signed-off-by: Nagarjuna Kristam --- V3: - No changes in this version --- V2: - Removed extra line before tegra_xusb_probe API. --- drivers/usb/host/xhci-tegra.c | 225 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 224 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 0b58ef3..22f1d36 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include #include "xhci.h" @@ -203,6 +206,7 @@ struct tegra_xusb_soc { bool scale_ss_clock; bool has_ipfs; + bool otg_reset_sspi; }; struct tegra_xusb_context { @@ -250,6 +254,14 @@ struct tegra_xusb { struct phy **phys; unsigned int num_phys; + struct usb_phy **usbphy; + unsigned int num_usb_phys; + int otg_usb2_port; + int otg_usb3_port; + bool host_mode; + struct notifier_block id_nb; + struct work_struct id_work; + /* Firmware loading related */ struct { size_t size; @@ -1081,6 +1093,205 @@ static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) return err; } +static void tegra_xhci_set_port_power(struct tegra_xusb *tegra, bool main, + bool set) +{ + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + struct usb_hcd *hcd = main ? xhci->main_hcd : xhci->shared_hcd; + int wait = (!main && !set) ? 1000 : 10; + u16 typeReq = set ? SetPortFeature : ClearPortFeature; + u16 wIndex = main ? tegra->otg_usb2_port + 1 : tegra->otg_usb3_port + 1; + u32 status; + u32 stat_power = main ? USB_PORT_STAT_POWER : USB_SS_PORT_STAT_POWER; + u32 status_val = set ? stat_power : 0; + + dev_dbg(tegra->dev, "%s:%s %s PP\n", __func__, set ? "set" : "clear", + main ? "HS" : "SS"); + + tegra_xhci_hc_driver.hub_control(hcd, typeReq, USB_PORT_FEAT_POWER, + wIndex, NULL, 0); + + do { + tegra_xhci_hc_driver.hub_control(hcd, GetPortStatus, 0, wIndex, + (char *) &status, sizeof(status)); + if (status_val == (status & stat_power)) + break; + + if (!main && !set) + usleep_range(600, 700); + else + usleep_range(10, 20); + } while (--wait > 0); + + if (status_val != (status & stat_power)) + dev_info(tegra->dev, "failed to %s %s PP %d\n", + set ? "set" : "clear", + main ? "HS" : "SS", status); +} + +static struct phy *tegra_xusb_get_phy(struct tegra_xusb *tegra, char *name, + int port) +{ + int i, phy_count = 0; + + for (i = 0; i < tegra->soc->num_types; i++) { + if (!strncmp(tegra->soc->phy_types[i].name, "usb2", + strlen(name))) + return tegra->phys[phy_count+port]; + + phy_count += tegra->soc->phy_types[i].num; + } + + return NULL; +} + +static void tegra_xhci_id_work(struct work_struct *work) +{ + struct tegra_xusb *tegra = container_of(work, struct tegra_xusb, + id_work); + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + struct tegra_xusb_mbox_msg msg; + struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", + tegra->otg_usb2_port); + u32 status; + int ret; + + dev_dbg(tegra->dev, "host mode %s\n", tegra->host_mode ? "on" : "off"); + + mutex_lock(&tegra->lock); + + if (tegra->host_mode) + phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST); + else + phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); + + mutex_unlock(&tegra->lock); + + if (tegra->host_mode) { + /* switch to host mode */ + if (tegra->otg_usb3_port >= 0) { + if (tegra->soc->otg_reset_sspi) { + /* set PP=0 */ + tegra_xhci_hc_driver.hub_control( + xhci->shared_hcd, GetPortStatus, + 0, tegra->otg_usb3_port+1, + (char *) &status, sizeof(status)); + if (status & USB_SS_PORT_STAT_POWER) + tegra_xhci_set_port_power(tegra, false, + false); + + /* reset OTG port SSPI */ + msg.cmd = MBOX_CMD_RESET_SSPI; + msg.data = tegra->otg_usb3_port+1; + + ret = tegra_xusb_mbox_send(tegra, &msg); + if (ret < 0) { + dev_info(tegra->dev, + "failed to RESET_SSPI %d\n", + ret); + } + } + + tegra_xhci_set_port_power(tegra, false, true); + } + + tegra_xhci_set_port_power(tegra, true, true); + + } else { + if (tegra->otg_usb3_port >= 0) + tegra_xhci_set_port_power(tegra, false, false); + + tegra_xhci_set_port_power(tegra, true, false); + } +} + +static int tegra_xusb_get_usb2_port(struct tegra_xusb *tegra, + struct usb_phy *usbphy) +{ + int i; + + for (i = 0; i < tegra->num_usb_phys; i++) { + if (tegra->usbphy[i] && usbphy == tegra->usbphy[i]) + return i; + } + + return -1; +} + +static int tegra_xhci_id_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct tegra_xusb *tegra = container_of(nb, struct tegra_xusb, + id_nb); + struct usb_phy *usbphy = (struct usb_phy *)data; + + dev_dbg(tegra->dev, "%s: action is %ld", __func__, action); + + if ((tegra->host_mode && action == USB_ROLE_HOST) || + (!tegra->host_mode && action != USB_ROLE_HOST)) { + dev_dbg(tegra->dev, "Same role(%d) received. Ignore", + tegra->host_mode); + return NOTIFY_OK; + } + + tegra->otg_usb2_port = tegra_xusb_get_usb2_port(tegra, usbphy); + tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion( + tegra->padctl, + tegra->otg_usb2_port); + + tegra->host_mode = (action == USB_ROLE_HOST) ? true : false; + + schedule_work(&tegra->id_work); + + return NOTIFY_OK; +} + +static int tegra_xusb_init_usb_phy(struct tegra_xusb *tegra) +{ + int i; + + tegra->usbphy = devm_kcalloc(tegra->dev, tegra->num_usb_phys, + sizeof(*tegra->usbphy), GFP_KERNEL); + if (!tegra->usbphy) + return -ENOMEM; + + INIT_WORK(&tegra->id_work, tegra_xhci_id_work); + tegra->id_nb.notifier_call = tegra_xhci_id_notify; + + for (i = 0; i < tegra->num_usb_phys; i++) { + struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", i); + + if (!phy) + continue; + + tegra->usbphy[i] = devm_usb_get_phy_by_node(tegra->dev, + phy->dev.of_node, + &tegra->id_nb); + if (!IS_ERR(tegra->usbphy[i])) { + dev_dbg(tegra->dev, "usbphy-%d registered", i); + otg_set_host(tegra->usbphy[i]->otg, &tegra->hcd->self); + } else { + /* + * usb-phy is optional, continue if its not available. + */ + tegra->usbphy[i] = NULL; + } + } + + return 0; +} + +static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra) +{ + int i; + + cancel_work_sync(&tegra->id_work); + + for (i = 0; i < tegra->num_usb_phys; i++) + if (tegra->usbphy[i]) + otg_set_host(tegra->usbphy[i]->otg, NULL); +} + static int tegra_xusb_probe(struct platform_device *pdev) { struct tegra_xusb *tegra; @@ -1254,8 +1465,11 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto put_powerdomains; } - for (i = 0; i < tegra->soc->num_types; i++) + for (i = 0; i < tegra->soc->num_types; i++) { + if (!strncmp(tegra->soc->phy_types[i].name, "usb2", 4)) + tegra->num_usb_phys = tegra->soc->phy_types[i].num; tegra->num_phys += tegra->soc->phy_types[i].num; + } tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys, sizeof(*tegra->phys), GFP_KERNEL); @@ -1384,6 +1598,12 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto remove_usb3; } + err = tegra_xusb_init_usb_phy(tegra); + if (err < 0) { + dev_err(&pdev->dev, "failed to Init usb-phy: %d\n", err); + goto remove_usb3; + } + return 0; remove_usb3: @@ -1420,6 +1640,8 @@ static int tegra_xusb_remove(struct platform_device *pdev) struct tegra_xusb *tegra = platform_get_drvdata(pdev); struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + tegra_xusb_deinit_usb_phy(tegra); + usb_remove_hcd(xhci->shared_hcd); usb_put_hcd(xhci->shared_hcd); xhci->shared_hcd = NULL; @@ -1733,6 +1955,7 @@ static const struct tegra_xusb_soc tegra210_soc = { }, .scale_ss_clock = false, .has_ipfs = true, + .otg_reset_sspi = true, .mbox = { .cmd = 0xe4, .data_in = 0xe8, -- 2.7.4