Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2016 Linaro Ltd
0004  */
0005 #include <linux/module.h>
0006 #include <linux/ulpi/driver.h>
0007 #include <linux/ulpi/regs.h>
0008 #include <linux/clk.h>
0009 #include <linux/regulator/consumer.h>
0010 #include <linux/of_device.h>
0011 #include <linux/phy/phy.h>
0012 #include <linux/reset.h>
0013 #include <linux/extcon.h>
0014 #include <linux/notifier.h>
0015 
0016 #define ULPI_PWR_CLK_MNG_REG        0x88
0017 # define ULPI_PWR_OTG_COMP_DISABLE  BIT(0)
0018 
0019 #define ULPI_MISC_A         0x96
0020 # define ULPI_MISC_A_VBUSVLDEXTSEL  BIT(1)
0021 # define ULPI_MISC_A_VBUSVLDEXT     BIT(0)
0022 
0023 
0024 struct ulpi_seq {
0025     u8 addr;
0026     u8 val;
0027 };
0028 
0029 struct qcom_usb_hs_phy {
0030     struct ulpi *ulpi;
0031     struct phy *phy;
0032     struct clk *ref_clk;
0033     struct clk *sleep_clk;
0034     struct regulator *v1p8;
0035     struct regulator *v3p3;
0036     struct reset_control *reset;
0037     struct ulpi_seq *init_seq;
0038     struct extcon_dev *vbus_edev;
0039     struct notifier_block vbus_notify;
0040 };
0041 
0042 static int qcom_usb_hs_phy_set_mode(struct phy *phy,
0043                     enum phy_mode mode, int submode)
0044 {
0045     struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
0046     u8 addr;
0047     int ret;
0048 
0049     if (!uphy->vbus_edev) {
0050         u8 val = 0;
0051 
0052         switch (mode) {
0053         case PHY_MODE_USB_OTG:
0054         case PHY_MODE_USB_HOST:
0055             val |= ULPI_INT_IDGRD;
0056             fallthrough;
0057         case PHY_MODE_USB_DEVICE:
0058             val |= ULPI_INT_SESS_VALID;
0059             break;
0060         default:
0061             break;
0062         }
0063 
0064         ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val);
0065         if (ret)
0066             return ret;
0067         ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val);
0068     } else {
0069         switch (mode) {
0070         case PHY_MODE_USB_OTG:
0071         case PHY_MODE_USB_DEVICE:
0072             addr = ULPI_SET(ULPI_MISC_A);
0073             break;
0074         case PHY_MODE_USB_HOST:
0075             addr = ULPI_CLR(ULPI_MISC_A);
0076             break;
0077         default:
0078             return -EINVAL;
0079         }
0080 
0081         ret = ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG),
0082                  ULPI_PWR_OTG_COMP_DISABLE);
0083         if (ret)
0084             return ret;
0085         ret = ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXTSEL);
0086     }
0087 
0088     return ret;
0089 }
0090 
0091 static int
0092 qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long event,
0093                   void *ptr)
0094 {
0095     struct qcom_usb_hs_phy *uphy;
0096     u8 addr;
0097 
0098     uphy = container_of(nb, struct qcom_usb_hs_phy, vbus_notify);
0099 
0100     if (event)
0101         addr = ULPI_SET(ULPI_MISC_A);
0102     else
0103         addr = ULPI_CLR(ULPI_MISC_A);
0104 
0105     return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT);
0106 }
0107 
0108 static int qcom_usb_hs_phy_power_on(struct phy *phy)
0109 {
0110     struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
0111     struct ulpi *ulpi = uphy->ulpi;
0112     const struct ulpi_seq *seq;
0113     int ret, state;
0114 
0115     ret = clk_prepare_enable(uphy->ref_clk);
0116     if (ret)
0117         return ret;
0118 
0119     ret = clk_prepare_enable(uphy->sleep_clk);
0120     if (ret)
0121         goto err_sleep;
0122 
0123     ret = regulator_set_load(uphy->v1p8, 50000);
0124     if (ret < 0)
0125         goto err_1p8;
0126 
0127     ret = regulator_enable(uphy->v1p8);
0128     if (ret)
0129         goto err_1p8;
0130 
0131     ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000,
0132                         3300000);
0133     if (ret)
0134         goto err_3p3;
0135 
0136     ret = regulator_set_load(uphy->v3p3, 50000);
0137     if (ret < 0)
0138         goto err_3p3;
0139 
0140     ret = regulator_enable(uphy->v3p3);
0141     if (ret)
0142         goto err_3p3;
0143 
0144     for (seq = uphy->init_seq; seq->addr; seq++) {
0145         ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr,
0146                  seq->val);
0147         if (ret)
0148             goto err_ulpi;
0149     }
0150 
0151     if (uphy->reset) {
0152         ret = reset_control_reset(uphy->reset);
0153         if (ret)
0154             goto err_ulpi;
0155     }
0156 
0157     if (uphy->vbus_edev) {
0158         state = extcon_get_state(uphy->vbus_edev, EXTCON_USB);
0159         /* setup initial state */
0160         qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state,
0161                           uphy->vbus_edev);
0162         ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB,
0163                            &uphy->vbus_notify);
0164         if (ret)
0165             goto err_ulpi;
0166     }
0167 
0168     return 0;
0169 err_ulpi:
0170     regulator_disable(uphy->v3p3);
0171 err_3p3:
0172     regulator_disable(uphy->v1p8);
0173 err_1p8:
0174     clk_disable_unprepare(uphy->sleep_clk);
0175 err_sleep:
0176     clk_disable_unprepare(uphy->ref_clk);
0177     return ret;
0178 }
0179 
0180 static int qcom_usb_hs_phy_power_off(struct phy *phy)
0181 {
0182     struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
0183 
0184     if (uphy->vbus_edev)
0185         extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB,
0186                        &uphy->vbus_notify);
0187     regulator_disable(uphy->v3p3);
0188     regulator_disable(uphy->v1p8);
0189     clk_disable_unprepare(uphy->sleep_clk);
0190     clk_disable_unprepare(uphy->ref_clk);
0191 
0192     return 0;
0193 }
0194 
0195 static const struct phy_ops qcom_usb_hs_phy_ops = {
0196     .power_on = qcom_usb_hs_phy_power_on,
0197     .power_off = qcom_usb_hs_phy_power_off,
0198     .set_mode = qcom_usb_hs_phy_set_mode,
0199     .owner = THIS_MODULE,
0200 };
0201 
0202 static int qcom_usb_hs_phy_probe(struct ulpi *ulpi)
0203 {
0204     struct qcom_usb_hs_phy *uphy;
0205     struct phy_provider *p;
0206     struct clk *clk;
0207     struct regulator *reg;
0208     struct reset_control *reset;
0209     int size;
0210     int ret;
0211 
0212     uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL);
0213     if (!uphy)
0214         return -ENOMEM;
0215     ulpi_set_drvdata(ulpi, uphy);
0216     uphy->ulpi = ulpi;
0217 
0218     size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq");
0219     if (size < 0)
0220         size = 0;
0221     uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1,
0222                        sizeof(*uphy->init_seq), GFP_KERNEL);
0223     if (!uphy->init_seq)
0224         return -ENOMEM;
0225     ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq",
0226                     (u8 *)uphy->init_seq, size);
0227     if (ret && size)
0228         return ret;
0229     /* NUL terminate */
0230     uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0;
0231 
0232     uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref");
0233     if (IS_ERR(clk))
0234         return PTR_ERR(clk);
0235 
0236     uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep");
0237     if (IS_ERR(clk))
0238         return PTR_ERR(clk);
0239 
0240     uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8");
0241     if (IS_ERR(reg))
0242         return PTR_ERR(reg);
0243 
0244     uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3");
0245     if (IS_ERR(reg))
0246         return PTR_ERR(reg);
0247 
0248     uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por");
0249     if (IS_ERR(reset)) {
0250         if (PTR_ERR(reset) == -EPROBE_DEFER)
0251             return PTR_ERR(reset);
0252         uphy->reset = NULL;
0253     }
0254 
0255     uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node,
0256                     &qcom_usb_hs_phy_ops);
0257     if (IS_ERR(uphy->phy))
0258         return PTR_ERR(uphy->phy);
0259 
0260     uphy->vbus_edev = extcon_get_edev_by_phandle(&ulpi->dev, 0);
0261     if (IS_ERR(uphy->vbus_edev)) {
0262         if (PTR_ERR(uphy->vbus_edev) != -ENODEV)
0263             return PTR_ERR(uphy->vbus_edev);
0264         uphy->vbus_edev = NULL;
0265     }
0266 
0267     uphy->vbus_notify.notifier_call = qcom_usb_hs_phy_vbus_notifier;
0268     phy_set_drvdata(uphy->phy, uphy);
0269 
0270     p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate);
0271     return PTR_ERR_OR_ZERO(p);
0272 }
0273 
0274 static const struct of_device_id qcom_usb_hs_phy_match[] = {
0275     { .compatible = "qcom,usb-hs-phy", },
0276     { }
0277 };
0278 MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match);
0279 
0280 static struct ulpi_driver qcom_usb_hs_phy_driver = {
0281     .probe = qcom_usb_hs_phy_probe,
0282     .driver = {
0283         .name = "qcom_usb_hs_phy",
0284         .of_match_table = qcom_usb_hs_phy_match,
0285     },
0286 };
0287 module_ulpi_driver(qcom_usb_hs_phy_driver);
0288 
0289 MODULE_DESCRIPTION("Qualcomm USB HS phy");
0290 MODULE_LICENSE("GPL v2");