1 // SPDX-License-Identifier: GPL-2.0 2 /** 3 * dwc3-exynos.c - Samsung EXYNOS DWC3 Specific Glue layer 4 * 5 * Copyright (c) 2012 Samsung Electronics Co., Ltd. 6 * http://www.samsung.com 7 * 8 * Author: Anton Tikhomirov <av.tikhomirov@samsung.com> 9 */ 10 11 #include <linux/module.h> 12 #include <linux/kernel.h> 13 #include <linux/slab.h> 14 #include <linux/platform_device.h> 15 #include <linux/clk.h> 16 #include <linux/usb/otg.h> 17 #include <linux/usb/usb_phy_generic.h> 18 #include <linux/of.h> 19 #include <linux/of_platform.h> 20 #include <linux/regulator/consumer.h> 21 22 struct dwc3_exynos { 23 struct platform_device *usb2_phy; 24 struct platform_device *usb3_phy; 25 struct device *dev; 26 27 struct clk *clk; 28 struct clk *susp_clk; 29 struct clk *axius_clk; 30 31 struct regulator *vdd33; 32 struct regulator *vdd10; 33 }; 34 35 static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) 36 { 37 struct usb_phy_generic_platform_data pdata; 38 struct platform_device *pdev; 39 int ret; 40 41 memset(&pdata, 0x00, sizeof(pdata)); 42 43 pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO); 44 if (!pdev) 45 return -ENOMEM; 46 47 exynos->usb2_phy = pdev; 48 pdata.type = USB_PHY_TYPE_USB2; 49 pdata.gpio_reset = -1; 50 51 ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata)); 52 if (ret) 53 goto err1; 54 55 pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO); 56 if (!pdev) { 57 ret = -ENOMEM; 58 goto err1; 59 } 60 61 exynos->usb3_phy = pdev; 62 pdata.type = USB_PHY_TYPE_USB3; 63 64 ret = platform_device_add_data(exynos->usb3_phy, &pdata, sizeof(pdata)); 65 if (ret) 66 goto err2; 67 68 ret = platform_device_add(exynos->usb2_phy); 69 if (ret) 70 goto err2; 71 72 ret = platform_device_add(exynos->usb3_phy); 73 if (ret) 74 goto err3; 75 76 return 0; 77 78 err3: 79 platform_device_del(exynos->usb2_phy); 80 81 err2: 82 platform_device_put(exynos->usb3_phy); 83 84 err1: 85 platform_device_put(exynos->usb2_phy); 86 87 return ret; 88 } 89 90 static int dwc3_exynos_remove_child(struct device *dev, void *unused) 91 { 92 struct platform_device *pdev = to_platform_device(dev); 93 94 platform_device_unregister(pdev); 95 96 return 0; 97 } 98 99 static int dwc3_exynos_probe(struct platform_device *pdev) 100 { 101 struct dwc3_exynos *exynos; 102 struct device *dev = &pdev->dev; 103 struct device_node *node = dev->of_node; 104 105 int ret; 106 107 exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL); 108 if (!exynos) 109 return -ENOMEM; 110 111 platform_set_drvdata(pdev, exynos); 112 113 exynos->dev = dev; 114 115 exynos->clk = devm_clk_get(dev, "usbdrd30"); 116 if (IS_ERR(exynos->clk)) { 117 dev_err(dev, "couldn't get clock\n"); 118 return -EINVAL; 119 } 120 ret = clk_prepare_enable(exynos->clk); 121 if (ret) 122 return ret; 123 124 exynos->susp_clk = devm_clk_get(dev, "usbdrd30_susp_clk"); 125 if (IS_ERR(exynos->susp_clk)) 126 exynos->susp_clk = NULL; 127 ret = clk_prepare_enable(exynos->susp_clk); 128 if (ret) 129 goto susp_clk_err; 130 131 if (of_device_is_compatible(node, "samsung,exynos7-dwusb3")) { 132 exynos->axius_clk = devm_clk_get(dev, "usbdrd30_axius_clk"); 133 if (IS_ERR(exynos->axius_clk)) { 134 dev_err(dev, "no AXI UpScaler clk specified\n"); 135 ret = -ENODEV; 136 goto axius_clk_err; 137 } 138 ret = clk_prepare_enable(exynos->axius_clk); 139 if (ret) 140 goto axius_clk_err; 141 } else { 142 exynos->axius_clk = NULL; 143 } 144 145 exynos->vdd33 = devm_regulator_get(dev, "vdd33"); 146 if (IS_ERR(exynos->vdd33)) { 147 ret = PTR_ERR(exynos->vdd33); 148 goto vdd33_err; 149 } 150 ret = regulator_enable(exynos->vdd33); 151 if (ret) { 152 dev_err(dev, "Failed to enable VDD33 supply\n"); 153 goto vdd33_err; 154 } 155 156 exynos->vdd10 = devm_regulator_get(dev, "vdd10"); 157 if (IS_ERR(exynos->vdd10)) { 158 ret = PTR_ERR(exynos->vdd10); 159 goto vdd10_err; 160 } 161 ret = regulator_enable(exynos->vdd10); 162 if (ret) { 163 dev_err(dev, "Failed to enable VDD10 supply\n"); 164 goto vdd10_err; 165 } 166 167 ret = dwc3_exynos_register_phys(exynos); 168 if (ret) { 169 dev_err(dev, "couldn't register PHYs\n"); 170 goto phys_err; 171 } 172 173 if (node) { 174 ret = of_platform_populate(node, NULL, NULL, dev); 175 if (ret) { 176 dev_err(dev, "failed to add dwc3 core\n"); 177 goto populate_err; 178 } 179 } else { 180 dev_err(dev, "no device node, failed to add dwc3 core\n"); 181 ret = -ENODEV; 182 goto populate_err; 183 } 184 185 return 0; 186 187 populate_err: 188 platform_device_unregister(exynos->usb2_phy); 189 platform_device_unregister(exynos->usb3_phy); 190 phys_err: 191 regulator_disable(exynos->vdd10); 192 vdd10_err: 193 regulator_disable(exynos->vdd33); 194 vdd33_err: 195 clk_disable_unprepare(exynos->axius_clk); 196 axius_clk_err: 197 clk_disable_unprepare(exynos->susp_clk); 198 susp_clk_err: 199 clk_disable_unprepare(exynos->clk); 200 return ret; 201 } 202 203 static int dwc3_exynos_remove(struct platform_device *pdev) 204 { 205 struct dwc3_exynos *exynos = platform_get_drvdata(pdev); 206 207 device_for_each_child(&pdev->dev, NULL, dwc3_exynos_remove_child); 208 platform_device_unregister(exynos->usb2_phy); 209 platform_device_unregister(exynos->usb3_phy); 210 211 clk_disable_unprepare(exynos->axius_clk); 212 clk_disable_unprepare(exynos->susp_clk); 213 clk_disable_unprepare(exynos->clk); 214 215 regulator_disable(exynos->vdd33); 216 regulator_disable(exynos->vdd10); 217 218 return 0; 219 } 220 221 static const struct of_device_id exynos_dwc3_match[] = { 222 { .compatible = "samsung,exynos5250-dwusb3" }, 223 { .compatible = "samsung,exynos7-dwusb3" }, 224 {}, 225 }; 226 MODULE_DEVICE_TABLE(of, exynos_dwc3_match); 227 228 #ifdef CONFIG_PM_SLEEP 229 static int dwc3_exynos_suspend(struct device *dev) 230 { 231 struct dwc3_exynos *exynos = dev_get_drvdata(dev); 232 233 clk_disable(exynos->axius_clk); 234 clk_disable(exynos->clk); 235 236 regulator_disable(exynos->vdd33); 237 regulator_disable(exynos->vdd10); 238 239 return 0; 240 } 241 242 static int dwc3_exynos_resume(struct device *dev) 243 { 244 struct dwc3_exynos *exynos = dev_get_drvdata(dev); 245 int ret; 246 247 ret = regulator_enable(exynos->vdd33); 248 if (ret) { 249 dev_err(dev, "Failed to enable VDD33 supply\n"); 250 return ret; 251 } 252 ret = regulator_enable(exynos->vdd10); 253 if (ret) { 254 dev_err(dev, "Failed to enable VDD10 supply\n"); 255 return ret; 256 } 257 258 clk_enable(exynos->clk); 259 clk_enable(exynos->axius_clk); 260 261 /* runtime set active to reflect active state. */ 262 pm_runtime_disable(dev); 263 pm_runtime_set_active(dev); 264 pm_runtime_enable(dev); 265 266 return 0; 267 } 268 269 static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = { 270 SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend, dwc3_exynos_resume) 271 }; 272 273 #define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops) 274 #else 275 #define DEV_PM_OPS NULL 276 #endif /* CONFIG_PM_SLEEP */ 277 278 static struct platform_driver dwc3_exynos_driver = { 279 .probe = dwc3_exynos_probe, 280 .remove = dwc3_exynos_remove, 281 .driver = { 282 .name = "exynos-dwc3", 283 .of_match_table = exynos_dwc3_match, 284 .pm = DEV_PM_OPS, 285 }, 286 }; 287 288 module_platform_driver(dwc3_exynos_driver); 289 290 MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>"); 291 MODULE_LICENSE("GPL v2"); 292 MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer"); 293