* [PATCH v7 1/5] phy: Add support for Qualcomm's USB HSIC phy
2017-01-20 18:50 [PATCH v7 0/5] Support qcom's HSIC USB and rewrite USB2 HS support Stephen Boyd
@ 2017-01-20 18:50 ` Stephen Boyd
2017-01-20 18:50 ` [PATCH v7 2/5] usb: chipidea: msm: Configure phy for appropriate mode Stephen Boyd
` (3 subsequent siblings)
4 siblings, 0 replies; 12+ messages in thread
From: Stephen Boyd @ 2017-01-20 18:50 UTC (permalink / raw)
To: Peter Chen, Kishon Vijay Abraham I
Cc: linux-arm-kernel, linux-kernel, linux-arm-msm, Andy Gross,
Bjorn Andersson, Neil Armstrong, Arnd Bergmann, Felipe Balbi,
linux-usb, devicetree
The HSIC USB controller on qcom SoCs has an integrated all
digital phy controlled via the ULPI viewport.
Acked-by: Rob Herring <robh@kernel.org>
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
No changes.
.../devicetree/bindings/phy/qcom,usb-hsic-phy.txt | 65 +++++++++
drivers/phy/Kconfig | 7 +
drivers/phy/Makefile | 1 +
drivers/phy/phy-qcom-usb-hsic.c | 160 +++++++++++++++++++++
4 files changed, 233 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt
create mode 100644 drivers/phy/phy-qcom-usb-hsic.c
diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt b/Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt
new file mode 100644
index 000000000000..3c7cb2be4b12
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt
@@ -0,0 +1,65 @@
+Qualcomm's USB HSIC PHY
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Should contain "qcom,usb-hsic-phy" and more specifically one of the
+ following:
+
+ "qcom,usb-hsic-phy-mdm9615"
+ "qcom,usb-hsic-phy-msm8974"
+
+- #phy-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: Should contain 0
+
+- clocks:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain clock specifier for phy, calibration and
+ a calibration sleep clock
+
+- clock-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "phy, "cal" and "cal_sleep"
+
+- pinctrl-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "init" and "default" in that order
+
+- pinctrl-0:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: List of pinctrl settings to apply to keep HSIC pins in a glitch
+ free state
+
+- pinctrl-1:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: List of pinctrl settings to apply to mux out the HSIC pins
+
+EXAMPLE
+
+usb-controller {
+ ulpi {
+ phy {
+ compatible = "qcom,usb-hsic-phy-msm8974",
+ "qcom,usb-hsic-phy";
+ #phy-cells = <0>;
+ pinctrl-names = "init", "default";
+ pinctrl-0 = <&hsic_sleep>;
+ pinctrl-1 = <&hsic_default>;
+ clocks = <&gcc GCC_USB_HSIC_CLK>,
+ <&gcc GCC_USB_HSIC_IO_CAL_CLK>,
+ <&gcc GCC_USB_HSIC_IO_CAL_SLEEP_CLK>;
+ clock-names = "phy", "cal", "cal_sleep";
+ assigned-clocks = <&gcc GCC_USB_HSIC_IO_CAL_CLK>;
+ assigned-clock-rates = <960000>;
+ };
+ };
+};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index e8eb7f225a88..a430a64981d5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -437,6 +437,13 @@ config PHY_QCOM_UFS
help
Support for UFS PHY on QCOM chipsets.
+config PHY_QCOM_USB_HSIC
+ tristate "Qualcomm USB HSIC ULPI PHY module"
+ depends on USB_ULPI_BUS
+ select GENERIC_PHY
+ help
+ Support for the USB HSIC ULPI compliant PHY on QCOM chipsets.
+
config PHY_TUSB1210
tristate "TI TUSB1210 ULPI PHY module"
depends on USB_ULPI_BUS
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 65eb2f436a41..c43c9df5d301 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
+obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
diff --git a/drivers/phy/phy-qcom-usb-hsic.c b/drivers/phy/phy-qcom-usb-hsic.c
new file mode 100644
index 000000000000..47690f9945b9
--- /dev/null
+++ b/drivers/phy/phy-qcom-usb-hsic.c
@@ -0,0 +1,160 @@
+/**
+ * Copyright (C) 2016 Linaro Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/ulpi/driver.h>
+#include <linux/ulpi/regs.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinctrl-state.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include "ulpi_phy.h"
+
+#define ULPI_HSIC_CFG 0x30
+#define ULPI_HSIC_IO_CAL 0x33
+
+struct qcom_usb_hsic_phy {
+ struct ulpi *ulpi;
+ struct phy *phy;
+ struct pinctrl *pctl;
+ struct clk *phy_clk;
+ struct clk *cal_clk;
+ struct clk *cal_sleep_clk;
+};
+
+static int qcom_usb_hsic_phy_power_on(struct phy *phy)
+{
+ struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy);
+ struct ulpi *ulpi = uphy->ulpi;
+ struct pinctrl_state *pins_default;
+ int ret;
+
+ ret = clk_prepare_enable(uphy->phy_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(uphy->cal_clk);
+ if (ret)
+ goto err_cal;
+
+ ret = clk_prepare_enable(uphy->cal_sleep_clk);
+ if (ret)
+ goto err_sleep;
+
+ /* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */
+ ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff);
+ if (ret)
+ goto err_ulpi;
+
+ /* Enable periodic IO calibration in HSIC_CFG register */
+ ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8);
+ if (ret)
+ goto err_ulpi;
+
+ /* Configure pins for HSIC functionality */
+ pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(pins_default))
+ return PTR_ERR(pins_default);
+
+ ret = pinctrl_select_state(uphy->pctl, pins_default);
+ if (ret)
+ goto err_ulpi;
+
+ /* Enable HSIC mode in HSIC_CFG register */
+ ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01);
+ if (ret)
+ goto err_ulpi;
+
+ /* Disable auto-resume */
+ ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL),
+ ULPI_IFC_CTRL_AUTORESUME);
+ if (ret)
+ goto err_ulpi;
+
+ return ret;
+err_ulpi:
+ clk_disable_unprepare(uphy->cal_sleep_clk);
+err_sleep:
+ clk_disable_unprepare(uphy->cal_clk);
+err_cal:
+ clk_disable_unprepare(uphy->phy_clk);
+ return ret;
+}
+
+static int qcom_usb_hsic_phy_power_off(struct phy *phy)
+{
+ struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy);
+
+ clk_disable_unprepare(uphy->cal_sleep_clk);
+ clk_disable_unprepare(uphy->cal_clk);
+ clk_disable_unprepare(uphy->phy_clk);
+
+ return 0;
+}
+
+static const struct phy_ops qcom_usb_hsic_phy_ops = {
+ .power_on = qcom_usb_hsic_phy_power_on,
+ .power_off = qcom_usb_hsic_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi)
+{
+ struct qcom_usb_hsic_phy *uphy;
+ struct phy_provider *p;
+ struct clk *clk;
+
+ uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL);
+ if (!uphy)
+ return -ENOMEM;
+ ulpi_set_drvdata(ulpi, uphy);
+
+ uphy->ulpi = ulpi;
+ uphy->pctl = devm_pinctrl_get(&ulpi->dev);
+ if (IS_ERR(uphy->pctl))
+ return PTR_ERR(uphy->pctl);
+
+ uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node,
+ &qcom_usb_hsic_phy_ops);
+ if (IS_ERR(uphy->phy))
+ return PTR_ERR(uphy->phy);
+ phy_set_drvdata(uphy->phy, uphy);
+
+ p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate);
+ return PTR_ERR_OR_ZERO(p);
+}
+
+static const struct of_device_id qcom_usb_hsic_phy_match[] = {
+ { .compatible = "qcom,usb-hsic-phy", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match);
+
+static struct ulpi_driver qcom_usb_hsic_phy_driver = {
+ .probe = qcom_usb_hsic_phy_probe,
+ .driver = {
+ .name = "qcom_usb_hsic_phy",
+ .of_match_table = qcom_usb_hsic_phy_match,
+ },
+};
+module_ulpi_driver(qcom_usb_hsic_phy_driver);
+
+MODULE_DESCRIPTION("Qualcomm USB HSIC phy");
+MODULE_LICENSE("GPL v2");
--
2.10.0.297.gf6727b0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v7 2/5] usb: chipidea: msm: Configure phy for appropriate mode
2017-01-20 18:50 [PATCH v7 0/5] Support qcom's HSIC USB and rewrite USB2 HS support Stephen Boyd
2017-01-20 18:50 ` [PATCH v7 1/5] phy: Add support for Qualcomm's USB HSIC phy Stephen Boyd
@ 2017-01-20 18:50 ` Stephen Boyd
2017-01-22 2:10 ` Peter Chen
2017-01-20 18:50 ` [PATCH v7 3/5] phy: Add set_vbus callback Stephen Boyd
` (2 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Stephen Boyd @ 2017-01-20 18:50 UTC (permalink / raw)
To: Peter Chen, Kishon Vijay Abraham I
Cc: linux-arm-kernel, linux-kernel, linux-arm-msm, Andy Gross,
Bjorn Andersson, Neil Armstrong, Arnd Bergmann, Felipe Balbi,
linux-usb, Greg Kroah-Hartman
When the qcom chipidea controller is used with an extcon, we need
to signal device mode or host mode to the phy so it can configure
itself for the correct mode. This should be done after the phy is
powered up, so that the register writes work correctly. Add in
the appropriate phy_set_mode() call here.
To signal the correct state to the qcom glue driver we need to
change the ci->role before we do the role switch. Make sure to
undo the change if the role switch fails, but otherwise update
the state before calling the role start function so that the glue
driver knows what state to configure for.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
Made this msm specific because of how msm handles phy powerup.
drivers/usb/chipidea/ci.h | 7 +++++--
drivers/usb/chipidea/ci_hdrc_msm.c | 4 ++++
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 59e22389c10b..18348b0529af 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -268,6 +268,7 @@ static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role)
{
int ret;
+ enum ci_role prev_role;
if (role >= CI_ROLE_END)
return -EINVAL;
@@ -275,9 +276,11 @@ static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role)
if (!ci->roles[role])
return -ENXIO;
+ prev_role = ci->role;
+ ci->role = role;
ret = ci->roles[role]->start(ci);
- if (!ret)
- ci->role = role;
+ if (ret)
+ ci->role = prev_role;
return ret;
}
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index f1ede7909f54..9c58d13970ca 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -125,6 +125,10 @@ static int ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
hw_write(ci, OP_USBCMD, HSPHY_SESS_VLD_CTRL,
HSPHY_SESS_VLD_CTRL);
+ if (ci->role == CI_ROLE_GADGET)
+ phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE);
+ else if (ci->role == CI_ROLE_HOST)
+ phy_set_mode(ci->phy, PHY_MODE_USB_HOST);
}
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
--
2.10.0.297.gf6727b0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v7 2/5] usb: chipidea: msm: Configure phy for appropriate mode
2017-01-20 18:50 ` [PATCH v7 2/5] usb: chipidea: msm: Configure phy for appropriate mode Stephen Boyd
@ 2017-01-22 2:10 ` Peter Chen
0 siblings, 0 replies; 12+ messages in thread
From: Peter Chen @ 2017-01-22 2:10 UTC (permalink / raw)
To: Stephen Boyd
Cc: Peter Chen, Kishon Vijay Abraham I, linux-arm-kernel,
linux-kernel, linux-arm-msm, Andy Gross, Bjorn Andersson,
Neil Armstrong, Arnd Bergmann, Felipe Balbi, linux-usb,
Greg Kroah-Hartman
On Fri, Jan 20, 2017 at 10:50:54AM -0800, Stephen Boyd wrote:
> When the qcom chipidea controller is used with an extcon, we need
> to signal device mode or host mode to the phy so it can configure
> itself for the correct mode. This should be done after the phy is
> powered up, so that the register writes work correctly. Add in
> the appropriate phy_set_mode() call here.
>
> To signal the correct state to the qcom glue driver we need to
> change the ci->role before we do the role switch. Make sure to
> undo the change if the role switch fails, but otherwise update
> the state before calling the role start function so that the glue
> driver knows what state to configure for.
>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
> ---
>
> Made this msm specific because of how msm handles phy powerup.
>
> drivers/usb/chipidea/ci.h | 7 +++++--
> drivers/usb/chipidea/ci_hdrc_msm.c | 4 ++++
> 2 files changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
> index 59e22389c10b..18348b0529af 100644
> --- a/drivers/usb/chipidea/ci.h
> +++ b/drivers/usb/chipidea/ci.h
> @@ -268,6 +268,7 @@ static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
> static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role)
> {
> int ret;
> + enum ci_role prev_role;
>
> if (role >= CI_ROLE_END)
> return -EINVAL;
> @@ -275,9 +276,11 @@ static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role)
> if (!ci->roles[role])
> return -ENXIO;
>
> + prev_role = ci->role;
> + ci->role = role;
> ret = ci->roles[role]->start(ci);
> - if (!ret)
> - ci->role = role;
> + if (ret)
> + ci->role = prev_role;
> return ret;
Sorry, this changes ci->role's life cycle. You may try to get coming
role at your glue layer code (eg, through usbmode), or add your
changes at ci_role_start directly if the sequence can make your requirement.
Peter
> }
>
> diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
> index f1ede7909f54..9c58d13970ca 100644
> --- a/drivers/usb/chipidea/ci_hdrc_msm.c
> +++ b/drivers/usb/chipidea/ci_hdrc_msm.c
> @@ -125,6 +125,10 @@ static int ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
> hw_write(ci, OP_USBCMD, HSPHY_SESS_VLD_CTRL,
> HSPHY_SESS_VLD_CTRL);
>
> + if (ci->role == CI_ROLE_GADGET)
> + phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE);
> + else if (ci->role == CI_ROLE_HOST)
> + phy_set_mode(ci->phy, PHY_MODE_USB_HOST);
> }
> break;
> case CI_HDRC_CONTROLLER_STOPPED_EVENT:
> --
> 2.10.0.297.gf6727b0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Best Regards,
Peter Chen
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v7 3/5] phy: Add set_vbus callback
2017-01-20 18:50 [PATCH v7 0/5] Support qcom's HSIC USB and rewrite USB2 HS support Stephen Boyd
2017-01-20 18:50 ` [PATCH v7 1/5] phy: Add support for Qualcomm's USB HSIC phy Stephen Boyd
2017-01-20 18:50 ` [PATCH v7 2/5] usb: chipidea: msm: Configure phy for appropriate mode Stephen Boyd
@ 2017-01-20 18:50 ` Stephen Boyd
2017-01-22 1:34 ` Peter Chen
2017-01-22 8:46 ` Kishon Vijay Abraham I
2017-01-20 18:50 ` [PATCH v7 4/5] usb: chipidea: Signal vbus state to phy Stephen Boyd
2017-01-20 18:50 ` [PATCH v7 5/5] phy: Add support for Qualcomm's USB HS phy Stephen Boyd
4 siblings, 2 replies; 12+ messages in thread
From: Stephen Boyd @ 2017-01-20 18:50 UTC (permalink / raw)
To: Peter Chen, Kishon Vijay Abraham I
Cc: linux-arm-kernel, linux-kernel, linux-arm-msm, Andy Gross,
Bjorn Andersson, Neil Armstrong, Arnd Bergmann, Felipe Balbi,
linux-usb
Some USB PHYs need to be told about vbus changing state
explicitly. For example the qcom USB HS PHY needs to toggle a bit
when vbus goes from low to high (VBUSVLDEXT) to cause the
"session valid" signal to toggle. This signal will pull up D+
when the phy starts running. If the vbus signal isn't routed to
the PHY this "session valid" signal won't ever toggle, so we have
to toggle it explicitly. This callback is used to do that.
Cc: Peter Chen <peter.chen@nxp.com>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
New patch
drivers/phy/phy-core.c | 15 +++++++++++++++
include/linux/phy/phy.h | 10 ++++++++++
2 files changed, 25 insertions(+)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index a268f4d6f3e9..8b1a6bfa5133 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -357,6 +357,21 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
}
EXPORT_SYMBOL_GPL(phy_set_mode);
+int phy_set_vbus(struct phy *phy, int on)
+{
+ int ret;
+
+ if (!phy || !phy->ops->set_vbus)
+ return 0;
+
+ mutex_lock(&phy->mutex);
+ ret = phy->ops->set_vbus(phy, on);
+ mutex_unlock(&phy->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_set_vbus);
+
int phy_reset(struct phy *phy)
{
int ret;
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index 78bb0d7f6b11..4d1ebde7fb14 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -36,6 +36,7 @@ enum phy_mode {
* @power_on: powering on the phy
* @power_off: powering off the phy
* @set_mode: set the mode of the phy
+ * @set_vbus: enable/disable vbus in the phy (USB)
* @reset: resetting the phy
* @owner: the module owner containing the ops
*/
@@ -45,6 +46,7 @@ struct phy_ops {
int (*power_on)(struct phy *phy);
int (*power_off)(struct phy *phy);
int (*set_mode)(struct phy *phy, enum phy_mode mode);
+ int (*set_vbus)(struct phy *phy, int on);
int (*reset)(struct phy *phy);
struct module *owner;
};
@@ -138,6 +140,7 @@ int phy_exit(struct phy *phy);
int phy_power_on(struct phy *phy);
int phy_power_off(struct phy *phy);
int phy_set_mode(struct phy *phy, enum phy_mode mode);
+int phy_set_vbus(struct phy *phy, int on);
int phy_reset(struct phy *phy);
static inline int phy_get_bus_width(struct phy *phy)
{
@@ -253,6 +256,13 @@ static inline int phy_set_mode(struct phy *phy, enum phy_mode mode)
return -ENOSYS;
}
+static inline int phy_set_vbus(struct phy *phy, int on)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
static inline int phy_reset(struct phy *phy)
{
if (!phy)
--
2.10.0.297.gf6727b0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* RE: [PATCH v7 3/5] phy: Add set_vbus callback
2017-01-20 18:50 ` [PATCH v7 3/5] phy: Add set_vbus callback Stephen Boyd
@ 2017-01-22 1:34 ` Peter Chen
2017-01-22 8:46 ` Kishon Vijay Abraham I
1 sibling, 0 replies; 12+ messages in thread
From: Peter Chen @ 2017-01-22 1:34 UTC (permalink / raw)
To: Stephen Boyd, Kishon Vijay Abraham I
Cc: linux-arm-kernel, linux-kernel, linux-arm-msm, Andy Gross,
Bjorn Andersson, Neil Armstrong, Arnd Bergmann, Felipe Balbi,
linux-usb
> * @set_mode: set the mode of the phy
>+ * @set_vbus: enable/disable vbus in the phy (USB)
> * @reset: resetting the phy
> * @owner: the module owner containing the ops
> */
>@@ -45,6 +46,7 @@ struct phy_ops {
> int (*power_on)(struct phy *phy);
> int (*power_off)(struct phy *phy);
> int (*set_mode)(struct phy *phy, enum phy_mode mode);
>+ int (*set_vbus)(struct phy *phy, int on);
How about using the name notify_vbus? The main purpose of this API is notify
PHY vbus status.
Peter
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v7 3/5] phy: Add set_vbus callback
2017-01-20 18:50 ` [PATCH v7 3/5] phy: Add set_vbus callback Stephen Boyd
2017-01-22 1:34 ` Peter Chen
@ 2017-01-22 8:46 ` Kishon Vijay Abraham I
2017-01-23 19:58 ` Stephen Boyd
1 sibling, 1 reply; 12+ messages in thread
From: Kishon Vijay Abraham I @ 2017-01-22 8:46 UTC (permalink / raw)
To: Stephen Boyd, Peter Chen
Cc: linux-arm-kernel, linux-kernel, linux-arm-msm, Andy Gross,
Bjorn Andersson, Neil Armstrong, Arnd Bergmann, Felipe Balbi,
linux-usb
Hi,
On Saturday 21 January 2017 12:20 AM, Stephen Boyd wrote:
> Some USB PHYs need to be told about vbus changing state
> explicitly. For example the qcom USB HS PHY needs to toggle a bit
> when vbus goes from low to high (VBUSVLDEXT) to cause the
> "session valid" signal to toggle. This signal will pull up D+
> when the phy starts running. If the vbus signal isn't routed to
> the PHY this "session valid" signal won't ever toggle, so we have
> to toggle it explicitly. This callback is used to do that.
>
> Cc: Peter Chen <peter.chen@nxp.com>
> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
> ---
>
> New patch
>
> drivers/phy/phy-core.c | 15 +++++++++++++++
> include/linux/phy/phy.h | 10 ++++++++++
> 2 files changed, 25 insertions(+)
>
> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> index a268f4d6f3e9..8b1a6bfa5133 100644
> --- a/drivers/phy/phy-core.c
> +++ b/drivers/phy/phy-core.c
> @@ -357,6 +357,21 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
> }
> EXPORT_SYMBOL_GPL(phy_set_mode);
>
> +int phy_set_vbus(struct phy *phy, int on)
> +{
> + int ret;
> +
> + if (!phy || !phy->ops->set_vbus)
> + return 0;
> +
> + mutex_lock(&phy->mutex);
> + ret = phy->ops->set_vbus(phy, on);
> + mutex_unlock(&phy->mutex);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(phy_set_vbus);
> +
> int phy_reset(struct phy *phy)
> {
> int ret;
> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> index 78bb0d7f6b11..4d1ebde7fb14 100644
> --- a/include/linux/phy/phy.h
> +++ b/include/linux/phy/phy.h
> @@ -36,6 +36,7 @@ enum phy_mode {
> * @power_on: powering on the phy
> * @power_off: powering off the phy
> * @set_mode: set the mode of the phy
> + * @set_vbus: enable/disable vbus in the phy (USB)
> * @reset: resetting the phy
> * @owner: the module owner containing the ops
> */
> @@ -45,6 +46,7 @@ struct phy_ops {
> int (*power_on)(struct phy *phy);
> int (*power_off)(struct phy *phy);
> int (*set_mode)(struct phy *phy, enum phy_mode mode);
> + int (*set_vbus)(struct phy *phy, int on);
please avoid adding usb specific ops in generic phy ops.
Thanks
Kishon
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v7 3/5] phy: Add set_vbus callback
2017-01-22 8:46 ` Kishon Vijay Abraham I
@ 2017-01-23 19:58 ` Stephen Boyd
2017-01-24 9:23 ` Kishon Vijay Abraham I
0 siblings, 1 reply; 12+ messages in thread
From: Stephen Boyd @ 2017-01-23 19:58 UTC (permalink / raw)
To: Kishon Vijay Abraham I, Peter Chen
Cc: linux-arm-kernel, linux-kernel, linux-arm-msm, Andy Gross,
Bjorn Andersson, Neil Armstrong, Arnd Bergmann, Felipe Balbi,
linux-usb
Quoting Kishon Vijay Abraham I (2017-01-22 00:46:21)
> Hi,
>
> On Saturday 21 January 2017 12:20 AM, Stephen Boyd wrote:
> > Some USB PHYs need to be told about vbus changing state
> > explicitly. For example the qcom USB HS PHY needs to toggle a bit
> > when vbus goes from low to high (VBUSVLDEXT) to cause the
> > "session valid" signal to toggle. This signal will pull up D+
> > when the phy starts running. If the vbus signal isn't routed to
> > the PHY this "session valid" signal won't ever toggle, so we have
> > to toggle it explicitly. This callback is used to do that.
> >
> > Cc: Peter Chen <peter.chen@nxp.com>
> > Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
> > ---
> >
> > New patch
> >
> > drivers/phy/phy-core.c | 15 +++++++++++++++
> > include/linux/phy/phy.h | 10 ++++++++++
> > 2 files changed, 25 insertions(+)
> >
> > diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> > index a268f4d6f3e9..8b1a6bfa5133 100644
> > --- a/drivers/phy/phy-core.c
> > +++ b/drivers/phy/phy-core.c
> > @@ -357,6 +357,21 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
> > }
> > EXPORT_SYMBOL_GPL(phy_set_mode);
> >
> > +int phy_set_vbus(struct phy *phy, int on)
> > +{
> > + int ret;
> > +
> > + if (!phy || !phy->ops->set_vbus)
> > + return 0;
> > +
> > + mutex_lock(&phy->mutex);
> > + ret = phy->ops->set_vbus(phy, on);
> > + mutex_unlock(&phy->mutex);
> > +
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(phy_set_vbus);
> > +
> > int phy_reset(struct phy *phy)
> > {
> > int ret;
> > diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> > index 78bb0d7f6b11..4d1ebde7fb14 100644
> > --- a/include/linux/phy/phy.h
> > +++ b/include/linux/phy/phy.h
> > @@ -36,6 +36,7 @@ enum phy_mode {
> > * @power_on: powering on the phy
> > * @power_off: powering off the phy
> > * @set_mode: set the mode of the phy
> > + * @set_vbus: enable/disable vbus in the phy (USB)
> > * @reset: resetting the phy
> > * @owner: the module owner containing the ops
> > */
> > @@ -45,6 +46,7 @@ struct phy_ops {
> > int (*power_on)(struct phy *phy);
> > int (*power_off)(struct phy *phy);
> > int (*set_mode)(struct phy *phy, enum phy_mode mode);
> > + int (*set_vbus)(struct phy *phy, int on);
>
> please avoid adding usb specific ops in generic phy ops.
>
Is there any alternative? Something has to happen here.
The only other thing I can think of is putting back the vbus extcon in
the phy driver so it can be notified when vbus is present or not. I can
rely on phy_set_mode() being called so that we don't need to figure out
if we're in host mode or not like was being done before.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v7 3/5] phy: Add set_vbus callback
2017-01-23 19:58 ` Stephen Boyd
@ 2017-01-24 9:23 ` Kishon Vijay Abraham I
0 siblings, 0 replies; 12+ messages in thread
From: Kishon Vijay Abraham I @ 2017-01-24 9:23 UTC (permalink / raw)
To: Stephen Boyd, Peter Chen
Cc: linux-arm-kernel, linux-kernel, linux-arm-msm, Andy Gross,
Bjorn Andersson, Neil Armstrong, Arnd Bergmann, Felipe Balbi,
linux-usb
Hi,
On Tuesday 24 January 2017 01:28 AM, Stephen Boyd wrote:
> Quoting Kishon Vijay Abraham I (2017-01-22 00:46:21)
>> Hi,
>>
>> On Saturday 21 January 2017 12:20 AM, Stephen Boyd wrote:
>>> Some USB PHYs need to be told about vbus changing state
>>> explicitly. For example the qcom USB HS PHY needs to toggle a bit
>>> when vbus goes from low to high (VBUSVLDEXT) to cause the
>>> "session valid" signal to toggle. This signal will pull up D+
>>> when the phy starts running. If the vbus signal isn't routed to
>>> the PHY this "session valid" signal won't ever toggle, so we have
>>> to toggle it explicitly. This callback is used to do that.
>>>
>>> Cc: Peter Chen <peter.chen@nxp.com>
>>> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
>>> ---
>>>
>>> New patch
>>>
>>> drivers/phy/phy-core.c | 15 +++++++++++++++
>>> include/linux/phy/phy.h | 10 ++++++++++
>>> 2 files changed, 25 insertions(+)
>>>
>>> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
>>> index a268f4d6f3e9..8b1a6bfa5133 100644
>>> --- a/drivers/phy/phy-core.c
>>> +++ b/drivers/phy/phy-core.c
>>> @@ -357,6 +357,21 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
>>> }
>>> EXPORT_SYMBOL_GPL(phy_set_mode);
>>>
>>> +int phy_set_vbus(struct phy *phy, int on)
>>> +{
>>> + int ret;
>>> +
>>> + if (!phy || !phy->ops->set_vbus)
>>> + return 0;
>>> +
>>> + mutex_lock(&phy->mutex);
>>> + ret = phy->ops->set_vbus(phy, on);
>>> + mutex_unlock(&phy->mutex);
>>> +
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(phy_set_vbus);
>>> +
>>> int phy_reset(struct phy *phy)
>>> {
>>> int ret;
>>> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
>>> index 78bb0d7f6b11..4d1ebde7fb14 100644
>>> --- a/include/linux/phy/phy.h
>>> +++ b/include/linux/phy/phy.h
>>> @@ -36,6 +36,7 @@ enum phy_mode {
>>> * @power_on: powering on the phy
>>> * @power_off: powering off the phy
>>> * @set_mode: set the mode of the phy
>>> + * @set_vbus: enable/disable vbus in the phy (USB)
>>> * @reset: resetting the phy
>>> * @owner: the module owner containing the ops
>>> */
>>> @@ -45,6 +46,7 @@ struct phy_ops {
>>> int (*power_on)(struct phy *phy);
>>> int (*power_off)(struct phy *phy);
>>> int (*set_mode)(struct phy *phy, enum phy_mode mode);
>>> + int (*set_vbus)(struct phy *phy, int on);
>>
>> please avoid adding usb specific ops in generic phy ops.
>>
>
> Is there any alternative? Something has to happen here.
>
> The only other thing I can think of is putting back the vbus extcon in
> the phy driver so it can be notified when vbus is present or not. I can
I think extcon should be used here to get vbus notification.
Thanks
Kishon
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v7 4/5] usb: chipidea: Signal vbus state to phy
2017-01-20 18:50 [PATCH v7 0/5] Support qcom's HSIC USB and rewrite USB2 HS support Stephen Boyd
` (2 preceding siblings ...)
2017-01-20 18:50 ` [PATCH v7 3/5] phy: Add set_vbus callback Stephen Boyd
@ 2017-01-20 18:50 ` Stephen Boyd
2017-01-22 1:37 ` Peter Chen
2017-01-20 18:50 ` [PATCH v7 5/5] phy: Add support for Qualcomm's USB HS phy Stephen Boyd
4 siblings, 1 reply; 12+ messages in thread
From: Stephen Boyd @ 2017-01-20 18:50 UTC (permalink / raw)
To: Peter Chen, Kishon Vijay Abraham I
Cc: linux-arm-kernel, linux-kernel, linux-arm-msm, Andy Gross,
Bjorn Andersson, Neil Armstrong, Arnd Bergmann, Felipe Balbi,
linux-usb, Greg Kroah-Hartman
Some USB PHYs need to be told about vbus changing state
explicitly. For example the qcom USB HS PHY needs to toggle a bit
when vbus goes from low to high (VBUSVLDEXT) to cause the
"session valid" signal to toggle. This signal will pull up D+
when the phy starts running. Add the appropriate phy_set_vbus()
call here to signal vbus state changes to the phy.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
New patch
drivers/usb/chipidea/otg.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 10236fe71522..6ea702beed48 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -134,10 +134,13 @@ void ci_handle_vbus_change(struct ci_hdrc *ci)
if (!ci->is_otg)
return;
- if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
+ if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active) {
usb_gadget_vbus_connect(&ci->gadget);
- else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
+ phy_set_vbus(ci->phy, 1);
+ } else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active) {
+ phy_set_vbus(ci->phy, 0);
usb_gadget_vbus_disconnect(&ci->gadget);
+ }
}
/**
--
2.10.0.297.gf6727b0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* RE: [PATCH v7 4/5] usb: chipidea: Signal vbus state to phy
2017-01-20 18:50 ` [PATCH v7 4/5] usb: chipidea: Signal vbus state to phy Stephen Boyd
@ 2017-01-22 1:37 ` Peter Chen
0 siblings, 0 replies; 12+ messages in thread
From: Peter Chen @ 2017-01-22 1:37 UTC (permalink / raw)
To: Stephen Boyd, Kishon Vijay Abraham I
Cc: linux-arm-kernel, linux-kernel, linux-arm-msm, Andy Gross,
Bjorn Andersson, Neil Armstrong, Arnd Bergmann, Felipe Balbi,
linux-usb, Greg Kroah-Hartman
>
> drivers/usb/chipidea/otg.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
>diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index
>10236fe71522..6ea702beed48 100644
>--- a/drivers/usb/chipidea/otg.c
>+++ b/drivers/usb/chipidea/otg.c
>@@ -134,10 +134,13 @@ void ci_handle_vbus_change(struct ci_hdrc *ci)
> if (!ci->is_otg)
> return;
>
>- if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
>+ if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active) {
> usb_gadget_vbus_connect(&ci->gadget);
>- else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
>+ phy_set_vbus(ci->phy, 1);
>+ } else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active) {
>+ phy_set_vbus(ci->phy, 0);
> usb_gadget_vbus_disconnect(&ci->gadget);
>+ }
> }
>
You can add my ack if you accept the name of phy_notify_vbus.
Peter
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v7 5/5] phy: Add support for Qualcomm's USB HS phy
2017-01-20 18:50 [PATCH v7 0/5] Support qcom's HSIC USB and rewrite USB2 HS support Stephen Boyd
` (3 preceding siblings ...)
2017-01-20 18:50 ` [PATCH v7 4/5] usb: chipidea: Signal vbus state to phy Stephen Boyd
@ 2017-01-20 18:50 ` Stephen Boyd
4 siblings, 0 replies; 12+ messages in thread
From: Stephen Boyd @ 2017-01-20 18:50 UTC (permalink / raw)
To: Peter Chen, Kishon Vijay Abraham I
Cc: linux-arm-kernel, linux-kernel, linux-arm-msm, Andy Gross,
Bjorn Andersson, Neil Armstrong, Arnd Bergmann, Felipe Balbi,
linux-usb, devicetree
The high-speed phy on qcom SoCs is controlled via the ULPI
viewport.
Cc: <devicetree@vger.kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
---
phy_set_mode() hook split up to toggle two bits independently
with new set_vbus() hook.
.../devicetree/bindings/phy/qcom,usb-hs-phy.txt | 78 +++++++
drivers/phy/Kconfig | 8 +
drivers/phy/Makefile | 1 +
drivers/phy/phy-qcom-usb-hs.c | 256 +++++++++++++++++++++
4 files changed, 343 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt
create mode 100644 drivers/phy/phy-qcom-usb-hs.c
diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt b/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt
new file mode 100644
index 000000000000..bec77a74bd39
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt
@@ -0,0 +1,78 @@
+Qualcomm's USB HS PHY
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Should contain "qcom,usb-hs-phy" and more specifically one of the
+ following:
+
+ "qcom,usb-hs-phy-apq8064"
+ "qcom,usb-hs-phy-msm8916"
+ "qcom,usb-hs-phy-msm8974"
+
+- #phy-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: Should contain 0
+
+- clocks:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain clock specifier for the reference and sleep
+ clocks
+
+- clock-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "ref" and "sleep" for the reference and sleep
+ clocks respectively
+
+- resets:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain the phy and POR resets
+
+- reset-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "phy" and "por" for the phy and POR resets
+ respectively
+
+- v3p3-supply:
+ Usage: required
+ Value type: <phandle>
+ Definition: Should contain a reference to the 3.3V supply
+
+- v1p8-supply:
+ Usage: required
+ Value type: <phandle>
+ Definition: Should contain a reference to the 1.8V supply
+
+- qcom,init-seq:
+ Usage: optional
+ Value type: <u8 array>
+ Definition: Should contain a sequence of ULPI address and value pairs to
+ program into the ULPI_EXT_VENDOR_SPECIFIC area. This is related
+ to Device Mode Eye Diagram test. The addresses are offsets
+ from the ULPI_EXT_VENDOR_SPECIFIC address, for example,
+ <0x1 0x53> would mean "write the value 0x53 to address 0x81".
+
+EXAMPLE
+
+otg: usb-controller {
+ ulpi {
+ phy {
+ compatible = "qcom,usb-hs-phy-msm8974", "qcom,usb-hs-phy";
+ #phy-cells = <0>;
+ clocks = <&xo_board>, <&gcc GCC_USB2A_PHY_SLEEP_CLK>;
+ clock-names = "ref", "sleep";
+ resets = <&gcc GCC_USB2A_PHY_BCR>, <&otg 0>;
+ reset-names = "phy", "por";
+ v3p3-supply = <&pm8941_l24>;
+ v1p8-supply = <&pm8941_l6>;
+ qcom,init-seq = /bits/ 8 <0x1 0x63>;
+ };
+ };
+};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index a430a64981d5..61a22e985831 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -437,6 +437,14 @@ config PHY_QCOM_UFS
help
Support for UFS PHY on QCOM chipsets.
+config PHY_QCOM_USB_HS
+ tristate "Qualcomm USB HS PHY module"
+ depends on USB_ULPI_BUS
+ select GENERIC_PHY
+ help
+ Support for the USB high-speed ULPI compliant phy on Qualcomm
+ chipsets.
+
config PHY_QCOM_USB_HSIC
tristate "Qualcomm USB HSIC ULPI PHY module"
depends on USB_ULPI_BUS
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index c43c9df5d301..0e4259473d28 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
+obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o
obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o
diff --git a/drivers/phy/phy-qcom-usb-hs.c b/drivers/phy/phy-qcom-usb-hs.c
new file mode 100644
index 000000000000..50cb40977737
--- /dev/null
+++ b/drivers/phy/phy-qcom-usb-hs.c
@@ -0,0 +1,256 @@
+/**
+ * Copyright (C) 2016 Linaro Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/ulpi/driver.h>
+#include <linux/ulpi/regs.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+#include <linux/usb/of.h>
+
+#include "ulpi_phy.h"
+
+#define ULPI_PWR_CLK_MNG_REG 0x88
+# define ULPI_PWR_OTG_COMP_DISABLE BIT(0)
+
+#define ULPI_MISC_A 0x96
+# define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1)
+# define ULPI_MISC_A_VBUSVLDEXT BIT(0)
+
+
+struct ulpi_seq {
+ u8 addr;
+ u8 val;
+};
+
+struct qcom_usb_hs_phy {
+ struct ulpi *ulpi;
+ struct phy *phy;
+ struct clk *ref_clk;
+ struct clk *sleep_clk;
+ struct regulator *v1p8;
+ struct regulator *v3p3;
+ struct reset_control *reset;
+ struct ulpi_seq *init_seq;
+ enum usb_dr_mode dr_mode;
+};
+
+static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode)
+{
+ struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
+ u8 addr, val = 0;
+ int ret;
+
+ switch (mode) {
+ case PHY_MODE_USB_OTG:
+ switch (uphy->dr_mode) {
+ case USB_DR_MODE_OTG:
+ val |= ULPI_INT_IDGRD;
+ case USB_DR_MODE_PERIPHERAL:
+ val |= ULPI_INT_SESS_VALID;
+ default:
+ break;
+ }
+
+ ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val);
+ if (ret)
+ return ret;
+ return ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val);
+ case PHY_MODE_USB_DEVICE:
+ addr = ULPI_SET(ULPI_MISC_A);
+ break;
+ case PHY_MODE_USB_HOST:
+ addr = ULPI_CLR(ULPI_MISC_A);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG),
+ ULPI_PWR_OTG_COMP_DISABLE);
+ return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXTSEL);
+}
+
+static int qcom_usb_hs_phy_set_vbus(struct phy *phy, int on)
+{
+ struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
+ u8 addr, val;
+ int ret;
+
+ if (on)
+ addr = ULPI_SET(ULPI_MISC_A);
+ else
+ addr = ULPI_CLR(ULPI_MISC_A);
+
+ return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT);
+}
+
+static int qcom_usb_hs_phy_power_on(struct phy *phy)
+{
+ struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
+ struct ulpi *ulpi = uphy->ulpi;
+ const struct ulpi_seq *seq;
+ int ret;
+
+ ret = clk_prepare_enable(uphy->ref_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(uphy->sleep_clk);
+ if (ret)
+ goto err_sleep;
+
+ ret = regulator_set_load(uphy->v1p8, 50000);
+ if (ret < 0)
+ goto err_1p8;
+
+ ret = regulator_enable(uphy->v1p8);
+ if (ret)
+ goto err_1p8;
+
+ ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000,
+ 3300000);
+ if (ret)
+ goto err_3p3;
+
+ ret = regulator_set_load(uphy->v3p3, 50000);
+ if (ret < 0)
+ goto err_3p3;
+
+ ret = regulator_enable(uphy->v3p3);
+ if (ret)
+ goto err_3p3;
+
+ for (seq = uphy->init_seq; seq->addr; seq++) {
+ ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr,
+ seq->val);
+ if (ret)
+ goto err_ulpi;
+ }
+
+ if (uphy->reset) {
+ ret = reset_control_reset(uphy->reset);
+ if (ret)
+ goto err_ulpi;
+ }
+
+ return 0;
+err_ulpi:
+ regulator_disable(uphy->v3p3);
+err_3p3:
+ regulator_disable(uphy->v1p8);
+err_1p8:
+ clk_disable_unprepare(uphy->sleep_clk);
+err_sleep:
+ clk_disable_unprepare(uphy->ref_clk);
+ return ret;
+}
+
+static int qcom_usb_hs_phy_power_off(struct phy *phy)
+{
+ struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
+
+ regulator_disable(uphy->v3p3);
+ regulator_disable(uphy->v1p8);
+ clk_disable_unprepare(uphy->sleep_clk);
+ clk_disable_unprepare(uphy->ref_clk);
+
+ return 0;
+}
+
+static const struct phy_ops qcom_usb_hs_phy_ops = {
+ .power_on = qcom_usb_hs_phy_power_on,
+ .power_off = qcom_usb_hs_phy_power_off,
+ .set_mode = qcom_usb_hs_phy_set_mode,
+ .set_vbus = qcom_usb_hs_phy_set_vbus,
+ .owner = THIS_MODULE,
+};
+
+static int qcom_usb_hs_phy_probe(struct ulpi *ulpi)
+{
+ struct qcom_usb_hs_phy *uphy;
+ struct phy_provider *p;
+ struct clk *clk;
+ struct regulator *reg;
+ struct reset_control *reset;
+ int size;
+ int ret;
+
+ uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL);
+ if (!uphy)
+ return -ENOMEM;
+ ulpi_set_drvdata(ulpi, uphy);
+ uphy->ulpi = ulpi;
+ uphy->dr_mode = of_usb_get_dr_mode_by_phy(ulpi->dev.of_node, -1);
+
+ size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq");
+ if (size < 0)
+ size = 0;
+ uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1,
+ sizeof(*uphy->init_seq), GFP_KERNEL);
+ if (!uphy->init_seq)
+ return -ENOMEM;
+ ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq",
+ (u8 *)uphy->init_seq, size);
+ if (ret && size)
+ return ret;
+ /* NUL terminate */
+ uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0;
+
+ uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por");
+ if (IS_ERR(reset)) {
+ if (PTR_ERR(reset) == -EPROBE_DEFER)
+ return PTR_ERR(reset);
+ uphy->reset = NULL;
+ }
+
+ uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node,
+ &qcom_usb_hs_phy_ops);
+ if (IS_ERR(uphy->phy))
+ return PTR_ERR(uphy->phy);
+
+ phy_set_drvdata(uphy->phy, uphy);
+
+ p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate);
+ return PTR_ERR_OR_ZERO(p);
+}
+
+static const struct of_device_id qcom_usb_hs_phy_match[] = {
+ { .compatible = "qcom,usb-hs-phy", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match);
+
+static struct ulpi_driver qcom_usb_hs_phy_driver = {
+ .probe = qcom_usb_hs_phy_probe,
+ .driver = {
+ .name = "qcom_usb_hs_phy",
+ .of_match_table = qcom_usb_hs_phy_match,
+ },
+};
+module_ulpi_driver(qcom_usb_hs_phy_driver);
+
+MODULE_DESCRIPTION("Qualcomm USB HS phy");
+MODULE_LICENSE("GPL v2");
--
2.10.0.297.gf6727b0
^ permalink raw reply related [flat|nested] 12+ messages in thread