Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.18 293 lines 7.4 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver 4 * 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.0 46 * from shutting PHY's power when USB1.1 might use it 47 */ 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 else 193 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 enable 255 * 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 = 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");