summaryrefslogtreecommitdiff
path: root/drivers/usb/host/ohci-sunxi.c
blob: ce2b47a5c4bacbbf4965c2f32da9dfda878c9d50 (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
143
144
145
// SPDX-License-Identifier: GPL-2.0+
/*
 * Sunxi ohci glue
 *
 * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.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 <usb.h>
#include "ohci.h"
#include <generic-phy.h>

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

struct ohci_sunxi_priv {
	struct sunxi_ccm_reg *ccm;
	ohci_t ohci;
	int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */
	int usb_gate_mask; /* Mask of usb_clk_cfg clk gate bits for this hcd */
	struct phy phy;
};

static int ohci_usb_probe(struct udevice *dev)
{
	struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev);
	struct ohci_sunxi_priv *priv = dev_get_priv(dev);
	struct ohci_regs *regs = (struct ohci_regs *)devfdt_get_addr(dev);
	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:
	bus_priv->companion = true;

	/*
	 * 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_OHCI0;
#ifdef CONFIG_MACH_SUN8I_H3
	extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0;
#endif
	priv->usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK;
	priv->ahb_gate_mask <<= phys * AHB_CLK_DIST;
	extra_ahb_gate_mask <<= phys * AHB_CLK_DIST;
	priv->usb_gate_mask <<= phys;

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

	return ohci_register(dev, regs);
}

static int ohci_usb_remove(struct udevice *dev)
{
	struct ohci_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 = ohci_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->usb_clk_cfg, priv->usb_gate_mask);
	clrbits_le32(&priv->ccm->ahb_gate0, priv->ahb_gate_mask);

	return 0;
}

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

U_BOOT_DRIVER(usb_ohci) = {
	.name	= "ohci_sunxi",
	.id	= UCLASS_USB,
	.of_match = ohci_usb_ids,
	.probe = ohci_usb_probe,
	.remove = ohci_usb_remove,
	.ops	= &ohci_usb_ops,
	.platdata_auto_alloc_size = sizeof(struct usb_platdata),
	.priv_auto_alloc_size = sizeof(struct ohci_sunxi_priv),
	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
};