Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver
0004  *
0005  * Copyright (C) 2016 David Lechner <david@lechnology.com>
0006  */
0007 
0008 #include <linux/clk.h>
0009 #include <linux/io.h>
0010 #include <linux/of.h>
0011 #include <linux/mfd/da8xx-cfgchip.h>
0012 #include <linux/mfd/syscon.h>
0013 #include <linux/module.h>
0014 #include <linux/phy/phy.h>
0015 #include <linux/platform_data/phy-da8xx-usb.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/regmap.h>
0018 
0019 #define PHY_INIT_BITS   (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN)
0020 
0021 struct da8xx_usb_phy {
0022     struct phy_provider *phy_provider;
0023     struct phy      *usb11_phy;
0024     struct phy      *usb20_phy;
0025     struct clk      *usb11_clk;
0026     struct clk      *usb20_clk;
0027     struct regmap       *regmap;
0028 };
0029 
0030 static int da8xx_usb11_phy_power_on(struct phy *phy)
0031 {
0032     struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
0033     int ret;
0034 
0035     ret = clk_prepare_enable(d_phy->usb11_clk);
0036     if (ret)
0037         return ret;
0038 
0039     regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM,
0040               CFGCHIP2_USB1SUSPENDM);
0041 
0042     return 0;
0043 }
0044 
0045 static int da8xx_usb11_phy_power_off(struct phy *phy)
0046 {
0047     struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
0048 
0049     regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0);
0050 
0051     clk_disable_unprepare(d_phy->usb11_clk);
0052 
0053     return 0;
0054 }
0055 
0056 static const struct phy_ops da8xx_usb11_phy_ops = {
0057     .power_on   = da8xx_usb11_phy_power_on,
0058     .power_off  = da8xx_usb11_phy_power_off,
0059     .owner      = THIS_MODULE,
0060 };
0061 
0062 static int da8xx_usb20_phy_power_on(struct phy *phy)
0063 {
0064     struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
0065     int ret;
0066 
0067     ret = clk_prepare_enable(d_phy->usb20_clk);
0068     if (ret)
0069         return ret;
0070 
0071     regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0);
0072 
0073     return 0;
0074 }
0075 
0076 static int da8xx_usb20_phy_power_off(struct phy *phy)
0077 {
0078     struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
0079 
0080     regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN,
0081               CFGCHIP2_OTGPWRDN);
0082 
0083     clk_disable_unprepare(d_phy->usb20_clk);
0084 
0085     return 0;
0086 }
0087 
0088 static int da8xx_usb20_phy_set_mode(struct phy *phy,
0089                     enum phy_mode mode, int submode)
0090 {
0091     struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
0092     u32 val;
0093 
0094     switch (mode) {
0095     case PHY_MODE_USB_HOST:     /* Force VBUS valid, ID = 0 */
0096         val = CFGCHIP2_OTGMODE_FORCE_HOST;
0097         break;
0098     case PHY_MODE_USB_DEVICE:   /* Force VBUS valid, ID = 1 */
0099         val = CFGCHIP2_OTGMODE_FORCE_DEVICE;
0100         break;
0101     case PHY_MODE_USB_OTG:  /* Don't override the VBUS/ID comparators */
0102         val = CFGCHIP2_OTGMODE_NO_OVERRIDE;
0103         break;
0104     default:
0105         return -EINVAL;
0106     }
0107 
0108     regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK,
0109               val);
0110 
0111     return 0;
0112 }
0113 
0114 static const struct phy_ops da8xx_usb20_phy_ops = {
0115     .power_on   = da8xx_usb20_phy_power_on,
0116     .power_off  = da8xx_usb20_phy_power_off,
0117     .set_mode   = da8xx_usb20_phy_set_mode,
0118     .owner      = THIS_MODULE,
0119 };
0120 
0121 static struct phy *da8xx_usb_phy_of_xlate(struct device *dev,
0122                      struct of_phandle_args *args)
0123 {
0124     struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);
0125 
0126     if (!d_phy)
0127         return ERR_PTR(-ENODEV);
0128 
0129     switch (args->args[0]) {
0130     case 0:
0131         return d_phy->usb20_phy;
0132     case 1:
0133         return d_phy->usb11_phy;
0134     default:
0135         return ERR_PTR(-EINVAL);
0136     }
0137 }
0138 
0139 static int da8xx_usb_phy_probe(struct platform_device *pdev)
0140 {
0141     struct device       *dev = &pdev->dev;
0142     struct da8xx_usb_phy_platform_data *pdata = dev->platform_data;
0143     struct device_node  *node = dev->of_node;
0144     struct da8xx_usb_phy    *d_phy;
0145 
0146     d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL);
0147     if (!d_phy)
0148         return -ENOMEM;
0149 
0150     if (pdata)
0151         d_phy->regmap = pdata->cfgchip;
0152     else
0153         d_phy->regmap = syscon_regmap_lookup_by_compatible(
0154                             "ti,da830-cfgchip");
0155     if (IS_ERR(d_phy->regmap)) {
0156         dev_err(dev, "Failed to get syscon\n");
0157         return PTR_ERR(d_phy->regmap);
0158     }
0159 
0160     d_phy->usb11_clk = devm_clk_get(dev, "usb1_clk48");
0161     if (IS_ERR(d_phy->usb11_clk)) {
0162         dev_err(dev, "Failed to get usb1_clk48\n");
0163         return PTR_ERR(d_phy->usb11_clk);
0164     }
0165 
0166     d_phy->usb20_clk = devm_clk_get(dev, "usb0_clk48");
0167     if (IS_ERR(d_phy->usb20_clk)) {
0168         dev_err(dev, "Failed to get usb0_clk48\n");
0169         return PTR_ERR(d_phy->usb20_clk);
0170     }
0171 
0172     d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops);
0173     if (IS_ERR(d_phy->usb11_phy)) {
0174         dev_err(dev, "Failed to create usb11 phy\n");
0175         return PTR_ERR(d_phy->usb11_phy);
0176     }
0177 
0178     d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops);
0179     if (IS_ERR(d_phy->usb20_phy)) {
0180         dev_err(dev, "Failed to create usb20 phy\n");
0181         return PTR_ERR(d_phy->usb20_phy);
0182     }
0183 
0184     platform_set_drvdata(pdev, d_phy);
0185     phy_set_drvdata(d_phy->usb11_phy, d_phy);
0186     phy_set_drvdata(d_phy->usb20_phy, d_phy);
0187 
0188     if (node) {
0189         d_phy->phy_provider = devm_of_phy_provider_register(dev,
0190                             da8xx_usb_phy_of_xlate);
0191         if (IS_ERR(d_phy->phy_provider)) {
0192             dev_err(dev, "Failed to create phy provider\n");
0193             return PTR_ERR(d_phy->phy_provider);
0194         }
0195     } else {
0196         int ret;
0197 
0198         ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy",
0199                     "ohci-da8xx");
0200         if (ret)
0201             dev_warn(dev, "Failed to create usb11 phy lookup\n");
0202         ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy",
0203                     "musb-da8xx");
0204         if (ret)
0205             dev_warn(dev, "Failed to create usb20 phy lookup\n");
0206     }
0207 
0208     regmap_write_bits(d_phy->regmap, CFGCHIP(2),
0209               PHY_INIT_BITS, PHY_INIT_BITS);
0210 
0211     return 0;
0212 }
0213 
0214 static int da8xx_usb_phy_remove(struct platform_device *pdev)
0215 {
0216     struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev);
0217 
0218     if (!pdev->dev.of_node) {
0219         phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx");
0220         phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci-da8xx");
0221     }
0222 
0223     return 0;
0224 }
0225 
0226 static const struct of_device_id da8xx_usb_phy_ids[] = {
0227     { .compatible = "ti,da830-usb-phy" },
0228     { }
0229 };
0230 MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids);
0231 
0232 static struct platform_driver da8xx_usb_phy_driver = {
0233     .probe  = da8xx_usb_phy_probe,
0234     .remove = da8xx_usb_phy_remove,
0235     .driver = {
0236         .name   = "da8xx-usb-phy",
0237         .of_match_table = da8xx_usb_phy_ids,
0238     },
0239 };
0240 
0241 module_platform_driver(da8xx_usb_phy_driver);
0242 
0243 MODULE_ALIAS("platform:da8xx-usb-phy");
0244 MODULE_AUTHOR("David Lechner <david@lechnology.com>");
0245 MODULE_DESCRIPTION("TI DA8xx USB PHY driver");
0246 MODULE_LICENSE("GPL v2");