294 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver4 *5 * Copyright (C) 2016 David Lechner <david@lechnology.com>6 */7 8#include <linux/clk.h>9#include <linux/io.h>10#include <linux/of.h>11#include <linux/mfd/da8xx-cfgchip.h>12#include <linux/mfd/syscon.h>13#include <linux/module.h>14#include <linux/phy/phy.h>15#include <linux/platform_data/phy-da8xx-usb.h>16#include <linux/platform_device.h>17#include <linux/pm_runtime.h>18#include <linux/regmap.h>19 20#define PHY_INIT_BITS (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN)21 22struct da8xx_usb_phy {23 struct device *dev;24 struct phy_provider *phy_provider;25 struct phy *usb11_phy;26 struct phy *usb20_phy;27 struct clk *usb11_clk;28 struct clk *usb20_clk;29 struct regmap *regmap;30};31 32static int da8xx_usb11_phy_power_on(struct phy *phy)33{34 struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);35 int ret;36 37 ret = clk_prepare_enable(d_phy->usb11_clk);38 if (ret)39 return ret;40 41 regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM,42 CFGCHIP2_USB1SUSPENDM);43 44 /*45 * USB1.1 can used USB2.0 output clock as reference clock so this is here to prevent USB2.046 * from shutting PHY's power when USB1.1 might use it47 */48 pm_runtime_get_sync(d_phy->dev);49 50 return 0;51}52 53static int da8xx_usb11_phy_power_off(struct phy *phy)54{55 struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);56 57 regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0);58 59 clk_disable_unprepare(d_phy->usb11_clk);60 pm_runtime_put_sync(d_phy->dev);61 62 return 0;63}64 65static const struct phy_ops da8xx_usb11_phy_ops = {66 .power_on = da8xx_usb11_phy_power_on,67 .power_off = da8xx_usb11_phy_power_off,68 .owner = THIS_MODULE,69};70 71static int da8xx_usb20_phy_power_on(struct phy *phy)72{73 struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);74 int ret;75 76 ret = clk_prepare_enable(d_phy->usb20_clk);77 if (ret)78 return ret;79 80 regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0);81 82 return 0;83}84 85static int da8xx_usb20_phy_power_off(struct phy *phy)86{87 struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);88 89 regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN,90 CFGCHIP2_OTGPWRDN);91 92 clk_disable_unprepare(d_phy->usb20_clk);93 94 return 0;95}96 97static int da8xx_usb20_phy_set_mode(struct phy *phy,98 enum phy_mode mode, int submode)99{100 struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);101 u32 val;102 103 switch (mode) {104 case PHY_MODE_USB_HOST: /* Force VBUS valid, ID = 0 */105 val = CFGCHIP2_OTGMODE_FORCE_HOST;106 break;107 case PHY_MODE_USB_DEVICE: /* Force VBUS valid, ID = 1 */108 val = CFGCHIP2_OTGMODE_FORCE_DEVICE;109 break;110 case PHY_MODE_USB_OTG: /* Don't override the VBUS/ID comparators */111 val = CFGCHIP2_OTGMODE_NO_OVERRIDE;112 break;113 default:114 return -EINVAL;115 }116 117 regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK,118 val);119 120 return 0;121}122 123static const struct phy_ops da8xx_usb20_phy_ops = {124 .power_on = da8xx_usb20_phy_power_on,125 .power_off = da8xx_usb20_phy_power_off,126 .set_mode = da8xx_usb20_phy_set_mode,127 .owner = THIS_MODULE,128};129 130static int __maybe_unused da8xx_runtime_suspend(struct device *dev)131{132 struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);133 134 dev_dbg(dev, "Suspending ...\n");135 136 regmap_set_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN);137 138 return 0;139}140 141static int __maybe_unused da8xx_runtime_resume(struct device *dev)142{143 u32 mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN | CFGCHIP2_PHY_PLLON;144 struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);145 u32 pll_status;146 147 regmap_update_bits(d_phy->regmap, CFGCHIP(2), mask, CFGCHIP2_PHY_PLLON);148 149 dev_dbg(dev, "Resuming ...\n");150 151 return regmap_read_poll_timeout(d_phy->regmap, CFGCHIP(2), pll_status,152 pll_status & CFGCHIP2_PHYCLKGD, 1000, 500000);153}154 155static const struct dev_pm_ops da8xx_usb_phy_pm_ops = {156 SET_RUNTIME_PM_OPS(da8xx_runtime_suspend, da8xx_runtime_resume, NULL)157};158 159static struct phy *da8xx_usb_phy_of_xlate(struct device *dev,160 const struct of_phandle_args *args)161{162 struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);163 164 if (!d_phy)165 return ERR_PTR(-ENODEV);166 167 switch (args->args[0]) {168 case 0:169 return d_phy->usb20_phy;170 case 1:171 return d_phy->usb11_phy;172 default:173 return ERR_PTR(-EINVAL);174 }175}176 177static int da8xx_usb_phy_probe(struct platform_device *pdev)178{179 struct device *dev = &pdev->dev;180 struct da8xx_usb_phy_platform_data *pdata = dev->platform_data;181 struct device_node *node = dev->of_node;182 struct da8xx_usb_phy *d_phy;183 184 d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL);185 if (!d_phy)186 return -ENOMEM;187 188 d_phy->dev = dev;189 190 if (pdata)191 d_phy->regmap = pdata->cfgchip;192 else193 d_phy->regmap = syscon_regmap_lookup_by_compatible(194 "ti,da830-cfgchip");195 if (IS_ERR(d_phy->regmap)) {196 dev_err(dev, "Failed to get syscon\n");197 return PTR_ERR(d_phy->regmap);198 }199 200 d_phy->usb11_clk = devm_clk_get(dev, "usb1_clk48");201 if (IS_ERR(d_phy->usb11_clk)) {202 dev_err(dev, "Failed to get usb1_clk48\n");203 return PTR_ERR(d_phy->usb11_clk);204 }205 206 d_phy->usb20_clk = devm_clk_get(dev, "usb0_clk48");207 if (IS_ERR(d_phy->usb20_clk)) {208 dev_err(dev, "Failed to get usb0_clk48\n");209 return PTR_ERR(d_phy->usb20_clk);210 }211 212 d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops);213 if (IS_ERR(d_phy->usb11_phy)) {214 dev_err(dev, "Failed to create usb11 phy\n");215 return PTR_ERR(d_phy->usb11_phy);216 }217 218 d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops);219 if (IS_ERR(d_phy->usb20_phy)) {220 dev_err(dev, "Failed to create usb20 phy\n");221 return PTR_ERR(d_phy->usb20_phy);222 }223 224 platform_set_drvdata(pdev, d_phy);225 phy_set_drvdata(d_phy->usb11_phy, d_phy);226 phy_set_drvdata(d_phy->usb20_phy, d_phy);227 228 if (node) {229 d_phy->phy_provider = devm_of_phy_provider_register(dev,230 da8xx_usb_phy_of_xlate);231 if (IS_ERR(d_phy->phy_provider)) {232 dev_err(dev, "Failed to create phy provider\n");233 return PTR_ERR(d_phy->phy_provider);234 }235 } else {236 int ret;237 238 ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy",239 "ohci-da8xx");240 if (ret)241 dev_warn(dev, "Failed to create usb11 phy lookup\n");242 ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy",243 "musb-da8xx");244 if (ret)245 dev_warn(dev, "Failed to create usb20 phy lookup\n");246 }247 248 regmap_write_bits(d_phy->regmap, CFGCHIP(2),249 PHY_INIT_BITS, PHY_INIT_BITS);250 251 pm_runtime_set_active(dev);252 devm_pm_runtime_enable(dev);253 /*254 * Prevent runtime pm from being ON by default. Users can enable255 * it using power/control in sysfs.256 */257 pm_runtime_forbid(dev);258 259 return 0;260}261 262static void da8xx_usb_phy_remove(struct platform_device *pdev)263{264 struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev);265 266 if (!pdev->dev.of_node) {267 phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx");268 phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci-da8xx");269 }270}271 272static const struct of_device_id da8xx_usb_phy_ids[] = {273 { .compatible = "ti,da830-usb-phy" },274 { }275};276MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids);277 278static struct platform_driver da8xx_usb_phy_driver = {279 .probe = da8xx_usb_phy_probe,280 .remove_new = da8xx_usb_phy_remove,281 .driver = {282 .name = "da8xx-usb-phy",283 .pm = &da8xx_usb_phy_pm_ops,284 .of_match_table = da8xx_usb_phy_ids,285 },286};287 288module_platform_driver(da8xx_usb_phy_driver);289 290MODULE_ALIAS("platform:da8xx-usb-phy");291MODULE_AUTHOR("David Lechner <david@lechnology.com>");292MODULE_DESCRIPTION("TI DA8xx USB PHY driver");293MODULE_LICENSE("GPL v2");294