blob: aa32af1f3aa6794f4eb9c619d24130267cc41d90 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Marek Vasutf7d7f942011-11-08 23:18:26 +00002/*
3 * Freescale i.MX28 USB Host driver
4 *
5 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
6 * on behalf of DENX Software Engineering GmbH
Marek Vasutf7d7f942011-11-08 23:18:26 +00007 */
8
9#include <common.h>
10#include <asm/io.h>
Marek Vasut83e330c2013-02-23 02:43:01 +000011#include <asm/arch/imx-regs.h>
Marek Vasut191a2ce2013-02-23 02:43:02 +000012#include <errno.h>
Simon Glassdbd79542020-05-10 11:40:11 -060013#include <linux/delay.h>
Marek Vasutf7d7f942011-11-08 23:18:26 +000014
Marek Vasutf7d7f942011-11-08 23:18:26 +000015#include "ehci.h"
16
Marek Vasut191a2ce2013-02-23 02:43:02 +000017/* This DIGCTL register ungates clock to USB */
18#define HW_DIGCTL_CTRL 0x8001c000
19#define HW_DIGCTL_CTRL_USB0_CLKGATE (1 << 2)
20#define HW_DIGCTL_CTRL_USB1_CLKGATE (1 << 16)
Marek Vasutf7d7f942011-11-08 23:18:26 +000021
Marek Vasut191a2ce2013-02-23 02:43:02 +000022struct ehci_mxs_port {
23 uint32_t usb_regs;
Otavio Salvador22f4ff92012-08-05 09:05:31 +000024 struct mxs_usbphy_regs *phy_regs;
Marek Vasut191a2ce2013-02-23 02:43:02 +000025
26 struct mxs_register_32 *pll;
27 uint32_t pll_en_bits;
28 uint32_t pll_dis_bits;
29 uint32_t gate_bits;
30};
31
Lukasz Majewski5fb7cb22021-12-22 10:55:07 +010032static int ehci_mxs_toggle_clock(const struct ehci_mxs_port *port, int enable)
33{
34 struct mxs_register_32 *digctl_ctrl =
35 (struct mxs_register_32 *)HW_DIGCTL_CTRL;
36 int pll_offset, dig_offset;
37
38 if (enable) {
39 pll_offset = offsetof(struct mxs_register_32, reg_set);
40 dig_offset = offsetof(struct mxs_register_32, reg_clr);
41 writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset);
42 writel(port->pll_en_bits, (u32)port->pll + pll_offset);
43 } else {
44 pll_offset = offsetof(struct mxs_register_32, reg_clr);
45 dig_offset = offsetof(struct mxs_register_32, reg_set);
46 writel(port->pll_dis_bits, (u32)port->pll + pll_offset);
47 writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset);
48 }
49
50 return 0;
51}
52
Lukasz Majewskie91edbc2021-12-22 10:55:08 +010053static int __ehci_hcd_init(struct ehci_mxs_port *port, enum usb_init_type init,
54 struct ehci_hccr **hccr, struct ehci_hcor **hcor)
55{
56 u32 usb_base, cap_base;
57 int ret;
58
59 /* Reset the PHY block */
60 writel(USBPHY_CTRL_SFTRST, &port->phy_regs->hw_usbphy_ctrl_set);
61 udelay(10);
62 writel(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE,
63 &port->phy_regs->hw_usbphy_ctrl_clr);
64
65 /* Enable USB clock */
66 ret = ehci_mxs_toggle_clock(port, 1);
67 if (ret)
68 return ret;
69
70 /* Start USB PHY */
71 writel(0, &port->phy_regs->hw_usbphy_pwd);
72
73 /* Enable UTMI+ Level 2 and Level 3 compatibility */
74 writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2 | 1,
75 &port->phy_regs->hw_usbphy_ctrl_set);
76
77 usb_base = port->usb_regs + 0x100;
78 *hccr = (struct ehci_hccr *)usb_base;
79
80 cap_base = ehci_readl(&(*hccr)->cr_capbase);
81 *hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base));
82
83 return 0;
84}
85
86static int __ehci_hcd_stop(struct ehci_mxs_port *port)
87{
88 u32 usb_base, cap_base, tmp;
89 struct ehci_hccr *hccr;
90 struct ehci_hcor *hcor;
91
92 /* Stop the USB port */
93 usb_base = port->usb_regs + 0x100;
94 hccr = (struct ehci_hccr *)usb_base;
95 cap_base = ehci_readl(&hccr->cr_capbase);
96 hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base));
97
98 tmp = ehci_readl(&hcor->or_usbcmd);
99 tmp &= ~CMD_RUN;
100 ehci_writel(&hcor->or_usbcmd, tmp);
101
102 /* Disable the PHY */
103 tmp = USBPHY_PWD_RXPWDRX | USBPHY_PWD_RXPWDDIFF |
104 USBPHY_PWD_RXPWD1PT1 | USBPHY_PWD_RXPWDENV |
105 USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS |
106 USBPHY_PWD_TXPWDFS;
107 writel(tmp, &port->phy_regs->hw_usbphy_pwd);
108
109 /* Disable USB clock */
110 return ehci_mxs_toggle_clock(port, 0);
111}
112
Marek Vasut191a2ce2013-02-23 02:43:02 +0000113static const struct ehci_mxs_port mxs_port[] = {
114#ifdef CONFIG_EHCI_MXS_PORT0
115 {
116 MXS_USBCTRL0_BASE,
117 (struct mxs_usbphy_regs *)MXS_USBPHY0_BASE,
118 (struct mxs_register_32 *)(MXS_CLKCTRL_BASE +
119 offsetof(struct mxs_clkctrl_regs,
120 hw_clkctrl_pll0ctrl0_reg)),
121 CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER,
122 CLKCTRL_PLL0CTRL0_EN_USB_CLKS,
123 HW_DIGCTL_CTRL_USB0_CLKGATE,
124 },
125#endif
126#ifdef CONFIG_EHCI_MXS_PORT1
127 {
128 MXS_USBCTRL1_BASE,
129 (struct mxs_usbphy_regs *)MXS_USBPHY1_BASE,
130 (struct mxs_register_32 *)(MXS_CLKCTRL_BASE +
131 offsetof(struct mxs_clkctrl_regs,
132 hw_clkctrl_pll1ctrl0_reg)),
133 CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER,
134 CLKCTRL_PLL1CTRL0_EN_USB_CLKS,
135 HW_DIGCTL_CTRL_USB1_CLKGATE,
136 },
137#endif
138};
Marek Vasutf7d7f942011-11-08 23:18:26 +0000139
Marek Vasut809860b2014-04-28 03:38:39 +0200140int __weak board_ehci_hcd_init(int port)
141{
142 return 0;
143}
144
145int __weak board_ehci_hcd_exit(int port)
146{
147 return 0;
148}
149
Troy Kisky7d6bbb92013-10-10 15:27:57 -0700150int ehci_hcd_init(int index, enum usb_init_type init,
151 struct ehci_hccr **hccr, struct ehci_hcor **hcor)
Marek Vasutf7d7f942011-11-08 23:18:26 +0000152{
153
154 int ret;
Marek Vasut191a2ce2013-02-23 02:43:02 +0000155 const struct ehci_mxs_port *port;
Marek Vasutf7d7f942011-11-08 23:18:26 +0000156
Marek Vasut191a2ce2013-02-23 02:43:02 +0000157 if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) {
158 printf("Invalid port index (index = %d)!\n", index);
159 return -EINVAL;
160 }
161
Marek Vasut809860b2014-04-28 03:38:39 +0200162 ret = board_ehci_hcd_init(index);
163 if (ret)
164 return ret;
165
Marek Vasut191a2ce2013-02-23 02:43:02 +0000166 port = &mxs_port[index];
Lukasz Majewskie91edbc2021-12-22 10:55:08 +0100167 return __ehci_hcd_init(port, init, hccr, hcor);
Marek Vasutf7d7f942011-11-08 23:18:26 +0000168}
169
Lucas Stach3494a4c2012-09-26 00:14:35 +0200170int ehci_hcd_stop(int index)
Marek Vasutf7d7f942011-11-08 23:18:26 +0000171{
172 int ret;
Marek Vasut191a2ce2013-02-23 02:43:02 +0000173 const struct ehci_mxs_port *port;
Marek Vasutf7d7f942011-11-08 23:18:26 +0000174
Marek Vasut191a2ce2013-02-23 02:43:02 +0000175 if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) {
176 printf("Invalid port index (index = %d)!\n", index);
177 return -EINVAL;
178 }
179
180 port = &mxs_port[index];
Marek Vasutf7d7f942011-11-08 23:18:26 +0000181
Lukasz Majewskie91edbc2021-12-22 10:55:08 +0100182 ret = __ehci_hcd_stop(port);
Marek Vasut809860b2014-04-28 03:38:39 +0200183 board_ehci_hcd_exit(index);
184
Marek Vasut191a2ce2013-02-23 02:43:02 +0000185 return ret;
Marek Vasutf7d7f942011-11-08 23:18:26 +0000186}