| 
 | 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");  | 
0 commit comments