On Saturday 10 September 2016 05:48 PM, Kishon Vijay Abraham I wrote:
> 
> On Wed, Sep 07, 2016 at 02:35:19PM -0700, Stephen Boyd wrote:
>> The high-speed phy on qcom SoCs is controlled via the ULPI
>> viewport.
>>
>> Cc: Kishon Vijay Abraham I <kis...@ti.com>
>> Cc: <devicet...@vger.kernel.org>
>> Signed-off-by: Stephen Boyd <stephen.b...@linaro.org>
> 
> merged this and the previous patch to linux-phy.

since there are pending discussions, I'll drop this patch for now.

Thanks
Kishon
> 
> Thanks
> Kishon
> 
>> ---
>>  .../devicetree/bindings/phy/qcom,usb-hs-phy.txt    |  83 ++++++
>>  drivers/phy/Kconfig                                |   8 +
>>  drivers/phy/Makefile                               |   1 +
>>  drivers/phy/phy-qcom-usb-hs.c                      | 289 
>> +++++++++++++++++++++
>>  4 files changed, 381 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..d7eacd63d06b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt
>> @@ -0,0 +1,83 @@
>> +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
>> +
>> +- extcon:
>> +    Usage: optional
>> +    Value type: <prop-encoded-array>
>> +    Definition: Should contain the vbus and ID extcons in the first and 
>> second
>> +                cells respectively
>> +
>> +- qcom,init-seq:
>> +    Usage: optional
>> +    Value type: <u8 array>
>> +    Definition: Should contain a sequence of ULPI register and address 
>> pairs to
>> +                program into the ULPI_EXT_VENDOR_SPECIFIC area. This is 
>> related
>> +                to Device Mode Eye Diagram test.
>> +
>> +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>;
>> +                    extcon = <&smbb>, <&usb_id>;
>> +                    qcom,init-seq = /bits/ 8 <0x81 0x63>;
>> +            };
>> +    };
>> +};
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 830c443eeabf..ee0ec021a98c 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -417,6 +417,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 5422f543d17d..31c84faa07fa 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -50,6 +50,7 @@ obj-$(CONFIG_PHY_STIH41X_USB)              += 
>> phy-stih41x-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..73fb4b49a8e1
>> --- /dev/null
>> +++ b/drivers/phy/phy-qcom-usb-hs.c
>> @@ -0,0 +1,289 @@
>> +/**
>> + * 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/extcon.h>
>> +#include <linux/notifier.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;
>> +    struct notifier_block vbus_notify;
>> +    struct extcon_dev *vbus_edev;
>> +    struct extcon_dev *id_edev;
>> +    enum usb_dr_mode dr_mode;
>> +};
>> +
>> +static int
>> +qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long 
>> event,
>> +                          void *ptr)
>> +{
>> +    struct qcom_usb_hs_phy *uphy;
>> +    int is_host;
>> +    u8 addr;
>> +
>> +    uphy = container_of(nb, struct qcom_usb_hs_phy, vbus_notify);
>> +    is_host = extcon_get_cable_state_(uphy->id_edev, EXTCON_USB_HOST);
>> +    if (is_host < 0)
>> +            is_host = 0; /* No id event means always a peripheral */
>> +
>> +    if (event && !is_host)
>> +            addr = ULPI_SET(ULPI_MISC_A);
>> +    else
>> +            addr = ULPI_CLR(ULPI_MISC_A);
>> +
>> +    return ulpi_write(uphy->ulpi, addr,
>> +                      ULPI_MISC_A_VBUSVLDEXTSEL | 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, state;
>> +
>> +    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_voltage(uphy->v1p8, 1800000, 1800000);
>> +    if (ret)
>> +            goto err_1p8;
>> +
>> +    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, seq->addr, seq->val);
>> +            if (ret)
>> +                    goto err_ulpi;
>> +    }
>> +
>> +    if (uphy->reset) {
>> +            ret = reset_control_reset(uphy->reset);
>> +            if (ret)
>> +                    goto err_ulpi;
>> +    }
>> +
>> +    if (uphy->vbus_edev) {
>> +            ulpi_write(ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG),
>> +                       ULPI_PWR_OTG_COMP_DISABLE);
>> +            state = extcon_get_cable_state_(uphy->vbus_edev, EXTCON_USB);
>> +            /* setup initial state */
>> +            qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state,
>> +                                          uphy->vbus_edev);
>> +            ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB,
>> +                                            &uphy->vbus_notify);
>> +            if (ret)
>> +                    return ret;
>> +    } else {
>> +            u8 val;
>> +
>> +            switch (uphy->dr_mode) {
>> +            case USB_DR_MODE_OTG:
>> +                    val = ULPI_INT_IDGRD;
>> +            case USB_DR_MODE_PERIPHERAL:
>> +                    val |= ULPI_INT_SESS_VALID;
>> +                    break;
>> +            default:
>> +                    val = 0;
>> +            }
>> +
>> +            ret = ulpi_write(ulpi, ULPI_USB_INT_EN_RISE, val);
>> +            if (ret)
>> +                    goto err_ulpi;
>> +            ret = ulpi_write(ulpi, ULPI_USB_INT_EN_FALL, val);
>> +            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)
>> +{
>> +    int ret;
>> +    struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
>> +
>> +    if (uphy->vbus_edev) {
>> +            ret = extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB,
>> +                                             &uphy->vbus_notify);
>> +            if (ret)
>> +                    return ret;
>> +    }
>> +
>> +    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,
>> +    .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);
>> +
>> +    uphy->vbus_edev = extcon_get_edev_by_phandle(&ulpi->dev, 0);
>> +    if (IS_ERR(uphy->vbus_edev)) {
>> +            if (PTR_ERR(uphy->vbus_edev) != -ENODEV)
>> +                    return PTR_ERR(uphy->vbus_edev);
>> +            uphy->vbus_edev = NULL;
>> +    }
>> +
>> +    uphy->id_edev = extcon_get_edev_by_phandle(&ulpi->dev, 1);
>> +    if (IS_ERR(uphy->id_edev)) {
>> +            if (PTR_ERR(uphy->id_edev) != -ENODEV)
>> +                    return PTR_ERR(uphy->id_edev);
>> +            uphy->id_edev = NULL;
>> +    }
>> +
>> +    uphy->vbus_notify.notifier_call = qcom_usb_hs_phy_vbus_notifier;
>> +    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.9.0.rc2.8.ga28705d
>>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to