blob: 7a79931a979b2ea1b4027d91c70c50e884194787 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Roman Byshko8d154002014-07-27 19:32:44 +02002/*
Hans de Goede7b798652015-04-27 14:54:47 +02003 * Sunxi ehci glue
Roman Byshko8d154002014-07-27 19:32:44 +02004 *
Hans de Goede7b798652015-04-27 14:54:47 +02005 * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
6 * Copyright (C) 2014 Roman Byshko <rbyshko@gmail.com>
Roman Byshko8d154002014-07-27 19:32:44 +02007 *
8 * Based on code from
9 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
Roman Byshko8d154002014-07-27 19:32:44 +020010 */
11
Roman Byshko8d154002014-07-27 19:32:44 +020012#include <common.h>
Hans de Goede375de012015-04-27 11:44:22 +020013#include <asm/arch/clock.h>
Hans de Goede375de012015-04-27 11:44:22 +020014#include <asm/io.h>
Hans de Goede8d837a12015-05-10 14:10:26 +020015#include <dm.h>
Roman Byshko8d154002014-07-27 19:32:44 +020016#include "ehci.h"
Jagan Tekidd322812018-05-07 13:03:38 +053017#include <generic-phy.h>
Roman Byshko8d154002014-07-27 19:32:44 +020018
Hans de Goede948603d2016-03-21 14:44:35 +010019#ifdef CONFIG_SUNXI_GEN_SUN4I
Jagan Teki9c22aec2018-06-28 19:40:46 +053020#define BASE_DIST 0x8000
Hans de Goede948603d2016-03-21 14:44:35 +010021#define AHB_CLK_DIST 2
22#else
Jagan Teki9c22aec2018-06-28 19:40:46 +053023#define BASE_DIST 0x1000
Hans de Goede948603d2016-03-21 14:44:35 +010024#define AHB_CLK_DIST 1
25#endif
26
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -070027#define SUN6I_AHB_RESET0_CFG_OFFSET 0x2c0
28#define SUN9I_AHB_RESET0_CFG_OFFSET 0x5a0
29
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -070030struct ehci_sunxi_cfg {
31 bool has_reset;
32 u32 extra_ahb_gate_mask;
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -070033 u32 reset0_cfg_offset;
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -070034};
35
Hans de Goede8d837a12015-05-10 14:10:26 +020036struct ehci_sunxi_priv {
37 struct ehci_ctrl ehci;
Jagan Teki831cc982018-05-07 13:03:17 +053038 struct sunxi_ccm_reg *ccm;
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -070039 u32 *reset0_cfg;
Hans de Goede8d837a12015-05-10 14:10:26 +020040 int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */
Jagan Tekidd322812018-05-07 13:03:38 +053041 struct phy phy;
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -070042 const struct ehci_sunxi_cfg *cfg;
Hans de Goede8d837a12015-05-10 14:10:26 +020043};
44
45static int ehci_usb_probe(struct udevice *dev)
Roman Byshko8d154002014-07-27 19:32:44 +020046{
Hans de Goede8d837a12015-05-10 14:10:26 +020047 struct usb_platdata *plat = dev_get_platdata(dev);
48 struct ehci_sunxi_priv *priv = dev_get_priv(dev);
Simon Glassa821c4a2017-05-17 17:18:05 -060049 struct ehci_hccr *hccr = (struct ehci_hccr *)devfdt_get_addr(dev);
Hans de Goede8d837a12015-05-10 14:10:26 +020050 struct ehci_hcor *hcor;
Hans de Goedefb3bfbb2016-04-09 15:00:52 +020051 int extra_ahb_gate_mask = 0;
Jagan Teki9c22aec2018-06-28 19:40:46 +053052 u8 reg_mask = 0;
Jagan Tekidd322812018-05-07 13:03:38 +053053 int phys, ret;
Hans de Goede115200c2014-11-07 16:09:00 +010054
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -070055 priv->cfg = (const struct ehci_sunxi_cfg *)dev_get_driver_data(dev);
Jagan Teki831cc982018-05-07 13:03:17 +053056 priv->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
57 if (IS_ERR(priv->ccm))
58 return PTR_ERR(priv->ccm);
59
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -070060 priv->reset0_cfg = (void *)priv->ccm +
61 priv->cfg->reset0_cfg_offset;
62
Jagan Tekidd322812018-05-07 13:03:38 +053063 phys = dev_count_phandle_with_args(dev, "phys", "#phy-cells");
64 if (phys < 0) {
65 phys = 0;
66 goto no_phy;
67 }
68
69 ret = generic_phy_get_by_name(dev, "usb", &priv->phy);
70 if (ret) {
71 pr_err("failed to get %s usb PHY\n", dev->name);
72 return ret;
73 }
74
75 ret = generic_phy_init(&priv->phy);
76 if (ret) {
77 pr_err("failed to init %s USB PHY\n", dev->name);
78 return ret;
79 }
80
81 ret = generic_phy_power_on(&priv->phy);
82 if (ret) {
83 pr_err("failed to power on %s USB PHY\n", dev->name);
84 return ret;
85 }
86
87no_phy:
Hans de Goede8d837a12015-05-10 14:10:26 +020088 /*
89 * This should go away once we've moved to the driver model for
90 * clocks resp. phys.
91 */
Jagan Teki9c22aec2018-06-28 19:40:46 +053092 reg_mask = ((uintptr_t)hccr - SUNXI_USB1_BASE) / BASE_DIST;
Jelle van der Waadc44fd82016-02-09 23:59:33 +010093 priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0;
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -070094 extra_ahb_gate_mask = priv->cfg->extra_ahb_gate_mask;
Jagan Teki9c22aec2018-06-28 19:40:46 +053095 priv->ahb_gate_mask <<= reg_mask * AHB_CLK_DIST;
96 extra_ahb_gate_mask <<= reg_mask * AHB_CLK_DIST;
Hans de Goede8d837a12015-05-10 14:10:26 +020097
Jagan Teki831cc982018-05-07 13:03:17 +053098 setbits_le32(&priv->ccm->ahb_gate0,
Hans de Goedefb3bfbb2016-04-09 15:00:52 +020099 priv->ahb_gate_mask | extra_ahb_gate_mask);
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -0700100 if (priv->cfg->has_reset)
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -0700101 setbits_le32(priv->reset0_cfg,
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -0700102 priv->ahb_gate_mask | extra_ahb_gate_mask);
Hans de Goede375de012015-04-27 11:44:22 +0200103
Amit Singh Tomar9d6c9d92016-10-21 02:24:30 +0100104 hcor = (struct ehci_hcor *)((uintptr_t)hccr +
Hans de Goede8d837a12015-05-10 14:10:26 +0200105 HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
Roman Byshko8d154002014-07-27 19:32:44 +0200106
Hans de Goede8d837a12015-05-10 14:10:26 +0200107 return ehci_register(dev, hccr, hcor, NULL, 0, plat->init_type);
108}
Roman Byshko8d154002014-07-27 19:32:44 +0200109
Hans de Goede8d837a12015-05-10 14:10:26 +0200110static int ehci_usb_remove(struct udevice *dev)
111{
Hans de Goede8d837a12015-05-10 14:10:26 +0200112 struct ehci_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 Goede8d837a12015-05-10 14:10:26 +0200123 ret = ehci_deregister(dev);
124 if (ret)
125 return ret;
126
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -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->ahb_gate0, priv->ahb_gate_mask);
Roman Byshko8d154002014-07-27 19:32:44 +0200130
Roman Byshko8d154002014-07-27 19:32:44 +0200131 return 0;
132}
133
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -0700134static const struct ehci_sunxi_cfg sun4i_a10_cfg = {
135 .has_reset = false,
136};
137
138static const struct ehci_sunxi_cfg sun6i_a31_cfg = {
139 .has_reset = true,
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -0700140 .reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET,
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -0700141};
142
143static const struct ehci_sunxi_cfg sun8i_h3_cfg = {
144 .has_reset = true,
145 .extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0,
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -0700146 .reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET,
147};
148
149static const struct ehci_sunxi_cfg sun9i_a80_cfg = {
150 .has_reset = true,
151 .reset0_cfg_offset = SUN9I_AHB_RESET0_CFG_OFFSET,
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -0700152};
153
Hans de Goede8d837a12015-05-10 14:10:26 +0200154static const struct udevice_id ehci_usb_ids[] = {
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -0700155 {
156 .compatible = "allwinner,sun4i-a10-ehci",
157 .data = (ulong)&sun4i_a10_cfg,
158 },
159 {
160 .compatible = "allwinner,sun5i-a13-ehci",
161 .data = (ulong)&sun4i_a10_cfg,
162 },
163 {
164 .compatible = "allwinner,sun6i-a31-ehci",
165 .data = (ulong)&sun6i_a31_cfg,
166 },
167 {
168 .compatible = "allwinner,sun7i-a20-ehci",
169 .data = (ulong)&sun4i_a10_cfg,
170 },
171 {
172 .compatible = "allwinner,sun8i-a23-ehci",
173 .data = (ulong)&sun6i_a31_cfg,
174 },
175 {
176 .compatible = "allwinner,sun8i-a83t-ehci",
177 .data = (ulong)&sun6i_a31_cfg,
178 },
179 {
180 .compatible = "allwinner,sun8i-h3-ehci",
181 .data = (ulong)&sun8i_h3_cfg,
182 },
183 {
184 .compatible = "allwinner,sun9i-a80-ehci",
Vasily Khoruzhickb9f34752018-06-13 23:19:34 -0700185 .data = (ulong)&sun9i_a80_cfg,
Vasily Khoruzhick1ed9c112018-06-07 19:23:39 -0700186 },
187 {
188 .compatible = "allwinner,sun50i-a64-ehci",
189 .data = (ulong)&sun8i_h3_cfg,
190 },
191 { /* sentinel */ }
Hans de Goede8d837a12015-05-10 14:10:26 +0200192};
Hans de Goede375de012015-04-27 11:44:22 +0200193
Marek Vasut40c92082015-11-30 18:15:33 +0100194U_BOOT_DRIVER(ehci_sunxi) = {
Hans de Goede8d837a12015-05-10 14:10:26 +0200195 .name = "ehci_sunxi",
196 .id = UCLASS_USB,
197 .of_match = ehci_usb_ids,
198 .probe = ehci_usb_probe,
199 .remove = ehci_usb_remove,
200 .ops = &ehci_usb_ops,
201 .platdata_auto_alloc_size = sizeof(struct usb_platdata),
202 .priv_auto_alloc_size = sizeof(struct ehci_sunxi_priv),
203 .flags = DM_FLAG_ALLOC_PRIV_DMA,
204};