1*8e8e69d6SThomas 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> 17f2e60041SDavid Lechner #include <linux/regmap.h> 18f2e60041SDavid Lechner 1923e9b38dSAlexandre Bailon #define PHY_INIT_BITS (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN) 2023e9b38dSAlexandre Bailon 21f2e60041SDavid Lechner struct da8xx_usb_phy { 22f2e60041SDavid Lechner struct phy_provider *phy_provider; 23f2e60041SDavid Lechner struct phy *usb11_phy; 24f2e60041SDavid Lechner struct phy *usb20_phy; 25f2e60041SDavid Lechner struct clk *usb11_clk; 26f2e60041SDavid Lechner struct clk *usb20_clk; 27f2e60041SDavid Lechner struct regmap *regmap; 28f2e60041SDavid Lechner }; 29f2e60041SDavid Lechner 30f2e60041SDavid Lechner static int da8xx_usb11_phy_power_on(struct phy *phy) 31f2e60041SDavid Lechner { 32f2e60041SDavid Lechner struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); 33f2e60041SDavid Lechner int ret; 34f2e60041SDavid Lechner 35f2e60041SDavid Lechner ret = clk_prepare_enable(d_phy->usb11_clk); 36f2e60041SDavid Lechner if (ret) 37f2e60041SDavid Lechner return ret; 38f2e60041SDavid Lechner 39f2e60041SDavid Lechner regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 40f2e60041SDavid Lechner CFGCHIP2_USB1SUSPENDM); 41f2e60041SDavid Lechner 42f2e60041SDavid Lechner return 0; 43f2e60041SDavid Lechner } 44f2e60041SDavid Lechner 45f2e60041SDavid Lechner static int da8xx_usb11_phy_power_off(struct phy *phy) 46f2e60041SDavid Lechner { 47f2e60041SDavid Lechner struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); 48f2e60041SDavid Lechner 49f2e60041SDavid Lechner regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0); 50f2e60041SDavid Lechner 51f2e60041SDavid Lechner clk_disable_unprepare(d_phy->usb11_clk); 52f2e60041SDavid Lechner 53f2e60041SDavid Lechner return 0; 54f2e60041SDavid Lechner } 55f2e60041SDavid Lechner 56f2e60041SDavid Lechner static const struct phy_ops da8xx_usb11_phy_ops = { 57f2e60041SDavid Lechner .power_on = da8xx_usb11_phy_power_on, 58f2e60041SDavid Lechner .power_off = da8xx_usb11_phy_power_off, 59f2e60041SDavid Lechner .owner = THIS_MODULE, 60f2e60041SDavid Lechner }; 61f2e60041SDavid Lechner 62f2e60041SDavid Lechner static int da8xx_usb20_phy_power_on(struct phy *phy) 63f2e60041SDavid Lechner { 64f2e60041SDavid Lechner struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); 65f2e60041SDavid Lechner int ret; 66f2e60041SDavid Lechner 67f2e60041SDavid Lechner ret = clk_prepare_enable(d_phy->usb20_clk); 68f2e60041SDavid Lechner if (ret) 69f2e60041SDavid Lechner return ret; 70f2e60041SDavid Lechner 71f2e60041SDavid Lechner regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0); 72f2e60041SDavid Lechner 73f2e60041SDavid Lechner return 0; 74f2e60041SDavid Lechner } 75f2e60041SDavid Lechner 76f2e60041SDavid Lechner static int da8xx_usb20_phy_power_off(struct phy *phy) 77f2e60041SDavid Lechner { 78f2e60041SDavid Lechner struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); 79f2e60041SDavid Lechner 80f2e60041SDavid Lechner regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 81f2e60041SDavid Lechner CFGCHIP2_OTGPWRDN); 82f2e60041SDavid Lechner 83f2e60041SDavid Lechner clk_disable_unprepare(d_phy->usb20_clk); 84f2e60041SDavid Lechner 85f2e60041SDavid Lechner return 0; 86f2e60041SDavid Lechner } 87f2e60041SDavid Lechner 8879a5a18aSGrygorii Strashko static int da8xx_usb20_phy_set_mode(struct phy *phy, 8979a5a18aSGrygorii Strashko enum phy_mode mode, int submode) 90f2e60041SDavid Lechner { 91f2e60041SDavid Lechner struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); 92f2e60041SDavid Lechner u32 val; 93f2e60041SDavid Lechner 94f2e60041SDavid Lechner switch (mode) { 95f2e60041SDavid Lechner case PHY_MODE_USB_HOST: /* Force VBUS valid, ID = 0 */ 96f2e60041SDavid Lechner val = CFGCHIP2_OTGMODE_FORCE_HOST; 97f2e60041SDavid Lechner break; 98f2e60041SDavid Lechner case PHY_MODE_USB_DEVICE: /* Force VBUS valid, ID = 1 */ 99f2e60041SDavid Lechner val = CFGCHIP2_OTGMODE_FORCE_DEVICE; 100f2e60041SDavid Lechner break; 101f2e60041SDavid Lechner case PHY_MODE_USB_OTG: /* Don't override the VBUS/ID comparators */ 102f2e60041SDavid Lechner val = CFGCHIP2_OTGMODE_NO_OVERRIDE; 103f2e60041SDavid Lechner break; 104f2e60041SDavid Lechner default: 105f2e60041SDavid Lechner return -EINVAL; 106f2e60041SDavid Lechner } 107f2e60041SDavid Lechner 108f2e60041SDavid Lechner regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK, 109f2e60041SDavid Lechner val); 110f2e60041SDavid Lechner 111f2e60041SDavid Lechner return 0; 112f2e60041SDavid Lechner } 113f2e60041SDavid Lechner 114f2e60041SDavid Lechner static const struct phy_ops da8xx_usb20_phy_ops = { 115f2e60041SDavid Lechner .power_on = da8xx_usb20_phy_power_on, 116f2e60041SDavid Lechner .power_off = da8xx_usb20_phy_power_off, 117f2e60041SDavid Lechner .set_mode = da8xx_usb20_phy_set_mode, 118f2e60041SDavid Lechner .owner = THIS_MODULE, 119f2e60041SDavid Lechner }; 120f2e60041SDavid Lechner 121f2e60041SDavid Lechner static struct phy *da8xx_usb_phy_of_xlate(struct device *dev, 122f2e60041SDavid Lechner struct of_phandle_args *args) 123f2e60041SDavid Lechner { 124f2e60041SDavid Lechner struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev); 125f2e60041SDavid Lechner 126f2e60041SDavid Lechner if (!d_phy) 127f2e60041SDavid Lechner return ERR_PTR(-ENODEV); 128f2e60041SDavid Lechner 129f2e60041SDavid Lechner switch (args->args[0]) { 130f2e60041SDavid Lechner case 0: 131f2e60041SDavid Lechner return d_phy->usb20_phy; 132f2e60041SDavid Lechner case 1: 133f2e60041SDavid Lechner return d_phy->usb11_phy; 134f2e60041SDavid Lechner default: 135f2e60041SDavid Lechner return ERR_PTR(-EINVAL); 136f2e60041SDavid Lechner } 137f2e60041SDavid Lechner } 138f2e60041SDavid Lechner 139f2e60041SDavid Lechner static int da8xx_usb_phy_probe(struct platform_device *pdev) 140f2e60041SDavid Lechner { 141f2e60041SDavid Lechner struct device *dev = &pdev->dev; 142bdec5a6bSDavid Lechner struct da8xx_usb_phy_platform_data *pdata = dev->platform_data; 143f2e60041SDavid Lechner struct device_node *node = dev->of_node; 144f2e60041SDavid Lechner struct da8xx_usb_phy *d_phy; 145f2e60041SDavid Lechner 146f2e60041SDavid Lechner d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL); 147f2e60041SDavid Lechner if (!d_phy) 148f2e60041SDavid Lechner return -ENOMEM; 149f2e60041SDavid Lechner 150bdec5a6bSDavid Lechner if (pdata) 151bdec5a6bSDavid Lechner d_phy->regmap = pdata->cfgchip; 152bdec5a6bSDavid Lechner else 153f2e60041SDavid Lechner d_phy->regmap = syscon_regmap_lookup_by_compatible( 154f2e60041SDavid Lechner "ti,da830-cfgchip"); 155f2e60041SDavid Lechner if (IS_ERR(d_phy->regmap)) { 156f2e60041SDavid Lechner dev_err(dev, "Failed to get syscon\n"); 157f2e60041SDavid Lechner return PTR_ERR(d_phy->regmap); 158f2e60041SDavid Lechner } 159f2e60041SDavid Lechner 160e98bbbf3SDavid Lechner d_phy->usb11_clk = devm_clk_get(dev, "usb1_clk48"); 161f2e60041SDavid Lechner if (IS_ERR(d_phy->usb11_clk)) { 162e98bbbf3SDavid Lechner dev_err(dev, "Failed to get usb1_clk48\n"); 163f2e60041SDavid Lechner return PTR_ERR(d_phy->usb11_clk); 164f2e60041SDavid Lechner } 165f2e60041SDavid Lechner 166e98bbbf3SDavid Lechner d_phy->usb20_clk = devm_clk_get(dev, "usb0_clk48"); 167f2e60041SDavid Lechner if (IS_ERR(d_phy->usb20_clk)) { 168e98bbbf3SDavid Lechner dev_err(dev, "Failed to get usb0_clk48\n"); 169f2e60041SDavid Lechner return PTR_ERR(d_phy->usb20_clk); 170f2e60041SDavid Lechner } 171f2e60041SDavid Lechner 172f2e60041SDavid Lechner d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops); 173f2e60041SDavid Lechner if (IS_ERR(d_phy->usb11_phy)) { 174f2e60041SDavid Lechner dev_err(dev, "Failed to create usb11 phy\n"); 175f2e60041SDavid Lechner return PTR_ERR(d_phy->usb11_phy); 176f2e60041SDavid Lechner } 177f2e60041SDavid Lechner 178f2e60041SDavid Lechner d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops); 179f2e60041SDavid Lechner if (IS_ERR(d_phy->usb20_phy)) { 180f2e60041SDavid Lechner dev_err(dev, "Failed to create usb20 phy\n"); 181f2e60041SDavid Lechner return PTR_ERR(d_phy->usb20_phy); 182f2e60041SDavid Lechner } 183f2e60041SDavid Lechner 184f2e60041SDavid Lechner platform_set_drvdata(pdev, d_phy); 185f2e60041SDavid Lechner phy_set_drvdata(d_phy->usb11_phy, d_phy); 186f2e60041SDavid Lechner phy_set_drvdata(d_phy->usb20_phy, d_phy); 187f2e60041SDavid Lechner 188f2e60041SDavid Lechner if (node) { 189f2e60041SDavid Lechner d_phy->phy_provider = devm_of_phy_provider_register(dev, 190f2e60041SDavid Lechner da8xx_usb_phy_of_xlate); 191f2e60041SDavid Lechner if (IS_ERR(d_phy->phy_provider)) { 192f2e60041SDavid Lechner dev_err(dev, "Failed to create phy provider\n"); 193f2e60041SDavid Lechner return PTR_ERR(d_phy->phy_provider); 194f2e60041SDavid Lechner } 195f2e60041SDavid Lechner } else { 196f2e60041SDavid Lechner int ret; 197f2e60041SDavid Lechner 19876632542SAxel Haslam ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy", 19976632542SAxel Haslam "ohci-da8xx"); 200f2e60041SDavid Lechner if (ret) 201f2e60041SDavid Lechner dev_warn(dev, "Failed to create usb11 phy lookup\n"); 202f2e60041SDavid Lechner ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy", 203f2e60041SDavid Lechner "musb-da8xx"); 204f2e60041SDavid Lechner if (ret) 205f2e60041SDavid Lechner dev_warn(dev, "Failed to create usb20 phy lookup\n"); 206f2e60041SDavid Lechner } 207f2e60041SDavid Lechner 20823e9b38dSAlexandre Bailon regmap_write_bits(d_phy->regmap, CFGCHIP(2), 20923e9b38dSAlexandre Bailon PHY_INIT_BITS, PHY_INIT_BITS); 21023e9b38dSAlexandre Bailon 211f2e60041SDavid Lechner return 0; 212f2e60041SDavid Lechner } 213f2e60041SDavid Lechner 214f2e60041SDavid Lechner static int da8xx_usb_phy_remove(struct platform_device *pdev) 215f2e60041SDavid Lechner { 216f2e60041SDavid Lechner struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev); 217f2e60041SDavid Lechner 218f2e60041SDavid Lechner if (!pdev->dev.of_node) { 219f2e60041SDavid Lechner phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx"); 22076632542SAxel Haslam phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci-da8xx"); 221f2e60041SDavid Lechner } 222f2e60041SDavid Lechner 223f2e60041SDavid Lechner return 0; 224f2e60041SDavid Lechner } 225f2e60041SDavid Lechner 226f2e60041SDavid Lechner static const struct of_device_id da8xx_usb_phy_ids[] = { 227f2e60041SDavid Lechner { .compatible = "ti,da830-usb-phy" }, 228f2e60041SDavid Lechner { } 229f2e60041SDavid Lechner }; 230f2e60041SDavid Lechner MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids); 231f2e60041SDavid Lechner 232f2e60041SDavid Lechner static struct platform_driver da8xx_usb_phy_driver = { 233f2e60041SDavid Lechner .probe = da8xx_usb_phy_probe, 234f2e60041SDavid Lechner .remove = da8xx_usb_phy_remove, 235f2e60041SDavid Lechner .driver = { 236f2e60041SDavid Lechner .name = "da8xx-usb-phy", 237f2e60041SDavid Lechner .of_match_table = da8xx_usb_phy_ids, 238f2e60041SDavid Lechner }, 239f2e60041SDavid Lechner }; 240f2e60041SDavid Lechner 241f2e60041SDavid Lechner module_platform_driver(da8xx_usb_phy_driver); 242f2e60041SDavid Lechner 243f2e60041SDavid Lechner MODULE_ALIAS("platform:da8xx-usb-phy"); 244f2e60041SDavid Lechner MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 245f2e60041SDavid Lechner MODULE_DESCRIPTION("TI DA8xx USB PHY driver"); 246f2e60041SDavid Lechner MODULE_LICENSE("GPL v2"); 247