blob: db6f438275c016ad0647551af011865762006ef4 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Hans de Goede6a72e802015-05-10 14:10:27 +02002/*
3 * Sunxi ohci glue
4 *
5 * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
6 *
7 * Based on code from
8 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
Hans de Goede6a72e802015-05-10 14:10:27 +02009 */
10
11#include <common.h>
12#include <asm/arch/clock.h>
Hans de Goede6a72e802015-05-10 14:10:27 +020013#include <asm/io.h>
14#include <dm.h>
15#include <usb.h>
16#include "ohci.h"
Jagan Tekidd322812018-05-07 13:03:38 +053017#include <generic-phy.h>
Hans de Goede6a72e802015-05-10 14:10:27 +020018
Hans de Goede948603d2016-03-21 14:44:35 +010019#ifdef CONFIG_SUNXI_GEN_SUN4I
Hans de Goede948603d2016-03-21 14:44:35 +010020#define AHB_CLK_DIST 2
21#else
Hans de Goede948603d2016-03-21 14:44:35 +010022#define AHB_CLK_DIST 1
23#endif
24
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -070025#define SUN6I_AHB_RESET0_CFG_OFFSET 0x2c0
26#define SUN9I_AHB_RESET0_CFG_OFFSET 0x5a0
27
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -070028struct ohci_sunxi_cfg {
29 bool has_reset;
30 u32 extra_ahb_gate_mask;
Vasily Khoruzhick11bb6272018-06-07 19:23:41 -070031 u32 extra_usb_gate_mask;
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -070032 u32 reset0_cfg_offset;
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -070033};
34
Hans de Goede6a72e802015-05-10 14:10:27 +020035struct ohci_sunxi_priv {
Vasily Khoruzhickebbc23a2018-06-17 09:13:42 -070036 ohci_t ohci;
Jagan Teki831cc982018-05-07 13:03:17 +053037 struct sunxi_ccm_reg *ccm;
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -070038 u32 *reset0_cfg;
Hans de Goede6a72e802015-05-10 14:10:27 +020039 int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */
40 int usb_gate_mask; /* Mask of usb_clk_cfg clk gate bits for this hcd */
Jagan Tekidd322812018-05-07 13:03:38 +053041 struct phy phy;
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -070042 const struct ohci_sunxi_cfg *cfg;
Hans de Goede6a72e802015-05-10 14:10:27 +020043};
44
45static int ohci_usb_probe(struct udevice *dev)
46{
Hans de Goede6a72e802015-05-10 14:10:27 +020047 struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev);
48 struct ohci_sunxi_priv *priv = dev_get_priv(dev);
Simon Glassa821c4a2017-05-17 17:18:05 -060049 struct ohci_regs *regs = (struct ohci_regs *)devfdt_get_addr(dev);
Hans de Goedefb3bfbb2016-04-09 15:00:52 +020050 int extra_ahb_gate_mask = 0;
Jagan Tekidd322812018-05-07 13:03:38 +053051 int phys, ret;
Hans de Goede6a72e802015-05-10 14:10:27 +020052
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -070053 priv->cfg = (const struct ohci_sunxi_cfg *)dev_get_driver_data(dev);
Jagan Teki831cc982018-05-07 13:03:17 +053054 priv->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
55 if (IS_ERR(priv->ccm))
56 return PTR_ERR(priv->ccm);
57
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -070058 priv->reset0_cfg = (void *)priv->ccm +
59 priv->cfg->reset0_cfg_offset;
60
Jagan Tekidd322812018-05-07 13:03:38 +053061 phys = dev_count_phandle_with_args(dev, "phys", "#phy-cells");
62 if (phys < 0) {
63 phys = 0;
64 goto no_phy;
65 }
66
67 ret = generic_phy_get_by_name(dev, "usb", &priv->phy);
68 if (ret) {
69 pr_err("failed to get %s usb PHY\n", dev->name);
70 return ret;
71 }
72
73 ret = generic_phy_init(&priv->phy);
74 if (ret) {
75 pr_err("failed to init %s USB PHY\n", dev->name);
76 return ret;
77 }
78
79 ret = generic_phy_power_on(&priv->phy);
80 if (ret) {
81 pr_err("failed to power on %s USB PHY\n", dev->name);
82 return ret;
83 }
84
85no_phy:
Hans de Goede6a72e802015-05-10 14:10:27 +020086 bus_priv->companion = true;
87
88 /*
89 * This should go away once we've moved to the driver model for
90 * clocks resp. phys.
91 */
Jelle van der Waadc44fd82016-02-09 23:59:33 +010092 priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0;
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -070093 extra_ahb_gate_mask = priv->cfg->extra_ahb_gate_mask;
Jelle van der Waadc44fd82016-02-09 23:59:33 +010094 priv->usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK;
Jagan Tekidd322812018-05-07 13:03:38 +053095 priv->ahb_gate_mask <<= phys * AHB_CLK_DIST;
96 extra_ahb_gate_mask <<= phys * AHB_CLK_DIST;
97 priv->usb_gate_mask <<= phys;
Hans de Goede6a72e802015-05-10 14:10:27 +020098
Jagan Teki831cc982018-05-07 13:03:17 +053099 setbits_le32(&priv->ccm->ahb_gate0,
Hans de Goedefb3bfbb2016-04-09 15:00:52 +0200100 priv->ahb_gate_mask | extra_ahb_gate_mask);
Vasily Khoruzhick11bb6272018-06-07 19:23:41 -0700101 setbits_le32(&priv->ccm->usb_clk_cfg,
102 priv->usb_gate_mask | priv->cfg->extra_usb_gate_mask);
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -0700103 if (priv->cfg->has_reset)
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -0700104 setbits_le32(priv->reset0_cfg,
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -0700105 priv->ahb_gate_mask | extra_ahb_gate_mask);
Hans de Goede6a72e802015-05-10 14:10:27 +0200106
Hans de Goede6a72e802015-05-10 14:10:27 +0200107 return ohci_register(dev, regs);
108}
109
110static int ohci_usb_remove(struct udevice *dev)
111{
Hans de Goede6a72e802015-05-10 14:10:27 +0200112 struct ohci_sunxi_priv *priv = dev_get_priv(dev);
113 int ret;
114
Jagan Tekidd322812018-05-07 13:03:38 +0530115 if (generic_phy_valid(&priv->phy)) {
116 ret = generic_phy_exit(&priv->phy);
117 if (ret) {
118 pr_err("failed to exit %s USB PHY\n", dev->name);
119 return ret;
120 }
121 }
122
Hans de Goede6a72e802015-05-10 14:10:27 +0200123 ret = ohci_deregister(dev);
124 if (ret)
125 return ret;
126
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -0700127 if (priv->cfg->has_reset)
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -0700128 clrbits_le32(priv->reset0_cfg, priv->ahb_gate_mask);
Jagan Teki831cc982018-05-07 13:03:17 +0530129 clrbits_le32(&priv->ccm->usb_clk_cfg, priv->usb_gate_mask);
130 clrbits_le32(&priv->ccm->ahb_gate0, priv->ahb_gate_mask);
Hans de Goede6a72e802015-05-10 14:10:27 +0200131
132 return 0;
133}
134
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -0700135static const struct ohci_sunxi_cfg sun4i_a10_cfg = {
136 .has_reset = false,
137};
138
139static const struct ohci_sunxi_cfg sun6i_a31_cfg = {
140 .has_reset = true,
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -0700141 .reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET,
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -0700142};
143
144static const struct ohci_sunxi_cfg sun8i_h3_cfg = {
145 .has_reset = true,
146 .extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0,
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -0700147 .reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET,
148};
149
150static const struct ohci_sunxi_cfg sun9i_a80_cfg = {
151 .has_reset = true,
152 .reset0_cfg_offset = SUN9I_AHB_RESET0_CFG_OFFSET,
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -0700153};
154
Vasily Khoruzhick11bb6272018-06-07 19:23:41 -0700155static const struct ohci_sunxi_cfg sun50i_a64_cfg = {
156 .has_reset = true,
157 .extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0,
158 .extra_usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK,
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -0700159 .reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET,
Vasily Khoruzhick11bb6272018-06-07 19:23:41 -0700160};
161
Hans de Goede6a72e802015-05-10 14:10:27 +0200162static const struct udevice_id ohci_usb_ids[] = {
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -0700163 {
164 .compatible = "allwinner,sun4i-a10-ohci",
165 .data = (ulong)&sun4i_a10_cfg,
166 },
167 {
168 .compatible = "allwinner,sun5i-a13-ohci",
169 .data = (ulong)&sun4i_a10_cfg,
170 },
171 {
172 .compatible = "allwinner,sun6i-a31-ohci",
173 .data = (ulong)&sun6i_a31_cfg,
174 },
175 {
176 .compatible = "allwinner,sun7i-a20-ohci",
177 .data = (ulong)&sun4i_a10_cfg,
178 },
179 {
180 .compatible = "allwinner,sun8i-a23-ohci",
181 .data = (ulong)&sun6i_a31_cfg,
182 },
183 {
184 .compatible = "allwinner,sun8i-a83t-ohci",
185 .data = (ulong)&sun6i_a31_cfg,
186 },
187 {
188 .compatible = "allwinner,sun8i-h3-ohci",
189 .data = (ulong)&sun8i_h3_cfg,
190 },
191 {
192 .compatible = "allwinner,sun9i-a80-ohci",
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -0700193 .data = (ulong)&sun9i_a80_cfg,
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -0700194 },
195 {
196 .compatible = "allwinner,sun50i-a64-ohci",
Vasily Khoruzhick11bb6272018-06-07 19:23:41 -0700197 .data = (ulong)&sun50i_a64_cfg,
Vasily Khoruzhick56830ce2018-06-07 19:23:40 -0700198 },
199 { /* sentinel */ }
Hans de Goede6a72e802015-05-10 14:10:27 +0200200};
201
202U_BOOT_DRIVER(usb_ohci) = {
203 .name = "ohci_sunxi",
204 .id = UCLASS_USB,
205 .of_match = ohci_usb_ids,
206 .probe = ohci_usb_probe,
207 .remove = ohci_usb_remove,
208 .ops = &ohci_usb_ops,
209 .platdata_auto_alloc_size = sizeof(struct usb_platdata),
210 .priv_auto_alloc_size = sizeof(struct ohci_sunxi_priv),
211 .flags = DM_FLAG_ALLOC_PRIV_DMA,
212};