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 <linux/regmap.h> 14 #include <linux/mfd/syscon.h> 15 #include "glue.h" 16 17 #define EIC7700_HSP_BUS_FILTER_EN BIT(0) 18 #define EIC7700_HSP_BUS_CLKEN_GM BIT(9) 19 #define EIC7700_HSP_BUS_CLKEN_GS BIT(16) 20 #define EIC7700_HSP_AXI_LP_XM_CSYSREQ BIT(0) 21 #define EIC7700_HSP_AXI_LP_XS_CSYSREQ BIT(16) 22 23 struct dwc3_generic { 24 struct device *dev; 25 struct dwc3 dwc; 26 struct clk_bulk_data *clks; 27 int num_clocks; 28 struct reset_control *resets; 29 }; 30 31 struct dwc3_generic_config { 32 int (*init)(struct dwc3_generic *dwc3g); 33 struct dwc3_properties properties; 34 }; 35 36 #define to_dwc3_generic(d) container_of((d), struct dwc3_generic, dwc) 37 38 static void dwc3_generic_reset_control_assert(void *data) 39 { 40 reset_control_assert(data); 41 } 42 43 static int dwc3_eic7700_init(struct dwc3_generic *dwc3g) 44 { 45 struct device *dev = dwc3g->dev; 46 struct regmap *regmap; 47 u32 hsp_usb_axi_lp; 48 u32 hsp_usb_bus; 49 u32 args[2]; 50 u32 val; 51 52 regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, 53 "eswin,hsp-sp-csr", 54 ARRAY_SIZE(args), args); 55 if (IS_ERR(regmap)) { 56 dev_err(dev, "No hsp-sp-csr phandle specified\n"); 57 return PTR_ERR(regmap); 58 } 59 60 hsp_usb_bus = args[0]; 61 hsp_usb_axi_lp = args[1]; 62 63 regmap_read(regmap, hsp_usb_bus, &val); 64 regmap_write(regmap, hsp_usb_bus, val | EIC7700_HSP_BUS_FILTER_EN | 65 EIC7700_HSP_BUS_CLKEN_GM | EIC7700_HSP_BUS_CLKEN_GS); 66 67 regmap_write(regmap, hsp_usb_axi_lp, EIC7700_HSP_AXI_LP_XM_CSYSREQ | 68 EIC7700_HSP_AXI_LP_XS_CSYSREQ); 69 return 0; 70 } 71 72 static int dwc3_generic_probe(struct platform_device *pdev) 73 { 74 const struct dwc3_generic_config *plat_config; 75 struct dwc3_probe_data probe_data = {}; 76 struct device *dev = &pdev->dev; 77 struct dwc3_generic *dwc3g; 78 struct resource *res; 79 int ret; 80 81 dwc3g = devm_kzalloc(dev, sizeof(*dwc3g), GFP_KERNEL); 82 if (!dwc3g) 83 return -ENOMEM; 84 85 dwc3g->dev = dev; 86 87 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 88 if (!res) { 89 dev_err(&pdev->dev, "missing memory resource\n"); 90 return -ENODEV; 91 } 92 93 dwc3g->resets = devm_reset_control_array_get_optional_exclusive(dev); 94 if (IS_ERR(dwc3g->resets)) 95 return dev_err_probe(dev, PTR_ERR(dwc3g->resets), "failed to get resets\n"); 96 97 ret = reset_control_assert(dwc3g->resets); 98 if (ret) 99 return dev_err_probe(dev, ret, "failed to assert resets\n"); 100 101 /* Not strict timing, just for safety */ 102 udelay(2); 103 104 ret = reset_control_deassert(dwc3g->resets); 105 if (ret) 106 return dev_err_probe(dev, ret, "failed to deassert resets\n"); 107 108 ret = devm_add_action_or_reset(dev, dwc3_generic_reset_control_assert, dwc3g->resets); 109 if (ret) 110 return ret; 111 112 ret = devm_clk_bulk_get_all_enabled(dwc3g->dev, &dwc3g->clks); 113 if (ret < 0) 114 return dev_err_probe(dev, ret, "failed to get clocks\n"); 115 116 dwc3g->num_clocks = ret; 117 dwc3g->dwc.dev = dev; 118 probe_data.dwc = &dwc3g->dwc; 119 probe_data.res = res; 120 probe_data.ignore_clocks_and_resets = true; 121 122 plat_config = of_device_get_match_data(dev); 123 if (!plat_config) { 124 probe_data.properties = DWC3_DEFAULT_PROPERTIES; 125 goto core_probe; 126 } 127 128 probe_data.properties = plat_config->properties; 129 if (plat_config->init) { 130 ret = plat_config->init(dwc3g); 131 if (ret) 132 return dev_err_probe(dev, ret, 133 "failed to init platform\n"); 134 } 135 136 core_probe: 137 ret = dwc3_core_probe(&probe_data); 138 if (ret) 139 return dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); 140 141 return 0; 142 } 143 144 static void dwc3_generic_remove(struct platform_device *pdev) 145 { 146 struct dwc3 *dwc = platform_get_drvdata(pdev); 147 148 dwc3_core_remove(dwc); 149 } 150 151 static int dwc3_generic_suspend(struct device *dev) 152 { 153 struct dwc3 *dwc = dev_get_drvdata(dev); 154 struct dwc3_generic *dwc3g = to_dwc3_generic(dwc); 155 int ret; 156 157 ret = dwc3_pm_suspend(dwc); 158 if (ret) 159 return ret; 160 161 clk_bulk_disable_unprepare(dwc3g->num_clocks, dwc3g->clks); 162 163 return 0; 164 } 165 166 static int dwc3_generic_resume(struct device *dev) 167 { 168 struct dwc3 *dwc = dev_get_drvdata(dev); 169 struct dwc3_generic *dwc3g = to_dwc3_generic(dwc); 170 int ret; 171 172 ret = clk_bulk_prepare_enable(dwc3g->num_clocks, dwc3g->clks); 173 if (ret) 174 return ret; 175 176 ret = dwc3_pm_resume(dwc); 177 if (ret) 178 return ret; 179 180 return 0; 181 } 182 183 static int dwc3_generic_runtime_suspend(struct device *dev) 184 { 185 return dwc3_runtime_suspend(dev_get_drvdata(dev)); 186 } 187 188 static int dwc3_generic_runtime_resume(struct device *dev) 189 { 190 return dwc3_runtime_resume(dev_get_drvdata(dev)); 191 } 192 193 static int dwc3_generic_runtime_idle(struct device *dev) 194 { 195 return dwc3_runtime_idle(dev_get_drvdata(dev)); 196 } 197 198 static const struct dev_pm_ops dwc3_generic_dev_pm_ops = { 199 SYSTEM_SLEEP_PM_OPS(dwc3_generic_suspend, dwc3_generic_resume) 200 RUNTIME_PM_OPS(dwc3_generic_runtime_suspend, dwc3_generic_runtime_resume, 201 dwc3_generic_runtime_idle) 202 }; 203 204 static const struct dwc3_generic_config fsl_ls1028_dwc3 = { 205 .properties.gsbuscfg0_reqinfo = 0x2222, 206 }; 207 208 static const struct dwc3_generic_config eic7700_dwc3 = { 209 .init = dwc3_eic7700_init, 210 .properties = DWC3_DEFAULT_PROPERTIES, 211 }; 212 213 static const struct of_device_id dwc3_generic_of_match[] = { 214 { .compatible = "spacemit,k1-dwc3", }, 215 { .compatible = "fsl,ls1028a-dwc3", &fsl_ls1028_dwc3}, 216 { .compatible = "eswin,eic7700-dwc3", &eic7700_dwc3}, 217 { /* sentinel */ } 218 }; 219 MODULE_DEVICE_TABLE(of, dwc3_generic_of_match); 220 221 static struct platform_driver dwc3_generic_driver = { 222 .probe = dwc3_generic_probe, 223 .remove = dwc3_generic_remove, 224 .driver = { 225 .name = "dwc3-generic-plat", 226 .of_match_table = dwc3_generic_of_match, 227 .pm = pm_ptr(&dwc3_generic_dev_pm_ops), 228 }, 229 }; 230 module_platform_driver(dwc3_generic_driver); 231 232 MODULE_LICENSE("GPL"); 233 MODULE_DESCRIPTION("DesignWare USB3 generic platform driver"); 234