1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * dwc3-generic-plat.c - DesignWare USB3 generic platform driver 4 * 5 * Copyright (C) 2025 Ze Huang <huang.ze@linux.dev> 6 * 7 * Inspired by dwc3-qcom.c and dwc3-of-simple.c 8 */ 9 10 #include <linux/clk.h> 11 #include <linux/platform_device.h> 12 #include <linux/reset.h> 13 #include "glue.h" 14 15 struct dwc3_generic { 16 struct device *dev; 17 struct dwc3 dwc; 18 struct clk_bulk_data *clks; 19 int num_clocks; 20 struct reset_control *resets; 21 }; 22 23 #define to_dwc3_generic(d) container_of((d), struct dwc3_generic, dwc) 24 25 static void dwc3_generic_reset_control_assert(void *data) 26 { 27 reset_control_assert(data); 28 } 29 30 static int dwc3_generic_probe(struct platform_device *pdev) 31 { 32 struct dwc3_probe_data probe_data = {}; 33 struct device *dev = &pdev->dev; 34 struct dwc3_generic *dwc3g; 35 struct resource *res; 36 int ret; 37 38 dwc3g = devm_kzalloc(dev, sizeof(*dwc3g), GFP_KERNEL); 39 if (!dwc3g) 40 return -ENOMEM; 41 42 dwc3g->dev = dev; 43 44 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 45 if (!res) { 46 dev_err(&pdev->dev, "missing memory resource\n"); 47 return -ENODEV; 48 } 49 50 dwc3g->resets = devm_reset_control_array_get_optional_exclusive(dev); 51 if (IS_ERR(dwc3g->resets)) 52 return dev_err_probe(dev, PTR_ERR(dwc3g->resets), "failed to get resets\n"); 53 54 ret = reset_control_assert(dwc3g->resets); 55 if (ret) 56 return dev_err_probe(dev, ret, "failed to assert resets\n"); 57 58 /* Not strict timing, just for safety */ 59 udelay(2); 60 61 ret = reset_control_deassert(dwc3g->resets); 62 if (ret) 63 return dev_err_probe(dev, ret, "failed to deassert resets\n"); 64 65 ret = devm_add_action_or_reset(dev, dwc3_generic_reset_control_assert, dwc3g->resets); 66 if (ret) 67 return ret; 68 69 ret = devm_clk_bulk_get_all_enabled(dwc3g->dev, &dwc3g->clks); 70 if (ret < 0) 71 return dev_err_probe(dev, ret, "failed to get clocks\n"); 72 73 dwc3g->num_clocks = ret; 74 dwc3g->dwc.dev = dev; 75 probe_data.dwc = &dwc3g->dwc; 76 probe_data.res = res; 77 probe_data.ignore_clocks_and_resets = true; 78 ret = dwc3_core_probe(&probe_data); 79 if (ret) 80 return dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); 81 82 return 0; 83 } 84 85 static void dwc3_generic_remove(struct platform_device *pdev) 86 { 87 struct dwc3 *dwc = platform_get_drvdata(pdev); 88 struct dwc3_generic *dwc3g = to_dwc3_generic(dwc); 89 90 dwc3_core_remove(dwc); 91 92 clk_bulk_disable_unprepare(dwc3g->num_clocks, dwc3g->clks); 93 } 94 95 static int dwc3_generic_suspend(struct device *dev) 96 { 97 struct dwc3 *dwc = dev_get_drvdata(dev); 98 struct dwc3_generic *dwc3g = to_dwc3_generic(dwc); 99 int ret; 100 101 ret = dwc3_pm_suspend(dwc); 102 if (ret) 103 return ret; 104 105 clk_bulk_disable_unprepare(dwc3g->num_clocks, dwc3g->clks); 106 107 return 0; 108 } 109 110 static int dwc3_generic_resume(struct device *dev) 111 { 112 struct dwc3 *dwc = dev_get_drvdata(dev); 113 struct dwc3_generic *dwc3g = to_dwc3_generic(dwc); 114 int ret; 115 116 ret = clk_bulk_prepare_enable(dwc3g->num_clocks, dwc3g->clks); 117 if (ret) 118 return ret; 119 120 ret = dwc3_pm_resume(dwc); 121 if (ret) 122 return ret; 123 124 return 0; 125 } 126 127 static int dwc3_generic_runtime_suspend(struct device *dev) 128 { 129 return dwc3_runtime_suspend(dev_get_drvdata(dev)); 130 } 131 132 static int dwc3_generic_runtime_resume(struct device *dev) 133 { 134 return dwc3_runtime_resume(dev_get_drvdata(dev)); 135 } 136 137 static int dwc3_generic_runtime_idle(struct device *dev) 138 { 139 return dwc3_runtime_idle(dev_get_drvdata(dev)); 140 } 141 142 static const struct dev_pm_ops dwc3_generic_dev_pm_ops = { 143 SYSTEM_SLEEP_PM_OPS(dwc3_generic_suspend, dwc3_generic_resume) 144 RUNTIME_PM_OPS(dwc3_generic_runtime_suspend, dwc3_generic_runtime_resume, 145 dwc3_generic_runtime_idle) 146 }; 147 148 static const struct of_device_id dwc3_generic_of_match[] = { 149 { .compatible = "spacemit,k1-dwc3", }, 150 { /* sentinel */ } 151 }; 152 MODULE_DEVICE_TABLE(of, dwc3_generic_of_match); 153 154 static struct platform_driver dwc3_generic_driver = { 155 .probe = dwc3_generic_probe, 156 .remove = dwc3_generic_remove, 157 .driver = { 158 .name = "dwc3-generic-plat", 159 .of_match_table = dwc3_generic_of_match, 160 .pm = pm_ptr(&dwc3_generic_dev_pm_ops), 161 }, 162 }; 163 module_platform_driver(dwc3_generic_driver); 164 165 MODULE_LICENSE("GPL"); 166 MODULE_DESCRIPTION("DesignWare USB3 generic platform driver"); 167