From patchwork Mon Apr 27 09:50:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Chen X-Patchwork-Id: 11511759 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2719115E6 for ; Mon, 27 Apr 2020 09:50:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 07A03206D4 for ; Mon, 27 Apr 2020 09:50:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1587981055; bh=ZRueKNP17Zki6+Id+cJwVybkRSm6JcKCK9i7a4Hy2m4=; h=From:To:Cc:Subject:Date:List-ID:From; b=qjjOfte9WqDSl2f+/ve+8ixq2Koel+QYRM9Td1WsLHh/WzCkiqIkSC0j3uhK94E3A J3phUMAoIzSX8MSmxLBsLQDi2PynEw1mYgDBVQ9K8EMZJttcfnK8FX2phWu5VlDj24 h9vtSKTF4TGpx1vOuBtjNAc4jE9d7AiV79AyeUCo= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727015AbgD0Juy (ORCPT ); Mon, 27 Apr 2020 05:50:54 -0400 Received: from mail.kernel.org ([198.145.29.99]:56820 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727013AbgD0Jux (ORCPT ); Mon, 27 Apr 2020 05:50:53 -0400 Received: from localhost.localdomain (unknown [180.171.74.255]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 83AD820663; Mon, 27 Apr 2020 09:50:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1587981053; bh=ZRueKNP17Zki6+Id+cJwVybkRSm6JcKCK9i7a4Hy2m4=; h=From:To:Cc:Subject:Date:From; b=OR15UMrzXIBOxQc7tO123652BRY+C/bBpDsE+MjPD6EZUYd+m2nyEtn8Ln7GwED9x aeWDEjLweDYxhSN1uoQQmXT8J+nXIglyLVs6PrLy/tO3lOc81+wMsfuqOXRIX18VnU QAB7iOQB7z7sQrFHt3parYCDczBefEvDEfwcuG5E= From: Peter Chen To: linux-usb@vger.kernel.org Cc: linux-imx@nxp.com, Peter Chen Subject: [PATCH 1/4] usb: chipidea: introduce CI_HDRC_CONTROLLER_VBUS_EVENT glue layer use Date: Mon, 27 Apr 2020 17:50:36 +0800 Message-Id: <20200427095039.3833-1-peter.chen@kernel.org> X-Mailer: git-send-email 2.17.1 Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org From: Peter Chen Some vendors glue layer need to handle some events for vbus, eg, some i.mx platforms (imx7d, imx8mm, imx8mn, etc) needs vbus event to handle charger detection, its charger detection is finished at glue layer code, but not at USB PHY driver. Signed-off-by: Peter Chen --- drivers/usb/chipidea/udc.c | 7 ++++++- include/linux/usb/chipidea.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 921bcf14dc06..da70fbe7ca4c 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1561,6 +1561,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); unsigned long flags; + int ret = 0; spin_lock_irqsave(&ci->lock, flags); ci->vbus_active = is_active; @@ -1570,10 +1571,14 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) usb_phy_set_charger_state(ci->usb_phy, is_active ? USB_CHARGER_PRESENT : USB_CHARGER_ABSENT); + if (ci->platdata->notify_event) + ret = ci->platdata->notify_event(ci, + CI_HDRC_CONTROLLER_VBUS_EVENT); + if (ci->driver) ci_hdrc_gadget_connect(_gadget, is_active); - return 0; + return ret; } static int ci_udc_wakeup(struct usb_gadget *_gadget) diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index edd89b7c8f18..54167a2d28ea 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -67,6 +67,7 @@ struct ci_hdrc_platform_data { #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 #define CI_HDRC_IMX_HSIC_ACTIVE_EVENT 2 #define CI_HDRC_IMX_HSIC_SUSPEND_EVENT 3 +#define CI_HDRC_CONTROLLER_VBUS_EVENT 4 int (*notify_event) (struct ci_hdrc *ci, unsigned event); struct regulator *reg_vbus; struct usb_otg_caps ci_otg_caps; From patchwork Mon Apr 27 09:50:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Chen X-Patchwork-Id: 11511763 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 49711912 for ; Mon, 27 Apr 2020 09:50:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1E539206E9 for ; Mon, 27 Apr 2020 09:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1587981058; bh=7jMbIp6ldAcq2LwtGoY9LN591hek4ERPbJ0D9H3xuc8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=CLnILWoEYmAyZFlWrByyfDQL97upIubployJ1hZe5PGeCR4KetSGRNMPUm/m0i+KQ FloRjRI+n64vmQs6mvlRfMXc/8NM/mq+ZKwvwm4PssRiXxj/RNso2eXMslj+U565vY aGF06knf2OL+SurTJootaFQXtYsdvh7QgfT0vOeM= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727018AbgD0Ju5 (ORCPT ); Mon, 27 Apr 2020 05:50:57 -0400 Received: from mail.kernel.org ([198.145.29.99]:56842 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726485AbgD0Ju4 (ORCPT ); Mon, 27 Apr 2020 05:50:56 -0400 Received: from localhost.localdomain (unknown [180.171.74.255]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id D350F206B8; Mon, 27 Apr 2020 09:50:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1587981054; bh=7jMbIp6ldAcq2LwtGoY9LN591hek4ERPbJ0D9H3xuc8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RikueDAiyvGIPXZH45p5AnNlyQkmkB3x2ZqazjhhIzXgniT7zB+4+mU1+z+SVXRpr wTfjvqUPkR7zGC9OaEHqjXms3UVe2eVUXOwtc8cxFw3+VXZ+L52LFYslzd/LaH4S7Y 4RwsidhQOhFOo9yXMtJVOsCZfO5ggrN2G5LaPgFk= From: Peter Chen To: linux-usb@vger.kernel.org Cc: linux-imx@nxp.com, Jun Li , Peter Chen Subject: [PATCH 2/4] usb: chipidea: introduce imx7d USB charger detection Date: Mon, 27 Apr 2020 17:50:37 +0800 Message-Id: <20200427095039.3833-2-peter.chen@kernel.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200427095039.3833-1-peter.chen@kernel.org> References: <20200427095039.3833-1-peter.chen@kernel.org> Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org From: Jun Li imx7d (and imx8mm, imx8mn) uses Samsung PHY and USB generic PHY driver. The USB generic PHY driver is impossible to have a charger detection for every user, so we implement USB charger detection routine at glue layer. After the detection has finished, it will notify USB PHY charger framework, and the uevents will be triggered. Signed-off-by: Jun Li Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_imx.c | 13 +- drivers/usb/chipidea/ci_hdrc_imx.h | 2 + drivers/usb/chipidea/usbmisc_imx.c | 245 +++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index d8e7eb2f97b9..fb4097f02391 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -271,6 +271,7 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) struct device *dev = ci->dev->parent; struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); int ret = 0; + struct imx_usbmisc_data *mdata = data->usbmisc_data; switch (event) { case CI_HDRC_IMX_HSIC_ACTIVE_EVENT: @@ -284,11 +285,19 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) } break; case CI_HDRC_IMX_HSIC_SUSPEND_EVENT: - ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data); + ret = imx_usbmisc_hsic_set_connect(mdata); if (ret) dev_err(dev, "hsic_set_connect failed, err=%d\n", ret); break; + case CI_HDRC_CONTROLLER_VBUS_EVENT: + if (ci->vbus_active) + ret = imx_usbmisc_charger_detection(mdata, true); + else + ret = imx_usbmisc_charger_detection(mdata, false); + if (ci->usb_phy) + schedule_work(&ci->usb_phy->chg_work); + break; default: break; } @@ -415,6 +424,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) } pdata.usb_phy = data->phy; + if (data->usbmisc_data) + data->usbmisc_data->usb_phy = data->phy; if ((of_device_is_compatible(np, "fsl,imx53-usb") || of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy && diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index c2051aeba13f..727d02b6dbd3 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -24,6 +24,7 @@ struct imx_usbmisc_data { unsigned int hsic:1; /* HSIC controlller */ unsigned int ext_id:1; /* ID from exteranl event */ unsigned int ext_vbus:1; /* Vbus from exteranl event */ + struct usb_phy *usb_phy; }; int imx_usbmisc_init(struct imx_usbmisc_data *data); @@ -31,5 +32,6 @@ int imx_usbmisc_init_post(struct imx_usbmisc_data *data); int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled); int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data); int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on); +int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect); #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */ diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index e81e33c26e6c..8d7e78657e3d 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "ci_hdrc_imx.h" @@ -99,6 +100,32 @@ #define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1) #define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2) #define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3) +/* The default DM/DP value is pull-down */ +#define MX7D_USBNC_USB_CTRL2_OPMODE(v) (v << 6) +#define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING MX7D_USBNC_USB_CTRL2_OPMODE(1) +#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK (BIT(7) | BIT(6)) +#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN BIT(8) +#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_VAL BIT(12) +#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_EN BIT(13) +#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_VAL BIT(14) +#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_EN BIT(15) +#define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \ + BIT(14) | BIT(15)) + +#define MX7D_USB_OTG_PHY_CFG1 0x30 +#define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0) +#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1) +#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2) +#define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3) +#define MX7D_USB_OTG_PHY_CFG2_DRVVBUS0 BIT(16) + +#define MX7D_USB_OTG_PHY_CFG2 0x34 + +#define MX7D_USB_OTG_PHY_STATUS 0x3c +#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0) +#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1) +#define MX7D_USB_OTG_PHY_STATUS_VBUS_VLD BIT(3) +#define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29) #define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \ MX6_BM_ID_WAKEUP) @@ -114,6 +141,8 @@ struct usbmisc_ops { int (*hsic_set_connect)(struct imx_usbmisc_data *data); /* It's called during suspend/resume */ int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled); + /* usb charger detection */ + int (*charger_detection)(struct imx_usbmisc_data *data); }; struct imx_usbmisc { @@ -621,6 +650,188 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) return 0; } +static int imx7d_charger_secondary_detection(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + struct usb_phy *usb_phy = data->usb_phy; + int val; + unsigned long flags; + + /* VDM_SRC is connected to D- and IDP_SINK is connected to D+ */ + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 | + MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 | + MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL, + usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + usleep_range(1000, 2000); + + /* + * Per BC 1.2, check voltage of D+: + * DCP: if greater than VDAT_REF; + * CDP: if less than VDAT_REF. + */ + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS); + if (val & MX7D_USB_OTG_PHY_STATUS_CHRGDET) { + dev_dbg(data->dev, "It is a dedicate charging port\n"); + usb_phy->chg_type = DCP_TYPE; + } else { + dev_dbg(data->dev, "It is a charging downstream port\n"); + usb_phy->chg_type = CDP_TYPE; + } + + return 0; +} + +static void imx7_disable_charger_detector(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + val &= ~(MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB | + MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 | + MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 | + MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL); + writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + + /* Set OPMODE to be 2'b00 and disable its override */ + val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK; + writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2); + + val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + writel(val & ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + spin_unlock_irqrestore(&usbmisc->lock, flags); +} + +static int imx7d_charger_data_contact_detect(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + int i, data_pin_contact_count = 0; + + /* Enable Data Contact Detect (DCD) per the USB BC 1.2 */ + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB, + usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + for (i = 0; i < 100; i = i + 1) { + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS); + if (!(val & MX7D_USB_OTG_PHY_STATUS_LINE_STATE0)) { + if (data_pin_contact_count++ > 5) + /* Data pin makes contact */ + break; + usleep_range(5000, 10000); + } else { + data_pin_contact_count = 0; + usleep_range(5000, 6000); + } + } + + /* Disable DCD after finished data contact check */ + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + writel(val & ~MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB, + usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + if (i == 100) { + dev_err(data->dev, + "VBUS is coming from a dedicated power supply.\n"); + return -ENXIO; + } + + return 0; +} + +static int imx7d_charger_primary_detection(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + struct usb_phy *usb_phy = data->usb_phy; + unsigned long flags; + u32 val; + + /* VDP_SRC is connected to D+ and IDM_SINK is connected to D- */ + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + val &= ~MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL; + writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 | + MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0, + usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + usleep_range(1000, 2000); + + /* Check if D- is less than VDAT_REF to determine an SDP per BC 1.2 */ + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS); + if (!(val & MX7D_USB_OTG_PHY_STATUS_CHRGDET)) { + dev_dbg(data->dev, "It is a standard downstream port\n"); + usb_phy->chg_type = SDP_TYPE; + } + + return 0; +} + +/** + * Whole charger detection process: + * 1. OPMODE override to be non-driving + * 2. Data contact check + * 3. Primary detection + * 4. Secondary detection + * 5. Disable charger detection + */ +static int imx7d_charger_detection(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + struct usb_phy *usb_phy = data->usb_phy; + unsigned long flags; + u32 val; + int ret; + + /* Check if vbus is valid */ + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS); + if (!(val & MX7D_USB_OTG_PHY_STATUS_VBUS_VLD)) { + dev_err(data->dev, "vbus is error\n"); + return -EINVAL; + } + + /* + * Keep OPMODE to be non-driving mode during the whole + * charger detection process. + */ + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK; + val |= MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING; + writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2); + + val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + writel(val | MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + ret = imx7d_charger_data_contact_detect(data); + if (ret) + return ret; + + ret = imx7d_charger_primary_detection(data); + if (!ret && usb_phy->chg_type != SDP_TYPE) + ret = imx7d_charger_secondary_detection(data); + + imx7_disable_charger_detector(data); + + return ret; +} + static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, @@ -659,6 +870,7 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = { static const struct usbmisc_ops imx7d_usbmisc_ops = { .init = usbmisc_imx7d_init, .set_wakeup = usbmisc_imx7d_set_wakeup, + .charger_detection = imx7d_charger_detection, }; static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data) @@ -737,6 +949,39 @@ int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on) return usbmisc->ops->hsic_set_clk(data, on); } EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk); + +int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect) +{ + struct imx_usbmisc *usbmisc; + struct usb_phy *usb_phy; + int ret = 0; + + if (!data) + return -EINVAL; + + usbmisc = dev_get_drvdata(data->dev); + usb_phy = data->usb_phy; + if (!usbmisc->ops->charger_detection) + return -ENOTSUPP; + + if (connect) { + ret = usbmisc->ops->charger_detection(data); + if (ret) { + dev_err(data->dev, + "Error occurs during detection: %d\n", + ret); + usb_phy->chg_state = USB_CHARGER_ABSENT; + } else { + usb_phy->chg_state = USB_CHARGER_PRESENT; + } + } else { + usb_phy->chg_state = USB_CHARGER_ABSENT; + usb_phy->chg_type = UNKNOWN_TYPE; + } + return ret; +} +EXPORT_SYMBOL_GPL(imx_usbmisc_charger_detection); + static const struct of_device_id usbmisc_imx_dt_ids[] = { { .compatible = "fsl,imx25-usbmisc", From patchwork Mon Apr 27 09:50:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Chen X-Patchwork-Id: 11511761 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 90FE91667 for ; Mon, 27 Apr 2020 09:50:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 78B54206D9 for ; Mon, 27 Apr 2020 09:50:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1587981057; bh=759Xb4P31zPAbKj7+F8DbYBtMU8VCjJMFuzIuD/gvvM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=ZZlnnuoIHfhTiHEIszX4d2Qzcy+nl9PkRLBZZBrjeUufJu/k4VwBBrbGFO1o5T5It 4bkhg7V7vDWYyvhsmhiJ8tRO87TS1/95wGXZwW2Ce9spIxznd2y19mhI7nZoVzSjMP ZKyBNLhLX6Tvd9wV5ufZ1oI7vejRXYPRbWPDaWjo= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727017AbgD0Ju5 (ORCPT ); Mon, 27 Apr 2020 05:50:57 -0400 Received: from mail.kernel.org ([198.145.29.99]:56866 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727013AbgD0Ju4 (ORCPT ); Mon, 27 Apr 2020 05:50:56 -0400 Received: from localhost.localdomain (unknown [180.171.74.255]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 60E82206CD; Mon, 27 Apr 2020 09:50:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1587981056; bh=759Xb4P31zPAbKj7+F8DbYBtMU8VCjJMFuzIuD/gvvM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WuB8OAq9qVjfLvapcL9AOv76CzRlAvsTvQFDK/Q3QN//X0BYDkjlkHY+usEm/HcPg OoUZ9C/gmK+WjkQdkef6tHdouw0G6NG9QcNZvELBfEU1nWsOkxHWZBT0G41JK/17VS NV+pEopfYLvmqZxxh/3v0q4BXOID9Tg/kfKdf18I= From: Peter Chen To: linux-usb@vger.kernel.org Cc: linux-imx@nxp.com, Peter Chen Subject: [PATCH 3/4] usb: chipidea: pull down dp for possible charger detection operation Date: Mon, 27 Apr 2020 17:50:38 +0800 Message-Id: <20200427095039.3833-3-peter.chen@kernel.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200427095039.3833-1-peter.chen@kernel.org> References: <20200427095039.3833-1-peter.chen@kernel.org> Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org From: Peter Chen The bootloader may use device mode, and keep dp up. We need dp to be pulled down before possbile charger detection operation. Signed-off-by: Peter Chen --- drivers/usb/chipidea/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index ea8ac4a54a8d..804c0a5a213b 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1123,8 +1123,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (!ci_otg_is_fsm_mode(ci)) { /* only update vbus status for peripheral */ - if (ci->role == CI_ROLE_GADGET) + if (ci->role == CI_ROLE_GADGET) { + /* Pull down DP for possible charger detection */ + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); ci_handle_vbus_change(ci); + } ret = ci_role_start(ci, ci->role); if (ret) { From patchwork Mon Apr 27 09:50:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Chen X-Patchwork-Id: 11511765 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5B794912 for ; Mon, 27 Apr 2020 09:50:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 43794206B8 for ; Mon, 27 Apr 2020 09:50:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1587981059; bh=cbonv1gsYD8NJAIAIYbRXL+tSke9pTH0C+CVM5DdVdk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=Cc/ca+7tTnMC3CUWgJj7ba8x3lIvO3WXztZF6kS5B98S9Glf/gkhqvbhn+C0k1xGs FRBjedsKfiv36qwi9KwQrY1mNEpYsD42JekIybZ+kVyvC2ymaGuHkpWASLvB4zKJvg hoCAQUCT10awpKTeQo39c3WM0xDR/lLoSx6RWC2Y= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727021AbgD0Ju6 (ORCPT ); Mon, 27 Apr 2020 05:50:58 -0400 Received: from mail.kernel.org ([198.145.29.99]:56896 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726485AbgD0Ju6 (ORCPT ); Mon, 27 Apr 2020 05:50:58 -0400 Received: from localhost.localdomain (unknown [180.171.74.255]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id B2F8220663; Mon, 27 Apr 2020 09:50:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1587981057; bh=cbonv1gsYD8NJAIAIYbRXL+tSke9pTH0C+CVM5DdVdk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EYf3ZGsWCz5YBGgysmNKdhrji7zEkZO+kBGKtD2kE9JoGkniU44UBIgcVXZqDKwgO sKb8Qc4KF5RkSR111eseWgmfJBisIwPJSis1biOre1FHFSWGHu2qBUEGLVXililXUI z6P50viTniUXgfs8TXLd0C/UtUBuFnmQERZ0cSio= From: Peter Chen To: linux-usb@vger.kernel.org Cc: linux-imx@nxp.com, Peter Chen Subject: [PATCH 4/4] usb: chipidea: usbmisc_imx: using different ops for imx7d and imx7ulp Date: Mon, 27 Apr 2020 17:50:39 +0800 Message-Id: <20200427095039.3833-4-peter.chen@kernel.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200427095039.3833-1-peter.chen@kernel.org> References: <20200427095039.3833-1-peter.chen@kernel.org> Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org From: Peter Chen imx7ulp uses different USB PHY with imx7d (MXS PHY vs PICO PHY), so the features are supported by non-core register are a little different. For example, autoresume feature is supported by all controllers for imx7ulp, but for imx7d, it is only supported by non-HSIC controller. Besides, these two platforms use different HSIC controller, imx7ulp needs software operation, but imx7d doesn't. Signed-off-by: Peter Chen --- drivers/usb/chipidea/usbmisc_imx.c | 89 ++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 8d7e78657e3d..f136876cb4a3 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -100,6 +100,7 @@ #define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1) #define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2) #define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3) +#define MX7D_USBNC_AUTO_RESUME BIT(2) /* The default DM/DP value is pull-down */ #define MX7D_USBNC_USB_CTRL2_OPMODE(v) (v << 6) #define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING MX7D_USBNC_USB_CTRL2_OPMODE(1) @@ -638,10 +639,17 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) reg |= MX6_BM_PWR_POLARITY; writel(reg, usbmisc->base); - reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); - reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; - writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID, - usbmisc->base + MX7D_USBNC_USB_CTRL2); + /* SoC non-burst setting */ + reg = readl(usbmisc->base); + writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base); + + if (!data->hsic) { + reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; + writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID + | MX7D_USBNC_AUTO_RESUME, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + } spin_unlock_irqrestore(&usbmisc->lock, flags); @@ -832,6 +840,70 @@ static int imx7d_charger_detection(struct imx_usbmisc_data *data) return ret; } +static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 reg; + + if (data->index >= 1) + return -EINVAL; + + spin_lock_irqsave(&usbmisc->lock, flags); + reg = readl(usbmisc->base); + if (data->disable_oc) { + reg |= MX6_BM_OVER_CUR_DIS; + } else { + reg &= ~MX6_BM_OVER_CUR_DIS; + + /* + * If the polarity is not configured keep it as setup by the + * bootloader. + */ + if (data->oc_pol_configured && data->oc_pol_active_low) + reg |= MX6_BM_OVER_CUR_POLARITY; + else if (data->oc_pol_configured) + reg &= ~MX6_BM_OVER_CUR_POLARITY; + } + /* If the polarity is not set keep it as setup by the bootlader */ + if (data->pwr_pol == 1) + reg |= MX6_BM_PWR_POLARITY; + + writel(reg, usbmisc->base); + + /* SoC non-burst setting */ + reg = readl(usbmisc->base); + writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base); + + if (data->hsic) { + reg = readl(usbmisc->base); + writel(reg | MX6_BM_UTMI_ON_CLOCK, usbmisc->base); + + reg = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET); + reg |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON; + writel(reg, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET); + + /* + * For non-HSIC controller, the autoresume is enabled + * at MXS PHY driver (usbphy_ctrl bit18). + */ + reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + writel(reg | MX7D_USBNC_AUTO_RESUME, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + } else { + reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; + writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + } + + spin_unlock_irqrestore(&usbmisc->lock, flags); + + usbmisc_imx7d_set_wakeup(data, false); + + return 0; +} + static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, @@ -873,6 +945,13 @@ static const struct usbmisc_ops imx7d_usbmisc_ops = { .charger_detection = imx7d_charger_detection, }; +static const struct usbmisc_ops imx7ulp_usbmisc_ops = { + .init = usbmisc_imx7ulp_init, + .set_wakeup = usbmisc_imx7d_set_wakeup, + .hsic_set_connect = usbmisc_imx6_hsic_set_connect, + .hsic_set_clk = usbmisc_imx6_hsic_set_clk, +}; + static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); @@ -1025,7 +1104,7 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { }, { .compatible = "fsl,imx7ulp-usbmisc", - .data = &imx7d_usbmisc_ops, + .data = &imx7ulp_usbmisc_ops, }, { /* sentinel */ } };