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 89 dwc3_core_remove(dwc); 90 } 91 92 static int dwc3_generic_suspend(struct device *dev) 93 { 94 struct dwc3 *dwc = dev_get_drvdata(dev); 95 struct dwc3_generic *dwc3g = to_dwc3_generic(dwc); 96 int ret; 97 98 ret = dwc3_pm_suspend(dwc); 99 if (ret) 100 return ret; 101 102 clk_bulk_disable_unprepare(dwc3g->num_clocks, dwc3g->clks); 103 104 return 0; 105 } 106 107 static int dwc3_generic_resume(struct device *dev) 108 { 109 struct dwc3 *dwc = dev_get_drvdata(dev); 110 struct dwc3_generic *dwc3g = to_dwc3_generic(dwc); 111 int ret; 112 113 ret = clk_bulk_prepare_enable(dwc3g->num_clocks, dwc3g->clks); 114 if (ret) 115 return ret; 116 117 ret = dwc3_pm_resume(dwc); 118 if (ret) 119 return ret; 120 121 return 0; 122 } 123 124 static int dwc3_generic_runtime_suspend(struct device *dev) 125 { 126 return dwc3_runtime_suspend(dev_get_drvdata(dev)); 127 } 128 129 static int dwc3_generic_runtime_resume(struct device *dev) 130 { 131 return dwc3_runtime_resume(dev_get_drvdata(dev)); 132 } 133 134 static int dwc3_generic_runtime_idle(struct device *dev) 135 { 136 return dwc3_runtime_idle(dev_get_drvdata(dev)); 137 } 138 139 static const struct dev_pm_ops dwc3_generic_dev_pm_ops = { 140 SYSTEM_SLEEP_PM_OPS(dwc3_generic_suspend, dwc3_generic_resume) 141 RUNTIME_PM_OPS(dwc3_generic_runtime_suspend, dwc3_generic_runtime_resume, 142 dwc3_generic_runtime_idle) 143 }; 144 145 static const struct of_device_id dwc3_generic_of_match[] = { 146 { .compatible = "spacemit,k1-dwc3", }, 147 { /* sentinel */ } 148 }; 149 MODULE_DEVICE_TABLE(of, dwc3_generic_of_match); 150 151 static struct platform_driver dwc3_generic_driver = { 152 .probe = dwc3_generic_probe, 153 .remove = dwc3_generic_remove, 154 .driver = { 155 .name = "dwc3-generic-plat", 156 .of_match_table = dwc3_generic_of_match, 157 .pm = pm_ptr(&dwc3_generic_dev_pm_ops), 158 }, 159 }; 160 module_platform_driver(dwc3_generic_driver); 161 162 MODULE_LICENSE("GPL"); 163 MODULE_DESCRIPTION("DesignWare USB3 generic platform driver"); 164