xref: /openbmc/linux/drivers/usb/host/xhci-mvebu.c (revision 8ffdff6a)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2014 Marvell
4  * Author: Gregory CLEMENT <gregory.clement@free-electrons.com>
5  */
6 
7 #include <linux/io.h>
8 #include <linux/mbus.h>
9 #include <linux/of.h>
10 #include <linux/platform_device.h>
11 #include <linux/phy/phy.h>
12 
13 #include <linux/usb.h>
14 #include <linux/usb/hcd.h>
15 
16 #include "xhci-mvebu.h"
17 #include "xhci.h"
18 
19 #define USB3_MAX_WINDOWS	4
20 #define USB3_WIN_CTRL(w)	(0x0 + ((w) * 8))
21 #define USB3_WIN_BASE(w)	(0x4 + ((w) * 8))
22 
23 static void xhci_mvebu_mbus_config(void __iomem *base,
24 			const struct mbus_dram_target_info *dram)
25 {
26 	int win;
27 
28 	/* Clear all existing windows */
29 	for (win = 0; win < USB3_MAX_WINDOWS; win++) {
30 		writel(0, base + USB3_WIN_CTRL(win));
31 		writel(0, base + USB3_WIN_BASE(win));
32 	}
33 
34 	/* Program each DRAM CS in a seperate window */
35 	for (win = 0; win < dram->num_cs; win++) {
36 		const struct mbus_dram_window *cs = dram->cs + win;
37 
38 		writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
39 		       (dram->mbus_dram_target_id << 4) | 1,
40 		       base + USB3_WIN_CTRL(win));
41 
42 		writel((cs->base & 0xffff0000), base + USB3_WIN_BASE(win));
43 	}
44 }
45 
46 int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
47 {
48 	struct device *dev = hcd->self.controller;
49 	struct platform_device *pdev = to_platform_device(dev);
50 	struct resource	*res;
51 	void __iomem *base;
52 	const struct mbus_dram_target_info *dram;
53 
54 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
55 	if (!res)
56 		return -ENODEV;
57 
58 	/*
59 	 * We don't use devm_ioremap() because this mapping should
60 	 * only exists for the duration of this probe function.
61 	 */
62 	base = ioremap(res->start, resource_size(res));
63 	if (!base)
64 		return -ENODEV;
65 
66 	dram = mv_mbus_dram_info();
67 	xhci_mvebu_mbus_config(base, dram);
68 
69 	/*
70 	 * This memory area was only needed to configure the MBus
71 	 * windows, and is therefore no longer useful.
72 	 */
73 	iounmap(base);
74 
75 	return 0;
76 }
77 
78 int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd)
79 {
80 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
81 	struct device *dev = hcd->self.controller;
82 	struct phy *phy;
83 	int ret;
84 
85 	/* Old bindings miss the PHY handle */
86 	phy = of_phy_get(dev->of_node, "usb3-phy");
87 	if (IS_ERR(phy) && PTR_ERR(phy) == -EPROBE_DEFER)
88 		return -EPROBE_DEFER;
89 	else if (IS_ERR(phy))
90 		goto phy_out;
91 
92 	ret = phy_init(phy);
93 	if (ret)
94 		goto phy_put;
95 
96 	ret = phy_set_mode(phy, PHY_MODE_USB_HOST_SS);
97 	if (ret)
98 		goto phy_exit;
99 
100 	ret = phy_power_on(phy);
101 	if (ret == -EOPNOTSUPP) {
102 		/* Skip initializatin of XHCI PHY when it is unsupported by firmware */
103 		dev_warn(dev, "PHY unsupported by firmware\n");
104 		xhci->quirks |= XHCI_SKIP_PHY_INIT;
105 	}
106 	if (ret)
107 		goto phy_exit;
108 
109 	phy_power_off(phy);
110 phy_exit:
111 	phy_exit(phy);
112 phy_put:
113 	of_phy_put(phy);
114 phy_out:
115 
116 	return 0;
117 }
118 
119 int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
120 {
121 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
122 
123 	/* Without reset on resume, the HC won't work at all */
124 	xhci->quirks |= XHCI_RESET_ON_RESUME;
125 
126 	return 0;
127 }
128