blob: 116fb23aebd9966587d0c3aca4db24b66235662f [file] [log] [blame]
Thomas Gleixner9c92ab62019-05-29 07:17:56 -07001// SPDX-License-Identifier: GPL-2.0-only
Al Cooper49859e52017-09-22 15:34:01 -04002/*
3 * phy-brcm-usb.c - Broadcom USB Phy Driver
4 *
5 * Copyright (C) 2015-2017 Broadcom
Al Cooper49859e52017-09-22 15:34:01 -04006 */
7
8#include <linux/clk.h>
9#include <linux/delay.h>
10#include <linux/err.h>
11#include <linux/io.h>
12#include <linux/module.h>
13#include <linux/of.h>
Rafał Miłeckid14f4cc2020-12-16 15:33:04 +010014#include <linux/of_device.h>
Al Cooper49859e52017-09-22 15:34:01 -040015#include <linux/phy/phy.h>
16#include <linux/platform_device.h>
17#include <linux/interrupt.h>
18#include <linux/soc/brcmstb/brcmstb.h>
19#include <dt-bindings/phy/phy.h>
Al Cooper9d5f51d2020-01-03 13:18:06 -050020#include <linux/mfd/syscon.h>
Al Cooper49859e52017-09-22 15:34:01 -040021
22#include "phy-brcm-usb-init.h"
23
Al Cooper415060b2017-09-22 15:34:02 -040024static DEFINE_MUTEX(sysfs_lock);
25
Al Cooper49859e52017-09-22 15:34:01 -040026enum brcm_usb_phy_id {
27 BRCM_USB_PHY_2_0 = 0,
28 BRCM_USB_PHY_3_0,
29 BRCM_USB_PHY_ID_MAX
30};
31
32struct value_to_name_map {
33 int value;
34 const char *name;
35};
36
Al Cooper9d5f51d2020-01-03 13:18:06 -050037struct match_chip_info {
Rafał Miłecki915f1d22020-12-16 15:33:05 +010038 void (*init_func)(struct brcm_usb_init_params *params);
Al Cooper9d5f51d2020-01-03 13:18:06 -050039 u8 required_regs[BRCM_REGS_MAX + 1];
Al Cooperbed63b62020-01-03 13:18:09 -050040 u8 optional_reg;
Al Cooper9d5f51d2020-01-03 13:18:06 -050041};
42
Rikard Falkebornc79cc3d2020-05-16 14:04:41 +020043static const struct value_to_name_map brcm_dr_mode_to_name[] = {
Al Cooper49859e52017-09-22 15:34:01 -040044 { USB_CTLR_MODE_HOST, "host" },
45 { USB_CTLR_MODE_DEVICE, "peripheral" },
46 { USB_CTLR_MODE_DRD, "drd" },
47 { USB_CTLR_MODE_TYPEC_PD, "typec-pd" }
48};
49
Rikard Falkebornc79cc3d2020-05-16 14:04:41 +020050static const struct value_to_name_map brcm_dual_mode_to_name[] = {
Al Cooper415060b2017-09-22 15:34:02 -040051 { 0, "host" },
52 { 1, "device" },
53 { 2, "auto" },
54};
55
Al Cooper49859e52017-09-22 15:34:01 -040056struct brcm_usb_phy {
57 struct phy *phy;
58 unsigned int id;
59 bool inited;
60};
61
62struct brcm_usb_phy_data {
63 struct brcm_usb_init_params ini;
64 bool has_eohci;
65 bool has_xhci;
66 struct clk *usb_20_clk;
67 struct clk *usb_30_clk;
Al Cooperf1c0db42020-01-03 13:18:02 -050068 struct clk *suspend_clk;
Al Cooper49859e52017-09-22 15:34:01 -040069 struct mutex mutex; /* serialize phy init */
70 int init_count;
Al Cooperf1c0db42020-01-03 13:18:02 -050071 int wake_irq;
Al Cooper49859e52017-09-22 15:34:01 -040072 struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX];
73};
74
Al Cooper9d5f51d2020-01-03 13:18:06 -050075static s8 *node_reg_names[BRCM_REGS_MAX] = {
Al Cooperbed63b62020-01-03 13:18:09 -050076 "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec"
Al Cooper9d5f51d2020-01-03 13:18:06 -050077};
78
Al Cooperf1c0db42020-01-03 13:18:02 -050079static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id)
80{
81 struct phy *gphy = dev_id;
82
83 pm_wakeup_event(&gphy->dev, 0);
84
85 return IRQ_HANDLED;
86}
87
Al Cooper49859e52017-09-22 15:34:01 -040088static int brcm_usb_phy_init(struct phy *gphy)
89{
90 struct brcm_usb_phy *phy = phy_get_drvdata(gphy);
91 struct brcm_usb_phy_data *priv =
92 container_of(phy, struct brcm_usb_phy_data, phys[phy->id]);
93
94 /*
95 * Use a lock to make sure a second caller waits until
96 * the base phy is inited before using it.
97 */
98 mutex_lock(&priv->mutex);
99 if (priv->init_count++ == 0) {
Al Cooperece5ffd2020-01-03 13:18:00 -0500100 clk_prepare_enable(priv->usb_20_clk);
101 clk_prepare_enable(priv->usb_30_clk);
Al Cooperf1c0db42020-01-03 13:18:02 -0500102 clk_prepare_enable(priv->suspend_clk);
Al Cooper49859e52017-09-22 15:34:01 -0400103 brcm_usb_init_common(&priv->ini);
104 }
105 mutex_unlock(&priv->mutex);
106 if (phy->id == BRCM_USB_PHY_2_0)
107 brcm_usb_init_eohci(&priv->ini);
108 else if (phy->id == BRCM_USB_PHY_3_0)
109 brcm_usb_init_xhci(&priv->ini);
110 phy->inited = true;
111 dev_dbg(&gphy->dev, "INIT, id: %d, total: %d\n", phy->id,
112 priv->init_count);
113
114 return 0;
115}
116
117static int brcm_usb_phy_exit(struct phy *gphy)
118{
119 struct brcm_usb_phy *phy = phy_get_drvdata(gphy);
120 struct brcm_usb_phy_data *priv =
121 container_of(phy, struct brcm_usb_phy_data, phys[phy->id]);
122
123 dev_dbg(&gphy->dev, "EXIT\n");
124 if (phy->id == BRCM_USB_PHY_2_0)
125 brcm_usb_uninit_eohci(&priv->ini);
126 if (phy->id == BRCM_USB_PHY_3_0)
127 brcm_usb_uninit_xhci(&priv->ini);
128
129 /* If both xhci and eohci are gone, reset everything else */
130 mutex_lock(&priv->mutex);
131 if (--priv->init_count == 0) {
132 brcm_usb_uninit_common(&priv->ini);
Al Cooperece5ffd2020-01-03 13:18:00 -0500133 clk_disable_unprepare(priv->usb_20_clk);
134 clk_disable_unprepare(priv->usb_30_clk);
Al Cooperf1c0db42020-01-03 13:18:02 -0500135 clk_disable_unprepare(priv->suspend_clk);
Al Cooper49859e52017-09-22 15:34:01 -0400136 }
137 mutex_unlock(&priv->mutex);
138 phy->inited = false;
139 return 0;
140}
141
Rikard Falkebornc79cc3d2020-05-16 14:04:41 +0200142static const struct phy_ops brcm_usb_phy_ops = {
Al Cooper49859e52017-09-22 15:34:01 -0400143 .init = brcm_usb_phy_init,
144 .exit = brcm_usb_phy_exit,
145 .owner = THIS_MODULE,
146};
147
148static struct phy *brcm_usb_phy_xlate(struct device *dev,
149 struct of_phandle_args *args)
150{
151 struct brcm_usb_phy_data *data = dev_get_drvdata(dev);
152
153 /*
154 * values 0 and 1 are for backward compatibility with
155 * device tree nodes from older bootloaders.
156 */
157 switch (args->args[0]) {
158 case 0:
159 case PHY_TYPE_USB2:
160 if (data->phys[BRCM_USB_PHY_2_0].phy)
161 return data->phys[BRCM_USB_PHY_2_0].phy;
162 dev_warn(dev, "Error, 2.0 Phy not found\n");
163 break;
164 case 1:
165 case PHY_TYPE_USB3:
166 if (data->phys[BRCM_USB_PHY_3_0].phy)
167 return data->phys[BRCM_USB_PHY_3_0].phy;
168 dev_warn(dev, "Error, 3.0 Phy not found\n");
169 break;
170 }
171 return ERR_PTR(-ENODEV);
172}
173
Rikard Falkebornc79cc3d2020-05-16 14:04:41 +0200174static int name_to_value(const struct value_to_name_map *table, int count,
Al Cooper49859e52017-09-22 15:34:01 -0400175 const char *name, int *value)
176{
177 int x;
178
179 *value = 0;
180 for (x = 0; x < count; x++) {
181 if (sysfs_streq(name, table[x].name)) {
182 *value = x;
183 return 0;
184 }
185 }
186 return -EINVAL;
187}
188
Rikard Falkebornc79cc3d2020-05-16 14:04:41 +0200189static const char *value_to_name(const struct value_to_name_map *table, int count,
Al Cooper415060b2017-09-22 15:34:02 -0400190 int value)
191{
192 if (value >= count)
193 return "unknown";
194 return table[value].name;
195}
196
197static ssize_t dr_mode_show(struct device *dev,
198 struct device_attribute *attr,
199 char *buf)
200{
201 struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
202
203 return sprintf(buf, "%s\n",
204 value_to_name(&brcm_dr_mode_to_name[0],
205 ARRAY_SIZE(brcm_dr_mode_to_name),
206 priv->ini.mode));
207}
208static DEVICE_ATTR_RO(dr_mode);
209
210static ssize_t dual_select_store(struct device *dev,
211 struct device_attribute *attr,
212 const char *buf, size_t len)
213{
214 struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
215 int value;
216 int res;
217
218 mutex_lock(&sysfs_lock);
219 res = name_to_value(&brcm_dual_mode_to_name[0],
220 ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value);
221 if (!res) {
Al Cooper94583a42020-01-03 13:18:03 -0500222 brcm_usb_set_dual_select(&priv->ini, value);
Al Cooper415060b2017-09-22 15:34:02 -0400223 res = len;
224 }
225 mutex_unlock(&sysfs_lock);
226 return res;
227}
228
229static ssize_t dual_select_show(struct device *dev,
230 struct device_attribute *attr,
231 char *buf)
232{
233 struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
234 int value;
235
236 mutex_lock(&sysfs_lock);
Al Cooper94583a42020-01-03 13:18:03 -0500237 value = brcm_usb_get_dual_select(&priv->ini);
Al Cooper415060b2017-09-22 15:34:02 -0400238 mutex_unlock(&sysfs_lock);
239 return sprintf(buf, "%s\n",
240 value_to_name(&brcm_dual_mode_to_name[0],
241 ARRAY_SIZE(brcm_dual_mode_to_name),
242 value));
243}
244static DEVICE_ATTR_RW(dual_select);
245
246static struct attribute *brcm_usb_phy_attrs[] = {
247 &dev_attr_dr_mode.attr,
248 &dev_attr_dual_select.attr,
249 NULL
250};
251
252static const struct attribute_group brcm_usb_phy_group = {
253 .attrs = brcm_usb_phy_attrs,
254};
255
Rikard Falkebornc79cc3d2020-05-16 14:04:41 +0200256static const struct match_chip_info chip_info_7216 = {
Al Cooper9d5f51d2020-01-03 13:18:06 -0500257 .init_func = &brcm_usb_dvr_init_7216,
258 .required_regs = {
259 BRCM_REGS_CTRL,
260 BRCM_REGS_XHCI_EC,
261 BRCM_REGS_XHCI_GBL,
262 -1,
263 },
264};
265
Rikard Falkebornc79cc3d2020-05-16 14:04:41 +0200266static const struct match_chip_info chip_info_7211b0 = {
Al Cooper9d5f51d2020-01-03 13:18:06 -0500267 .init_func = &brcm_usb_dvr_init_7211b0,
268 .required_regs = {
269 BRCM_REGS_CTRL,
270 BRCM_REGS_XHCI_EC,
271 BRCM_REGS_XHCI_GBL,
272 BRCM_REGS_USB_PHY,
273 BRCM_REGS_USB_MDIO,
274 -1,
275 },
Al Cooperbed63b62020-01-03 13:18:09 -0500276 .optional_reg = BRCM_REGS_BDC_EC,
Al Cooper9d5f51d2020-01-03 13:18:06 -0500277};
278
Rikard Falkebornc79cc3d2020-05-16 14:04:41 +0200279static const struct match_chip_info chip_info_7445 = {
Al Cooper9d5f51d2020-01-03 13:18:06 -0500280 .init_func = &brcm_usb_dvr_init_7445,
281 .required_regs = {
282 BRCM_REGS_CTRL,
283 BRCM_REGS_XHCI_EC,
284 -1,
285 },
286};
287
Al Cooper4e5b9c92020-01-03 13:18:05 -0500288static const struct of_device_id brcm_usb_dt_ids[] = {
289 {
Rafał Miłecki4b402fa2021-01-06 21:58:38 +0100290 .compatible = "brcm,bcm4908-usb-phy",
291 .data = &chip_info_7445,
292 },
293 {
Al Cooper4e5b9c92020-01-03 13:18:05 -0500294 .compatible = "brcm,bcm7216-usb-phy",
Al Cooper9d5f51d2020-01-03 13:18:06 -0500295 .data = &chip_info_7216,
Al Cooper4e5b9c92020-01-03 13:18:05 -0500296 },
Al Cooper9d5f51d2020-01-03 13:18:06 -0500297 {
298 .compatible = "brcm,bcm7211-usb-phy",
299 .data = &chip_info_7211b0,
300 },
301 {
302 .compatible = "brcm,brcmstb-usb-phy",
303 .data = &chip_info_7445,
304 },
Al Cooper4e5b9c92020-01-03 13:18:05 -0500305 { /* sentinel */ }
306};
307
Al Cooper9d5f51d2020-01-03 13:18:06 -0500308static int brcm_usb_get_regs(struct platform_device *pdev,
309 enum brcmusb_reg_sel regs,
Al Cooperbed63b62020-01-03 13:18:09 -0500310 struct brcm_usb_init_params *ini,
311 bool optional)
Al Cooper9d5f51d2020-01-03 13:18:06 -0500312{
313 struct resource *res;
314
315 /* Older DT nodes have ctrl and optional xhci_ec by index only */
316 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
317 node_reg_names[regs]);
318 if (res == NULL) {
319 if (regs == BRCM_REGS_CTRL) {
320 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
321 } else if (regs == BRCM_REGS_XHCI_EC) {
322 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
323 /* XHCI_EC registers are optional */
324 if (res == NULL)
325 return 0;
326 }
327 if (res == NULL) {
Al Cooperbed63b62020-01-03 13:18:09 -0500328 if (optional) {
329 dev_dbg(&pdev->dev,
330 "Optional reg %s not found\n",
331 node_reg_names[regs]);
332 return 0;
333 }
334 dev_err(&pdev->dev, "can't get %s base addr\n",
Al Cooper9d5f51d2020-01-03 13:18:06 -0500335 node_reg_names[regs]);
336 return 1;
337 }
338 }
339 ini->regs[regs] = devm_ioremap_resource(&pdev->dev, res);
340 if (IS_ERR(ini->regs[regs])) {
341 dev_err(&pdev->dev, "can't map %s register space\n",
342 node_reg_names[regs]);
343 return 1;
344 }
345 return 0;
346}
347
Al Cooperf1c0db42020-01-03 13:18:02 -0500348static int brcm_usb_phy_dvr_init(struct platform_device *pdev,
Al Cooper49859e52017-09-22 15:34:01 -0400349 struct brcm_usb_phy_data *priv,
350 struct device_node *dn)
351{
Al Cooperf1c0db42020-01-03 13:18:02 -0500352 struct device *dev = &pdev->dev;
353 struct phy *gphy = NULL;
Al Cooper49859e52017-09-22 15:34:01 -0400354 int err;
355
356 priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb");
357 if (IS_ERR(priv->usb_20_clk)) {
Al Cooper89927fe2020-01-03 13:18:07 -0500358 if (PTR_ERR(priv->usb_20_clk) == -EPROBE_DEFER)
359 return -EPROBE_DEFER;
Al Cooper49859e52017-09-22 15:34:01 -0400360 dev_info(dev, "Clock not found in Device Tree\n");
361 priv->usb_20_clk = NULL;
362 }
363 err = clk_prepare_enable(priv->usb_20_clk);
364 if (err)
365 return err;
366
367 if (priv->has_eohci) {
368 gphy = devm_phy_create(dev, NULL, &brcm_usb_phy_ops);
369 if (IS_ERR(gphy)) {
370 dev_err(dev, "failed to create EHCI/OHCI PHY\n");
371 return PTR_ERR(gphy);
372 }
373 priv->phys[BRCM_USB_PHY_2_0].phy = gphy;
374 priv->phys[BRCM_USB_PHY_2_0].id = BRCM_USB_PHY_2_0;
375 phy_set_drvdata(gphy, &priv->phys[BRCM_USB_PHY_2_0]);
376 }
377
378 if (priv->has_xhci) {
379 gphy = devm_phy_create(dev, NULL, &brcm_usb_phy_ops);
380 if (IS_ERR(gphy)) {
381 dev_err(dev, "failed to create XHCI PHY\n");
382 return PTR_ERR(gphy);
383 }
384 priv->phys[BRCM_USB_PHY_3_0].phy = gphy;
385 priv->phys[BRCM_USB_PHY_3_0].id = BRCM_USB_PHY_3_0;
386 phy_set_drvdata(gphy, &priv->phys[BRCM_USB_PHY_3_0]);
387
388 priv->usb_30_clk = of_clk_get_by_name(dn, "sw_usb3");
389 if (IS_ERR(priv->usb_30_clk)) {
Al Cooper89927fe2020-01-03 13:18:07 -0500390 if (PTR_ERR(priv->usb_30_clk) == -EPROBE_DEFER)
391 return -EPROBE_DEFER;
Al Cooper49859e52017-09-22 15:34:01 -0400392 dev_info(dev,
393 "USB3.0 clock not found in Device Tree\n");
394 priv->usb_30_clk = NULL;
395 }
396 err = clk_prepare_enable(priv->usb_30_clk);
397 if (err)
398 return err;
399 }
Al Cooperf1c0db42020-01-03 13:18:02 -0500400
401 priv->suspend_clk = clk_get(dev, "usb0_freerun");
402 if (IS_ERR(priv->suspend_clk)) {
Al Cooper89927fe2020-01-03 13:18:07 -0500403 if (PTR_ERR(priv->suspend_clk) == -EPROBE_DEFER)
404 return -EPROBE_DEFER;
Al Cooperf1c0db42020-01-03 13:18:02 -0500405 dev_err(dev, "Suspend Clock not found in Device Tree\n");
406 priv->suspend_clk = NULL;
407 }
408
409 priv->wake_irq = platform_get_irq_byname(pdev, "wake");
410 if (priv->wake_irq < 0)
411 priv->wake_irq = platform_get_irq_byname(pdev, "wakeup");
412 if (priv->wake_irq >= 0) {
413 err = devm_request_irq(dev, priv->wake_irq,
414 brcm_usb_phy_wake_isr, 0,
415 dev_name(dev), gphy);
416 if (err < 0)
417 return err;
418 device_set_wakeup_capable(dev, 1);
419 } else {
420 dev_info(dev,
421 "Wake interrupt missing, system wake not supported\n");
422 }
423
Al Cooper49859e52017-09-22 15:34:01 -0400424 return 0;
425}
426
427static int brcm_usb_phy_probe(struct platform_device *pdev)
428{
Al Cooper49859e52017-09-22 15:34:01 -0400429 struct device *dev = &pdev->dev;
430 struct brcm_usb_phy_data *priv;
431 struct phy_provider *phy_provider;
432 struct device_node *dn = pdev->dev.of_node;
433 int err;
434 const char *mode;
Al Cooper9d5f51d2020-01-03 13:18:06 -0500435 const struct match_chip_info *info;
436 struct regmap *rmap;
437 int x;
Al Cooper49859e52017-09-22 15:34:01 -0400438
439 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
440 if (!priv)
441 return -ENOMEM;
442 platform_set_drvdata(pdev, priv);
443
444 priv->ini.family_id = brcmstb_get_family_id();
445 priv->ini.product_id = brcmstb_get_product_id();
Al Cooper4e5b9c92020-01-03 13:18:05 -0500446
Rafał Miłeckid14f4cc2020-12-16 15:33:04 +0100447 info = of_device_get_match_data(&pdev->dev);
448 if (!info)
449 return -ENOENT;
Rafał Miłecki915f1d22020-12-16 15:33:05 +0100450
451 info->init_func(&priv->ini);
Al Cooper4e5b9c92020-01-03 13:18:05 -0500452
Al Cooper49859e52017-09-22 15:34:01 -0400453 dev_dbg(dev, "Best mapping table is for %s\n",
454 priv->ini.family_name);
Al Cooper4e5b9c92020-01-03 13:18:05 -0500455
Al Cooper49859e52017-09-22 15:34:01 -0400456 of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp);
457 of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc);
458
459 priv->ini.mode = USB_CTLR_MODE_HOST;
460 err = of_property_read_string(dn, "dr_mode", &mode);
461 if (err == 0) {
462 name_to_value(&brcm_dr_mode_to_name[0],
463 ARRAY_SIZE(brcm_dr_mode_to_name),
464 mode, &priv->ini.mode);
465 }
Al Cooper5e498ff2017-12-27 14:28:48 -0500466 if (of_property_read_bool(dn, "brcm,has-xhci"))
Al Cooper49859e52017-09-22 15:34:01 -0400467 priv->has_xhci = true;
Al Cooper5e498ff2017-12-27 14:28:48 -0500468 if (of_property_read_bool(dn, "brcm,has-eohci"))
Al Cooper49859e52017-09-22 15:34:01 -0400469 priv->has_eohci = true;
470
Al Cooper9d5f51d2020-01-03 13:18:06 -0500471 for (x = 0; x < BRCM_REGS_MAX; x++) {
472 if (info->required_regs[x] >= BRCM_REGS_MAX)
473 break;
474
475 err = brcm_usb_get_regs(pdev, info->required_regs[x],
Al Cooperbed63b62020-01-03 13:18:09 -0500476 &priv->ini, false);
477 if (err)
478 return -EINVAL;
479 }
480 if (info->optional_reg) {
481 err = brcm_usb_get_regs(pdev, info->optional_reg,
482 &priv->ini, true);
Al Cooper9d5f51d2020-01-03 13:18:06 -0500483 if (err)
484 return -EINVAL;
485 }
486
Al Cooperf1c0db42020-01-03 13:18:02 -0500487 err = brcm_usb_phy_dvr_init(pdev, priv, dn);
Al Cooper49859e52017-09-22 15:34:01 -0400488 if (err)
489 return err;
490
491 mutex_init(&priv->mutex);
492
493 /* make sure invert settings are correct */
494 brcm_usb_init_ipp(&priv->ini);
495
Al Cooper415060b2017-09-22 15:34:02 -0400496 /*
497 * Create sysfs entries for mode.
498 * Remove "dual_select" attribute if not in dual mode
499 */
500 if (priv->ini.mode != USB_CTLR_MODE_DRD)
501 brcm_usb_phy_attrs[1] = NULL;
502 err = sysfs_create_group(&dev->kobj, &brcm_usb_phy_group);
503 if (err)
504 dev_warn(dev, "Error creating sysfs attributes\n");
505
Al Cooper9d5f51d2020-01-03 13:18:06 -0500506 /* Get piarbctl syscon if it exists */
507 rmap = syscon_regmap_lookup_by_phandle(dev->of_node,
508 "syscon-piarbctl");
509 if (IS_ERR(rmap))
510 rmap = syscon_regmap_lookup_by_phandle(dev->of_node,
511 "brcm,syscon-piarbctl");
512 if (!IS_ERR(rmap))
513 priv->ini.syscon_piarbctl = rmap;
514
Al Cooper49859e52017-09-22 15:34:01 -0400515 /* start with everything off */
516 if (priv->has_xhci)
517 brcm_usb_uninit_xhci(&priv->ini);
518 if (priv->has_eohci)
519 brcm_usb_uninit_eohci(&priv->ini);
520 brcm_usb_uninit_common(&priv->ini);
Al Cooperece5ffd2020-01-03 13:18:00 -0500521 clk_disable_unprepare(priv->usb_20_clk);
522 clk_disable_unprepare(priv->usb_30_clk);
Al Cooper49859e52017-09-22 15:34:01 -0400523
524 phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate);
Al Cooper49859e52017-09-22 15:34:01 -0400525
zhong jiang52864812018-08-16 23:58:54 +0800526 return PTR_ERR_OR_ZERO(phy_provider);
Al Cooper49859e52017-09-22 15:34:01 -0400527}
528
Florian Fainellid9e10082019-05-22 11:35:25 -0700529static int brcm_usb_phy_remove(struct platform_device *pdev)
530{
531 sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group);
532
533 return 0;
534}
535
Al Cooper49859e52017-09-22 15:34:01 -0400536#ifdef CONFIG_PM_SLEEP
537static int brcm_usb_phy_suspend(struct device *dev)
538{
539 struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
540
541 if (priv->init_count) {
Al Cooperb0c0b662020-01-03 13:18:11 -0500542 priv->ini.wake_enabled = device_may_wakeup(dev);
Al Cooper6597af42020-01-03 13:18:01 -0500543 if (priv->phys[BRCM_USB_PHY_3_0].inited)
544 brcm_usb_uninit_xhci(&priv->ini);
545 if (priv->phys[BRCM_USB_PHY_2_0].inited)
546 brcm_usb_uninit_eohci(&priv->ini);
547 brcm_usb_uninit_common(&priv->ini);
Al Cooperb0c0b662020-01-03 13:18:11 -0500548
549 /*
550 * Handle the clocks unless needed for wake. This has
551 * to work for both older XHCI->3.0-clks, EOHCI->2.0-clks
552 * and newer XHCI->2.0-clks/3.0-clks.
553 */
554
555 if (!priv->ini.suspend_with_clocks) {
556 if (priv->phys[BRCM_USB_PHY_3_0].inited)
557 clk_disable_unprepare(priv->usb_30_clk);
558 if (priv->phys[BRCM_USB_PHY_2_0].inited ||
559 !priv->has_eohci)
560 clk_disable_unprepare(priv->usb_20_clk);
561 }
Al Cooperf1c0db42020-01-03 13:18:02 -0500562 if (priv->wake_irq >= 0)
563 enable_irq_wake(priv->wake_irq);
Al Cooper49859e52017-09-22 15:34:01 -0400564 }
565 return 0;
566}
567
568static int brcm_usb_phy_resume(struct device *dev)
569{
570 struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
571
Al Cooperece5ffd2020-01-03 13:18:00 -0500572 clk_prepare_enable(priv->usb_20_clk);
573 clk_prepare_enable(priv->usb_30_clk);
Al Cooper49859e52017-09-22 15:34:01 -0400574 brcm_usb_init_ipp(&priv->ini);
575
576 /*
577 * Initialize anything that was previously initialized.
578 * Uninitialize anything that wasn't previously initialized.
579 */
580 if (priv->init_count) {
Al Cooperf1c0db42020-01-03 13:18:02 -0500581 if (priv->wake_irq >= 0)
582 disable_irq_wake(priv->wake_irq);
Al Cooper49859e52017-09-22 15:34:01 -0400583 brcm_usb_init_common(&priv->ini);
584 if (priv->phys[BRCM_USB_PHY_2_0].inited) {
585 brcm_usb_init_eohci(&priv->ini);
586 } else if (priv->has_eohci) {
587 brcm_usb_uninit_eohci(&priv->ini);
Al Cooperece5ffd2020-01-03 13:18:00 -0500588 clk_disable_unprepare(priv->usb_20_clk);
Al Cooper49859e52017-09-22 15:34:01 -0400589 }
590 if (priv->phys[BRCM_USB_PHY_3_0].inited) {
591 brcm_usb_init_xhci(&priv->ini);
592 } else if (priv->has_xhci) {
593 brcm_usb_uninit_xhci(&priv->ini);
Al Cooperece5ffd2020-01-03 13:18:00 -0500594 clk_disable_unprepare(priv->usb_30_clk);
Al Cooperb0c0b662020-01-03 13:18:11 -0500595 if (!priv->has_eohci)
596 clk_disable_unprepare(priv->usb_20_clk);
Al Cooper49859e52017-09-22 15:34:01 -0400597 }
598 } else {
599 if (priv->has_xhci)
600 brcm_usb_uninit_xhci(&priv->ini);
601 if (priv->has_eohci)
602 brcm_usb_uninit_eohci(&priv->ini);
603 brcm_usb_uninit_common(&priv->ini);
Al Cooperece5ffd2020-01-03 13:18:00 -0500604 clk_disable_unprepare(priv->usb_20_clk);
605 clk_disable_unprepare(priv->usb_30_clk);
Al Cooper49859e52017-09-22 15:34:01 -0400606 }
Al Cooperb0c0b662020-01-03 13:18:11 -0500607 priv->ini.wake_enabled = false;
Al Cooper49859e52017-09-22 15:34:01 -0400608 return 0;
609}
610#endif /* CONFIG_PM_SLEEP */
611
612static const struct dev_pm_ops brcm_usb_phy_pm_ops = {
613 SET_LATE_SYSTEM_SLEEP_PM_OPS(brcm_usb_phy_suspend, brcm_usb_phy_resume)
614};
615
Al Cooper49859e52017-09-22 15:34:01 -0400616MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids);
617
618static struct platform_driver brcm_usb_driver = {
619 .probe = brcm_usb_phy_probe,
Florian Fainellid9e10082019-05-22 11:35:25 -0700620 .remove = brcm_usb_phy_remove,
Al Cooper49859e52017-09-22 15:34:01 -0400621 .driver = {
622 .name = "brcmstb-usb-phy",
Al Cooper49859e52017-09-22 15:34:01 -0400623 .pm = &brcm_usb_phy_pm_ops,
624 .of_match_table = brcm_usb_dt_ids,
625 },
626};
627
628module_platform_driver(brcm_usb_driver);
629
630MODULE_ALIAS("platform:brcmstb-usb-phy");
631MODULE_AUTHOR("Al Cooper <acooper@broadcom.com>");
632MODULE_DESCRIPTION("BRCM USB PHY driver");
633MODULE_LICENSE("GPL v2");