Received: by 2002:a25:8b91:0:0:0:0:0 with SMTP id j17csp5392935ybl; Tue, 10 Dec 2019 05:24:31 -0800 (PST) X-Google-Smtp-Source: APXvYqyetOpsMvcThdQqQzWcmqPCKZXH/sbaFEodbossTRb+nsXRtIXKOkbABns3YBzyxYZjOfIT X-Received: by 2002:aca:fc06:: with SMTP id a6mr3688031oii.12.1575984271437; Tue, 10 Dec 2019 05:24:31 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1575984271; cv=none; d=google.com; s=arc-20160816; b=u7X+YPURtcfnGVug0g5oWf6EaYlQ2d44RNHHScKbrCK4wdDVp00vBz6oeDJy8TS+DK M+Y7Th945TWe1TwlJxF7JdhDJQSQxfryoF9ejG9w+kRWkvyE5/nk4QcDd/BiiHtqWZL8 TbXHXdIirOLRFCT4cQULOkEC3cW/ZF7/rh1k81onOqiEmnJOzQgsLQg8s8mABdh4efre kEw6GymjMdkAQesTTZgbf0AmqckviX+tXrMzGKeVGVuFEv6dQoqGphBBFH5yNn/u5Wta LlCOPy+ScFD51NUQl4FAyzpbna8CxxWD5GcI5RYSnjSyWTLRT056Gp7jUIT3PVrmeSZv 0qhA== 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:dkim-signature; bh=1dTOzVRNiurnChNy+Ai5qce135Oq+vulQrNRDSy+X7Q=; b=jcob5ru0nmzZKE576zGiRtVPLDa2tdFsMMyuDtGEDJl5bcNNUPqkoVqRBv/pLgn8uX doM2xiB+5L/E1nRdWHTVfeyMIIMBG8af3VOBX5dyki0PRpYiOhVLuQGyDxud8Los419T iMb7VJoRcvqDTnmg1l7wPnMVOMQe0FLqkYt2zIEKSqbfvAl5/a6cfkLo7PVUvp3RRDL/ rBkZBzDULDWbn/pq28F/kRNbkzsdAMdBpfr86W2Zo45aQTsKn21AvBP2Lu0qN1uwgL2+ TOO53ymLbWQAoCK8yAoxf3zmAQfEc1uxZbar6MagmXNm/yxyIvG21FXL5HEWEjwBXhG2 yLZw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=uOHDPrjY; 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=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id c12si1728345otp.310.2019.12.10.05.24.19; Tue, 10 Dec 2019 05:24:31 -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=@gmail.com header.s=20161025 header.b=uOHDPrjY; 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=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727519AbfLJNXk (ORCPT + 99 others); Tue, 10 Dec 2019 08:23:40 -0500 Received: from mail-wm1-f67.google.com ([209.85.128.67]:37198 "EHLO mail-wm1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727502AbfLJNXi (ORCPT ); Tue, 10 Dec 2019 08:23:38 -0500 Received: by mail-wm1-f67.google.com with SMTP id f129so3186255wmf.2; Tue, 10 Dec 2019 05:23:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=1dTOzVRNiurnChNy+Ai5qce135Oq+vulQrNRDSy+X7Q=; b=uOHDPrjYqs4UpGGjS0Q+EOTbMS6DGBFiKXi2uEevvoTa4/1tFGEX8tSx3Y/OLxACkG SfytP7esIiSpJgJWF5vLR5vb+BMZ5QDn94SuT0nGETvAnp/97aVq/jb5nX40RPfftspu /Ql+3ndqz5WNtNlbrdMq9txWyzxrgbSmTj6y3U9bqsK8DtaV5uzLZPqiarNVVuJo6DF4 S35EERaFXVkQNROkCYImaaKlrTBPFm64tzOEmxBtI0ueTuEnmv7Ps1x90mX+7Kre5aLw SSksL6E9JxTdx7aNkFvcN39O0I+j9eWFJ3CcnRmb6w36fVX2XbfgPp0m38+IEzr/Je0U VFHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=1dTOzVRNiurnChNy+Ai5qce135Oq+vulQrNRDSy+X7Q=; b=EkPE3iT3sDCXHs4zD3BNQH24KU7QJiWdcYZgkA1ZfDNJgO3m5432/WZ3eZ7sHZPXYz Wi2Y4VPneuZuiTLIwmEVqu1iCLKGL97sAtsJdkJqt2dzZHlphMny6xRaxwwOyaH2vGxl NLgUFoYuh+u5JCEC4DjrWbthKY8war1MwVDMKJfnYijVz6syMuKn6ra4eNwi4ceNvODH AdcPuCM/GIw3/S8vV8PQHvoeaktUengtw3AkVtUq6UGASf2Q/jMWyMSs+gXgGIrr0WE1 IN74qL0DhqyqaHOIcqtgTqlmY/p+oJNHPtp0iIDZbEm6ZPxKgG0OhNgL5U/bYet+dB6z qzGw== X-Gm-Message-State: APjAAAVX8oa8L0mvdvtHP5gJnaTSXrtEmszk1olz/aPOxgbXKQpOu8oh ZL+dZEHGbyeO6/smaguExS5Y/O/zaMw= X-Received: by 2002:a1c:2155:: with SMTP id h82mr5386287wmh.21.1575984216301; Tue, 10 Dec 2019 05:23:36 -0800 (PST) Received: from stbsrv-and-01.and.broadcom.net ([192.19.231.250]) by smtp.gmail.com with ESMTPSA id s82sm3101680wms.28.2019.12.10.05.23.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Dec 2019 05:23:35 -0800 (PST) From: Al Cooper To: linux-kernel@vger.kernel.org Cc: Al Cooper , bcm-kernel-feedback-list@broadcom.com, devicetree@vger.kernel.org, Florian Fainelli , Kishon Vijay Abraham I , linux-arm-kernel@lists.infradead.org, Mark Rutland , Rob Herring , Srinath Mannam Subject: [PATCH v3 04/13] phy: usb: Add "wake on" functionality Date: Tue, 10 Dec 2019 08:21:23 -0500 Message-Id: <20191210132132.41509-5-alcooperx@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191210132132.41509-1-alcooperx@gmail.com> References: <20191210132132.41509-1-alcooperx@gmail.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add the ability to handle USB wake events from USB devices when in S2 mode. Typically there is some additional configuration needed to tell the USB device to generate the wake event when suspended but this varies with the different USB device classes. For example, on USB Ethernet dongles, ethtool should be used to enable the magic packet wake functionality in the dongle. NOTE: This requires that the "power/wakeup" sysfs entry for the USB device generating the wakeup be set to "enabled". This functionality requires a special hardware sideband path that will trigger the AON_PM_L2 interrupt needed to wake the system from S2 even though the USB host controllers are in IDDQ (low power state) and most USB related clocks are shut off. For the sideband signaling to work we need to leave the usbx_freerun clock running, but this clock consumes very little power by design. There's a bug in the XHCI wake hardware so only EHCI/OHCI wake is currently supported. Signed-off-by: Al Cooper --- drivers/phy/broadcom/phy-brcm-usb-init.c | 17 +++++++++ drivers/phy/broadcom/phy-brcm-usb-init.h | 1 + drivers/phy/broadcom/phy-brcm-usb.c | 48 ++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index ac7f7995c11f..58882c10396a 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -58,6 +58,8 @@ #define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 /* option */ #define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK 0x30000000 /* option */ #define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK 0x00300000 /* option */ +#define USB_CTRL_USB_PM_RMTWKUP_EN_MASK 0x00000001 +#define USB_CTRL_USB_PM_STATUS 0x38 #define USB_CTRL_USB30_CTL1 0x60 #define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK 0x00000010 #define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK 0x00010000 @@ -855,6 +857,10 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params) u32 reg; void __iomem *ctrl = params->ctrl_regs; + /* Clear any pending wake conditions */ + reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS)); + brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_PM_STATUS)); + /* Take USB out of power down */ if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) { USB_CTRL_UNSET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN); @@ -1010,6 +1016,17 @@ void brcm_usb_uninit_xhci(struct brcm_usb_init_params *params) USB_CTRL_SET(params->ctrl_regs, USB30_PCTL, PHY3_IDDQ_OVERRIDE); } +void brcm_usb_wake_enable(struct brcm_usb_init_params *params, + int enable) +{ + void __iomem *ctrl = params->ctrl_regs; + + if (enable) + USB_CTRL_SET(ctrl, USB_PM, RMTWKUP_EN); + else + USB_CTRL_UNSET(ctrl, USB_PM, RMTWKUP_EN); +} + void brcm_usb_set_family_map(struct brcm_usb_init_params *params) { int fam; diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index f4f4f6d5d258..f473e0c51f0b 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -38,5 +38,6 @@ void brcm_usb_init_xhci(struct brcm_usb_init_params *ini); void brcm_usb_uninit_common(struct brcm_usb_init_params *ini); void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini); void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini); +void brcm_usb_wake_enable(struct brcm_usb_init_params *params, int enable); #endif /* _USB_BRCM_COMMON_INIT_H */ diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 634afc803778..cca04d60f2d2 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -57,11 +57,22 @@ struct brcm_usb_phy_data { bool has_xhci; struct clk *usb_20_clk; struct clk *usb_30_clk; + struct clk *suspend_clk; struct mutex mutex; /* serialize phy init */ int init_count; + int wake_irq; struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX]; }; +static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id) +{ + struct phy *gphy = dev_id; + + pm_wakeup_event(&gphy->dev, 0); + + return IRQ_HANDLED; +} + static int brcm_usb_phy_init(struct phy *gphy) { struct brcm_usb_phy *phy = phy_get_drvdata(gphy); @@ -76,6 +87,7 @@ static int brcm_usb_phy_init(struct phy *gphy) if (priv->init_count++ == 0) { clk_prepare_enable(priv->usb_20_clk); clk_prepare_enable(priv->usb_30_clk); + clk_prepare_enable(priv->suspend_clk); brcm_usb_init_common(&priv->ini); } mutex_unlock(&priv->mutex); @@ -108,6 +120,7 @@ static int brcm_usb_phy_exit(struct phy *gphy) brcm_usb_uninit_common(&priv->ini); clk_disable_unprepare(priv->usb_20_clk); clk_disable_unprepare(priv->usb_30_clk); + clk_disable_unprepare(priv->suspend_clk); } mutex_unlock(&priv->mutex); phy->inited = false; @@ -228,11 +241,12 @@ static const struct attribute_group brcm_usb_phy_group = { .attrs = brcm_usb_phy_attrs, }; -static int brcm_usb_phy_dvr_init(struct device *dev, +static int brcm_usb_phy_dvr_init(struct platform_device *pdev, struct brcm_usb_phy_data *priv, struct device_node *dn) { - struct phy *gphy; + struct device *dev = &pdev->dev; + struct phy *gphy = NULL; int err; priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb"); @@ -275,6 +289,28 @@ static int brcm_usb_phy_dvr_init(struct device *dev, if (err) return err; } + + priv->suspend_clk = clk_get(dev, "usb0_freerun"); + if (IS_ERR(priv->suspend_clk)) { + dev_err(dev, "Suspend Clock not found in Device Tree\n"); + priv->suspend_clk = NULL; + } + + priv->wake_irq = platform_get_irq_byname(pdev, "wake"); + if (priv->wake_irq < 0) + priv->wake_irq = platform_get_irq_byname(pdev, "wakeup"); + if (priv->wake_irq >= 0) { + err = devm_request_irq(dev, priv->wake_irq, + brcm_usb_phy_wake_isr, 0, + dev_name(dev), gphy); + if (err < 0) + return err; + device_set_wakeup_capable(dev, 1); + } else { + dev_info(dev, + "Wake interrupt missing, system wake not supported\n"); + } + return 0; } @@ -335,7 +371,7 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) if (of_property_read_bool(dn, "brcm,has-eohci")) priv->has_eohci = true; - err = brcm_usb_phy_dvr_init(dev, priv, dn); + err = brcm_usb_phy_dvr_init(pdev, priv, dn); if (err) return err; @@ -386,10 +422,13 @@ static int brcm_usb_phy_suspend(struct device *dev) if (priv->phys[BRCM_USB_PHY_2_0].inited) brcm_usb_uninit_eohci(&priv->ini); brcm_usb_uninit_common(&priv->ini); + brcm_usb_wake_enable(&priv->ini, true); if (priv->phys[BRCM_USB_PHY_3_0].inited) clk_disable_unprepare(priv->usb_30_clk); if (priv->phys[BRCM_USB_PHY_2_0].inited) clk_disable_unprepare(priv->usb_20_clk); + if (priv->wake_irq >= 0) + enable_irq_wake(priv->wake_irq); } return 0; } @@ -400,6 +439,7 @@ static int brcm_usb_phy_resume(struct device *dev) clk_prepare_enable(priv->usb_20_clk); clk_prepare_enable(priv->usb_30_clk); + brcm_usb_wake_enable(&priv->ini, false); brcm_usb_init_ipp(&priv->ini); /* @@ -407,6 +447,8 @@ static int brcm_usb_phy_resume(struct device *dev) * Uninitialize anything that wasn't previously initialized. */ if (priv->init_count) { + if (priv->wake_irq >= 0) + disable_irq_wake(priv->wake_irq); brcm_usb_init_common(&priv->ini); if (priv->phys[BRCM_USB_PHY_2_0].inited) { brcm_usb_init_eohci(&priv->ini); -- 2.17.1