From patchwork Thu Apr 9 08:33:38 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Ivan T. Ivanov" X-Patchwork-Id: 6184271 X-Patchwork-Delegate: agross@codeaurora.org Return-Path: X-Original-To: patchwork-linux-arm-msm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 1A5A79F2EC for ; Thu, 9 Apr 2015 08:33:50 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D3AB8203EB for ; Thu, 9 Apr 2015 08:33:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3B5AC203DB for ; Thu, 9 Apr 2015 08:33:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932270AbbDIIdp (ORCPT ); Thu, 9 Apr 2015 04:33:45 -0400 Received: from mail-wi0-f176.google.com ([209.85.212.176]:38091 "EHLO mail-wi0-f176.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754878AbbDIIdn (ORCPT ); Thu, 9 Apr 2015 04:33:43 -0400 Received: by wiun10 with SMTP id n10so88654907wiu.1 for ; Thu, 09 Apr 2015 01:33:41 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=YY6+9lk2EzgeHS5H5tNR8RWQG3B5qc5Kgu1JQchmCJQ=; b=CvdY75X4cYBji7B3/0U/LwIXizw409vBvPcWmSYjcaaTdugfa2cqdUVZpOopyIz1kC JyWz5qVlIlbSuhJ996TI05ElM4lsqdQ/G3JYrsMLF3yc8XQAkxemtQ0F3MlYPwJi7g/5 Wja08sO984+aXA0k9FqTMknKkbCnB1ZjWIWrJv+kf/qIy0/Fqwpejofe3RkwDrEeccP+ +46y+CQHELOsoOmj/G6zZ9800vWpyS0EO8Z2x4djNipyRPmBVyhf/aIVEntB2GLbIeXw F0XC7EdACVEnok1DnbGrArYMKkgge7cwHr+EzYIvYvLXZGTasmR/cEd49ZOyuNjd9+bH pEgg== X-Gm-Message-State: ALoCoQlhBgY10u5Kdoa390u0qSgWAYmyFRG0j8bUzg6qay1pmxRntlJ6frNrfKXnR0IFQFhxd7u7 X-Received: by 10.180.87.66 with SMTP id v2mr3979132wiz.51.1428568421880; Thu, 09 Apr 2015 01:33:41 -0700 (PDT) Received: from localhost.localdomain ([37.157.136.206]) by mx.google.com with ESMTPSA id fa8sm19102154wib.14.2015.04.09.01.33.40 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 09 Apr 2015 01:33:41 -0700 (PDT) From: "Ivan T. Ivanov" To: Peter Chen Cc: Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Greg Kroah-Hartman , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, linux-arm-msm@vger.kernel.org Subject: [PATCH] usb: chipidea: Use extcon framework for VBUS and ID detection Date: Thu, 9 Apr 2015 11:33:38 +0300 Message-Id: <1428568418-22508-1-git-send-email-ivan.ivanov@linaro.org> X-Mailer: git-send-email 1.9.1 Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP On recent Qualcomm platforms VBUS and ID lines are not routed to USB PHY LINK controller. Use extcon framework to receive connect and disconnect ID and VBUS notification. Signed-off-by: Ivan T. Ivanov --- Suggestions for better solution are welcome! .../devicetree/bindings/usb/ci-hdrc-qcom.txt | 9 +++ drivers/usb/chipidea/Kconfig | 1 + drivers/usb/chipidea/ci.h | 18 +++++ drivers/usb/chipidea/core.c | 77 ++++++++++++++++++++++ drivers/usb/chipidea/otg.c | 19 +++++- 5 files changed, 123 insertions(+), 1 deletion(-) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-qcom.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-qcom.txt index f2899b5..788da49 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-qcom.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-qcom.txt @@ -7,6 +7,14 @@ Required properties: - usb-phy: phandle for the PHY device - dr_mode: Should be "peripheral" +Optional properties: +- extcon: phandles to external connector devices. First phandle + should point to external connector, which provide "USB" + cable events, the second should point to external connector + device, which provide "USB-HOST" cable events. If one of + the external connector devices is not required empty <0> + phandle should be specified. + Examples: gadget@f9a55000 { compatible = "qcom,ci-hdrc"; @@ -14,4 +22,5 @@ Examples: dr_mode = "peripheral"; interrupts = <0 134 0>; usb-phy = <&usbphy0>; + extcon = <&usb_vbus>, <&usb_id>; }; diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index 77b47d8..a67b67f 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig @@ -1,6 +1,7 @@ config USB_CHIPIDEA tristate "ChipIdea Highspeed Dual Role Controller" depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA + depends on EXTCON help Say Y here if your system has a dual role high speed USB controller based on ChipIdea silicon IP. Currently, only the diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 65913d4..04e7aee 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -13,6 +13,7 @@ #ifndef __DRIVERS_USB_CHIPIDEA_CI_H #define __DRIVERS_USB_CHIPIDEA_CI_H +#include #include #include #include @@ -132,6 +133,18 @@ struct hw_bank { }; /** + * struct ci_hdrc - structure for exteternal connector cable state tracking + * @state: current state of the line + * @nb: hold event notification callback + * @conn: used for notification registration + */ +struct ci_cable { + bool state; + struct notifier_block nb; + struct extcon_specific_cable_nb conn; +}; + +/** * struct ci_hdrc - chipidea device representation * @dev: pointer to parent device * @lock: access synchronization @@ -169,6 +182,8 @@ struct hw_bank { * @b_sess_valid_event: indicates there is a vbus event, and handled * at ci_otg_work * @imx28_write_fix: Freescale imx28 needs swp instruction for writing + * @vbus: VBUS signal state trakining, using extcon framework + * @id: ID signal state trakining, using extcon framework */ struct ci_hdrc { struct device *dev; @@ -211,6 +226,9 @@ struct ci_hdrc { bool id_event; bool b_sess_valid_event; bool imx28_write_fix; + + struct ci_cable vbus; + struct ci_cable id; }; static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index a57dc88..0f805bd 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -646,9 +647,36 @@ static void ci_get_otg_capable(struct ci_hdrc *ci) dev_dbg(ci->dev, "It is OTG capable controller\n"); } +static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct ci_cable *vbus = container_of(nb, struct ci_cable, nb); + + if (event) + vbus->state = true; + else + vbus->state = false; + + return NOTIFY_DONE; +} + +static int ci_host_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct ci_cable *id = container_of(nb, struct ci_cable, nb); + + if (event) + id->state = false; + else + id->state = true; + + return NOTIFY_DONE; +} + static int ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct extcon_dev *ext_vbus, *ext_id; struct ci_hdrc *ci; struct resource *res; void __iomem *base; @@ -702,6 +730,51 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci->usb_phy = NULL; } + ext_id = ERR_PTR(-ENODEV); + ext_vbus = ERR_PTR(-ENODEV); + if (of_property_read_bool(dev->parent->of_node, "extcon")) { + /* Each one of them is not mandatory */ + ext_vbus = extcon_get_edev_by_phandle(dev->parent, 0); + if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) + return PTR_ERR(ext_vbus); + + ext_id = extcon_get_edev_by_phandle(dev->parent, 1); + if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) + return PTR_ERR(ext_id); + } + + if (!IS_ERR(ext_vbus)) { + ci->vbus.nb.notifier_call = ci_vbus_notifier; + ret = extcon_register_interest(&ci->vbus.conn, ext_vbus->name, + "USB", &ci->vbus.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register VBUS failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_vbus, "USB"); + if (ret) + ci->vbus.state = true; + else + ci->vbus.state = false; + } + + if (!IS_ERR(ext_id)) { + ci->id.nb.notifier_call = ci_host_notifier; + ret = extcon_register_interest(&ci->id.conn, ext_id->name, + "USB-HOST", &ci->id.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register ID failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_id, "USB-HOST"); + if (ret) + ci->id.state = false; + else + ci->id.state = true; + } + ret = ci_usb_phy_init(ci); if (ret) { dev_err(dev, "unable to init phy: %d\n", ret); @@ -807,6 +880,10 @@ static int ci_hdrc_remove(struct platform_device *pdev) { struct ci_hdrc *ci = platform_get_drvdata(pdev); + if (ci->id.conn.edev) + extcon_unregister_interest(&ci->id.conn); + if (ci->vbus.conn.edev) + extcon_unregister_interest(&ci->vbus.conn); dbg_remove_files(ci); ci_role_destroy(ci); ci_hdrc_enter_lpm(ci, true); diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index a048b08..2e97c0d 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -30,7 +30,24 @@ */ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) { - return hw_read(ci, OP_OTGSC, mask); + u32 val = hw_read(ci, OP_OTGSC, mask); + + if ((mask & OTGSC_BSV) && ci->vbus.conn.edev) { + if (ci->vbus.state) + val |= OTGSC_BSV; + else + val &= ~OTGSC_BSV; + } + + if ((mask & OTGSC_ID) && ci->id.conn.edev) { + if (ci->id.state) + val |= OTGSC_ID; + else + val &= ~OTGSC_ID; + } + + val &= mask; + return val; } /**