Merge tag 'v6.6-rc7.vfs.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[platform/kernel/linux-rpi.git] / drivers / usb / cdns3 / cdns3-starfive.c
1 // SPDX-License-Identifier: GPL-2.0
2 /**
3  * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller
4  *
5  * Copyright (C) 2023 StarFive Technology Co., Ltd.
6  *
7  * Author:      Minda Chen <minda.chen@starfivetech.com>
8  */
9
10 #include <linux/bits.h>
11 #include <linux/clk.h>
12 #include <linux/module.h>
13 #include <linux/mfd/syscon.h>
14 #include <linux/kernel.h>
15 #include <linux/platform_device.h>
16 #include <linux/io.h>
17 #include <linux/of_platform.h>
18 #include <linux/reset.h>
19 #include <linux/regmap.h>
20 #include <linux/usb/otg.h>
21 #include "core.h"
22
23 #define USB_STRAP_HOST                  BIT(17)
24 #define USB_STRAP_DEVICE                BIT(18)
25 #define USB_STRAP_MASK                  GENMASK(18, 16)
26
27 #define USB_SUSPENDM_HOST               BIT(19)
28 #define USB_SUSPENDM_MASK               BIT(19)
29
30 #define USB_MISC_CFG_MASK               GENMASK(23, 20)
31 #define USB_SUSPENDM_BYPS               BIT(20)
32 #define USB_PLL_EN                      BIT(22)
33 #define USB_REFCLK_MODE                 BIT(23)
34
35 struct cdns_starfive {
36         struct device *dev;
37         struct regmap *stg_syscon;
38         struct reset_control *resets;
39         struct clk_bulk_data *clks;
40         int num_clks;
41         u32 stg_usb_mode;
42 };
43
44 static void cdns_mode_init(struct platform_device *pdev,
45                            struct cdns_starfive *data)
46 {
47         enum usb_dr_mode mode;
48
49         regmap_update_bits(data->stg_syscon, data->stg_usb_mode,
50                            USB_MISC_CFG_MASK,
51                            USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE);
52
53         /* dr mode setting */
54         mode = usb_get_dr_mode(&pdev->dev);
55
56         switch (mode) {
57         case USB_DR_MODE_HOST:
58                 regmap_update_bits(data->stg_syscon,
59                                    data->stg_usb_mode,
60                                    USB_STRAP_MASK,
61                                    USB_STRAP_HOST);
62                 regmap_update_bits(data->stg_syscon,
63                                    data->stg_usb_mode,
64                                    USB_SUSPENDM_MASK,
65                                    USB_SUSPENDM_HOST);
66                 break;
67
68         case USB_DR_MODE_PERIPHERAL:
69                 regmap_update_bits(data->stg_syscon, data->stg_usb_mode,
70                                    USB_STRAP_MASK, USB_STRAP_DEVICE);
71                 regmap_update_bits(data->stg_syscon, data->stg_usb_mode,
72                                    USB_SUSPENDM_MASK, 0);
73                 break;
74         default:
75                 break;
76         }
77 }
78
79 static int cdns_clk_rst_init(struct cdns_starfive *data)
80 {
81         int ret;
82
83         ret = clk_bulk_prepare_enable(data->num_clks, data->clks);
84         if (ret)
85                 return dev_err_probe(data->dev, ret,
86                                      "failed to enable clocks\n");
87
88         ret = reset_control_deassert(data->resets);
89         if (ret) {
90                 dev_err(data->dev, "failed to reset clocks\n");
91                 goto err_clk_init;
92         }
93
94         return ret;
95
96 err_clk_init:
97         clk_bulk_disable_unprepare(data->num_clks, data->clks);
98         return ret;
99 }
100
101 static void cdns_clk_rst_deinit(struct cdns_starfive *data)
102 {
103         reset_control_assert(data->resets);
104         clk_bulk_disable_unprepare(data->num_clks, data->clks);
105 }
106
107 static int cdns_starfive_probe(struct platform_device *pdev)
108 {
109         struct device *dev = &pdev->dev;
110         struct cdns_starfive *data;
111         unsigned int args;
112         int ret;
113
114         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
115         if (!data)
116                 return -ENOMEM;
117
118         data->dev = dev;
119
120         data->stg_syscon =
121                 syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node,
122                                                      "starfive,stg-syscon", 1, &args);
123
124         if (IS_ERR(data->stg_syscon))
125                 return dev_err_probe(dev, PTR_ERR(data->stg_syscon),
126                                      "Failed to parse starfive,stg-syscon\n");
127
128         data->stg_usb_mode = args;
129
130         data->num_clks = devm_clk_bulk_get_all(data->dev, &data->clks);
131         if (data->num_clks < 0)
132                 return dev_err_probe(data->dev, -ENODEV,
133                                      "Failed to get clocks\n");
134
135         data->resets = devm_reset_control_array_get_exclusive(data->dev);
136         if (IS_ERR(data->resets))
137                 return dev_err_probe(data->dev, PTR_ERR(data->resets),
138                                      "Failed to get resets");
139
140         cdns_mode_init(pdev, data);
141         ret = cdns_clk_rst_init(data);
142         if (ret)
143                 return ret;
144
145         ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
146         if (ret) {
147                 dev_err(dev, "Failed to create children\n");
148                 cdns_clk_rst_deinit(data);
149                 return ret;
150         }
151
152         device_set_wakeup_capable(dev, true);
153         pm_runtime_set_active(dev);
154         pm_runtime_enable(dev);
155         platform_set_drvdata(pdev, data);
156
157         return 0;
158 }
159
160 static int cdns_starfive_remove_core(struct device *dev, void *c)
161 {
162         struct platform_device *pdev = to_platform_device(dev);
163
164         platform_device_unregister(pdev);
165
166         return 0;
167 }
168
169 static void cdns_starfive_remove(struct platform_device *pdev)
170 {
171         struct device *dev = &pdev->dev;
172         struct cdns_starfive *data = dev_get_drvdata(dev);
173
174         pm_runtime_get_sync(dev);
175         device_for_each_child(dev, NULL, cdns_starfive_remove_core);
176
177         pm_runtime_disable(dev);
178         pm_runtime_put_noidle(dev);
179         cdns_clk_rst_deinit(data);
180         platform_set_drvdata(pdev, NULL);
181 }
182
183 #ifdef CONFIG_PM
184 static int cdns_starfive_runtime_resume(struct device *dev)
185 {
186         struct cdns_starfive *data = dev_get_drvdata(dev);
187
188         return clk_bulk_prepare_enable(data->num_clks, data->clks);
189 }
190
191 static int cdns_starfive_runtime_suspend(struct device *dev)
192 {
193         struct cdns_starfive *data = dev_get_drvdata(dev);
194
195         clk_bulk_disable_unprepare(data->num_clks, data->clks);
196
197         return 0;
198 }
199
200 #ifdef CONFIG_PM_SLEEP
201 static int cdns_starfive_resume(struct device *dev)
202 {
203         struct cdns_starfive *data = dev_get_drvdata(dev);
204
205         return cdns_clk_rst_init(data);
206 }
207
208 static int cdns_starfive_suspend(struct device *dev)
209 {
210         struct cdns_starfive *data = dev_get_drvdata(dev);
211
212         cdns_clk_rst_deinit(data);
213
214         return 0;
215 }
216 #endif
217 #endif
218
219 static const struct dev_pm_ops cdns_starfive_pm_ops = {
220         SET_RUNTIME_PM_OPS(cdns_starfive_runtime_suspend,
221                            cdns_starfive_runtime_resume, NULL)
222         SET_SYSTEM_SLEEP_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume)
223 };
224
225 static const struct of_device_id cdns_starfive_of_match[] = {
226         { .compatible = "starfive,jh7110-usb", },
227         { /* sentinel */ }
228 };
229 MODULE_DEVICE_TABLE(of, cdns_starfive_of_match);
230
231 static struct platform_driver cdns_starfive_driver = {
232         .probe          = cdns_starfive_probe,
233         .remove_new     = cdns_starfive_remove,
234         .driver         = {
235                 .name   = "cdns3-starfive",
236                 .of_match_table = cdns_starfive_of_match,
237                 .pm     = &cdns_starfive_pm_ops,
238         },
239 };
240 module_platform_driver(cdns_starfive_driver);
241
242 MODULE_ALIAS("platform:cdns3-starfive");
243 MODULE_LICENSE("GPL v2");
244 MODULE_DESCRIPTION("Cadence USB3 StarFive Glue Layer");