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