summaryrefslogtreecommitdiff
path: root/drivers/usb/host/ehci-sunxi.c
blob: 360efc9116086f2b021bdfe1b471516a5a3fb104 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// SPDX-License-Identifier: GPL-2.0+
/*
 * Sunxi ehci glue
 *
 * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
 * Copyright (C) 2014 Roman Byshko <rbyshko@gmail.com>
 *
 * Based on code from
 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
 */

#include <common.h>
#include <asm/arch/clock.h>
#include <asm/io.h>
#include <dm.h>
#include "ehci.h"
#include <generic-phy.h>

#ifdef CONFIG_SUNXI_GEN_SUN4I
#define AHB_CLK_DIST		2
#else
#define AHB_CLK_DIST		1
#endif

struct ehci_sunxi_priv {
	struct ehci_ctrl ehci;
	struct sunxi_ccm_reg *ccm;
	int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */
	struct phy phy;
};

static int ehci_usb_probe(struct udevice *dev)
{
	struct usb_platdata *plat = dev_get_platdata(dev);
	struct ehci_sunxi_priv *priv = dev_get_priv(dev);
	struct ehci_hccr *hccr = (struct ehci_hccr *)devfdt_get_addr(dev);
	struct ehci_hcor *hcor;
	int extra_ahb_gate_mask = 0;
	int phys, ret;

	priv->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
	if (IS_ERR(priv->ccm))
		return PTR_ERR(priv->ccm);

	phys = dev_count_phandle_with_args(dev, "phys", "#phy-cells");
	if (phys < 0) {
		phys = 0;
		goto no_phy;
	}

	ret = generic_phy_get_by_name(dev, "usb", &priv->phy);
	if (ret) {
		pr_err("failed to get %s usb PHY\n", dev->name);
		return ret;
	}

	ret = generic_phy_init(&priv->phy);
	if (ret) {
		pr_err("failed to init %s USB PHY\n", dev->name);
		return ret;
	}

	ret = generic_phy_power_on(&priv->phy);
	if (ret) {
		pr_err("failed to power on %s USB PHY\n", dev->name);
		return ret;
	}

no_phy:
	/*
	 * This should go away once we've moved to the driver model for
	 * clocks resp. phys.
	 */
	priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0;
#if defined(CONFIG_MACH_SUNXI_H3_H5) || defined(CONFIG_MACH_SUN50I)
	extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0;
#endif
	priv->ahb_gate_mask <<= phys * AHB_CLK_DIST;
	extra_ahb_gate_mask <<= phys * AHB_CLK_DIST;

	setbits_le32(&priv->ccm->ahb_gate0,
		     priv->ahb_gate_mask | extra_ahb_gate_mask);
#ifdef CONFIG_SUNXI_GEN_SUN6I
	setbits_le32(&priv->ccm->ahb_reset0_cfg,
		     priv->ahb_gate_mask | extra_ahb_gate_mask);
#endif

	hcor = (struct ehci_hcor *)((uintptr_t)hccr +
				    HC_LENGTH(ehci_readl(&hccr->cr_capbase)));

	return ehci_register(dev, hccr, hcor, NULL, 0, plat->init_type);
}

static int ehci_usb_remove(struct udevice *dev)
{
	struct ehci_sunxi_priv *priv = dev_get_priv(dev);
	int ret;

	if (generic_phy_valid(&priv->phy)) {
		ret = generic_phy_exit(&priv->phy);
		if (ret) {
			pr_err("failed to exit %s USB PHY\n", dev->name);
			return ret;
		}
	}

	ret = ehci_deregister(dev);
	if (ret)
		return ret;

#ifdef CONFIG_SUNXI_GEN_SUN6I
	clrbits_le32(&priv->ccm->ahb_reset0_cfg, priv->ahb_gate_mask);
#endif
	clrbits_le32(&priv->ccm->ahb_gate0, priv->ahb_gate_mask);

	return 0;
}

static const struct udevice_id ehci_usb_ids[] = {
	{ .compatible = "allwinner,sun4i-a10-ehci", },
	{ .compatible = "allwinner,sun5i-a13-ehci", },
	{ .compatible = "allwinner,sun6i-a31-ehci", },
	{ .compatible = "allwinner,sun7i-a20-ehci", },
	{ .compatible = "allwinner,sun8i-a23-ehci", },
	{ .compatible = "allwinner,sun8i-a83t-ehci", },
	{ .compatible = "allwinner,sun8i-h3-ehci",  },
	{ .compatible = "allwinner,sun9i-a80-ehci", },
	{ .compatible = "allwinner,sun50i-a64-ehci", },
	{ }
};

U_BOOT_DRIVER(ehci_sunxi) = {
	.name	= "ehci_sunxi",
	.id	= UCLASS_USB,
	.of_match = ehci_usb_ids,
	.probe = ehci_usb_probe,
	.remove = ehci_usb_remove,
	.ops	= &ehci_usb_ops,
	.platdata_auto_alloc_size = sizeof(struct usb_platdata),
	.priv_auto_alloc_size = sizeof(struct ehci_sunxi_priv),
	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
};