File Index
Files and directories
Sign in
FILE

dwc3-generic-plat.c

File preview with syntax highlighting.

linux-6.19.14/drivers/usb/dwc3/dwc3-generic-plat.c C
  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
 23struct 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
 31struct 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
 38static void dwc3_generic_reset_control_assert(void *data)
 39{
 40	reset_control_assert(data);
 41}
 42
 43static 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
 72static 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
136core_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
144static 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
151static 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
166static 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
183static int dwc3_generic_runtime_suspend(struct device *dev)
184{
185	return dwc3_runtime_suspend(dev_get_drvdata(dev));
186}
187
188static int dwc3_generic_runtime_resume(struct device *dev)
189{
190	return dwc3_runtime_resume(dev_get_drvdata(dev));
191}
192
193static int dwc3_generic_runtime_idle(struct device *dev)
194{
195	return dwc3_runtime_idle(dev_get_drvdata(dev));
196}
197
198static 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
204static const struct dwc3_generic_config fsl_ls1028_dwc3 = {
205	.properties.gsbuscfg0_reqinfo = 0x2222,
206};
207
208static const struct dwc3_generic_config eic7700_dwc3 =  {
209	.init = dwc3_eic7700_init,
210	.properties = DWC3_DEFAULT_PROPERTIES,
211};
212
213static 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};
219MODULE_DEVICE_TABLE(of, dwc3_generic_of_match);
220
221static 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};
230module_platform_driver(dwc3_generic_driver);
231
232MODULE_LICENSE("GPL");
233MODULE_DESCRIPTION("DesignWare USB3 generic platform driver");
~/ Read-only file preview.