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
23static 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
46int 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
78int 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);
110phy_exit:
111	phy_exit(phy);
112phy_put:
113	of_phy_put(phy);
114phy_out:
115
116	return 0;
117}
118
119int 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