blob: 5bee130425d5dfc84cf5bfeddae162e295a712b5 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +01002/*
Álvaro Fernández Rojasf02a81a2018-03-22 19:39:42 +01003 * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +01004 *
5 * Derived from linux/arch/mips/bcm63xx/usb-common.c:
6 * Copyright 2008 Maxime Bizon <mbizon@freebox.fr>
7 * Copyright 2013 Florian Fainelli <florian@openwrt.org>
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +01008 */
9
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +010010#include <clk.h>
11#include <dm.h>
12#include <generic-phy.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Simon Glass9bc15642020-02-03 07:36:16 -070014#include <malloc.h>
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +010015#include <power-domain.h>
16#include <reset.h>
17#include <asm/io.h>
18#include <dm/device.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060019#include <linux/bitops.h>
Simon Glassdbd79542020-05-10 11:40:11 -060020#include <linux/delay.h>
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +010021
22/* USBH PLL Control register */
23#define USBH_PLL_REG 0x18
24#define USBH_PLL_IDDQ_PWRDN BIT(9)
25#define USBH_PLL_PWRDN_DELAY BIT(10)
26
27/* USBH Swap Control register */
28#define USBH_SWAP_REG 0x1c
29#define USBH_SWAP_OHCI_DATA BIT(0)
30#define USBH_SWAP_OHCI_ENDIAN BIT(1)
31#define USBH_SWAP_EHCI_DATA BIT(3)
32#define USBH_SWAP_EHCI_ENDIAN BIT(4)
33
34/* USBH Setup register */
35#define USBH_SETUP_REG 0x28
36#define USBH_SETUP_IOC BIT(4)
37#define USBH_SETUP_IPP BIT(5)
38
39struct bcm6368_usbh_hw {
40 uint32_t setup_clr;
41 uint32_t pll_clr;
42};
43
44struct bcm6368_usbh_priv {
45 const struct bcm6368_usbh_hw *hw;
46 void __iomem *regs;
47};
48
49static int bcm6368_usbh_init(struct phy *phy)
50{
51 struct bcm6368_usbh_priv *priv = dev_get_priv(phy->dev);
52 const struct bcm6368_usbh_hw *hw = priv->hw;
53
54 /* configure to work in native cpu endian */
55 clrsetbits_be32(priv->regs + USBH_SWAP_REG,
56 USBH_SWAP_EHCI_ENDIAN | USBH_SWAP_OHCI_ENDIAN,
57 USBH_SWAP_EHCI_DATA | USBH_SWAP_OHCI_DATA);
58
59 /* setup config */
60 if (hw->setup_clr)
61 clrbits_be32(priv->regs + USBH_SETUP_REG, hw->setup_clr);
62
63 setbits_be32(priv->regs + USBH_SETUP_REG, USBH_SETUP_IOC);
64
65 /* enable pll control */
66 if (hw->pll_clr)
67 clrbits_be32(priv->regs + USBH_PLL_REG, hw->pll_clr);
68
69 return 0;
70}
71
72static struct phy_ops bcm6368_usbh_ops = {
73 .init = bcm6368_usbh_init,
74};
75
76static const struct bcm6368_usbh_hw bcm6328_hw = {
77 .pll_clr = USBH_PLL_IDDQ_PWRDN | USBH_PLL_PWRDN_DELAY,
78 .setup_clr = 0,
79};
80
81static const struct bcm6368_usbh_hw bcm6362_hw = {
82 .pll_clr = 0,
83 .setup_clr = 0,
84};
85
86static const struct bcm6368_usbh_hw bcm6368_hw = {
87 .pll_clr = 0,
88 .setup_clr = 0,
89};
90
91static const struct bcm6368_usbh_hw bcm63268_hw = {
92 .pll_clr = USBH_PLL_IDDQ_PWRDN | USBH_PLL_PWRDN_DELAY,
93 .setup_clr = USBH_SETUP_IPP,
94};
95
96static const struct udevice_id bcm6368_usbh_ids[] = {
97 {
98 .compatible = "brcm,bcm6328-usbh",
99 .data = (ulong)&bcm6328_hw,
100 }, {
101 .compatible = "brcm,bcm6362-usbh",
102 .data = (ulong)&bcm6362_hw,
103 }, {
104 .compatible = "brcm,bcm6368-usbh",
105 .data = (ulong)&bcm6368_hw,
106 }, {
107 .compatible = "brcm,bcm63268-usbh",
108 .data = (ulong)&bcm63268_hw,
109 }, { /* sentinel */ }
110};
111
112static int bcm6368_usbh_probe(struct udevice *dev)
113{
114 struct bcm6368_usbh_priv *priv = dev_get_priv(dev);
115 const struct bcm6368_usbh_hw *hw =
116 (const struct bcm6368_usbh_hw *)dev_get_driver_data(dev);
117#if defined(CONFIG_POWER_DOMAIN)
118 struct power_domain pwr_dom;
119#endif
120 struct reset_ctl rst_ctl;
121 struct clk clk;
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +0100122 int ret;
123
Álvaro Fernández Rojasf02a81a2018-03-22 19:39:42 +0100124 priv->regs = dev_remap_addr(dev);
125 if (!priv->regs)
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +0100126 return -EINVAL;
127
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +0100128 priv->hw = hw;
129
130 /* enable usbh clock */
131 ret = clk_get_by_name(dev, "usbh", &clk);
132 if (ret < 0)
133 return ret;
134
135 ret = clk_enable(&clk);
136 if (ret < 0)
137 return ret;
138
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +0100139#if defined(CONFIG_POWER_DOMAIN)
140 /* enable power domain */
141 ret = power_domain_get(dev, &pwr_dom);
142 if (ret < 0)
143 return ret;
144
145 ret = power_domain_on(&pwr_dom);
146 if (ret < 0)
147 return ret;
148
149 ret = power_domain_free(&pwr_dom);
150 if (ret < 0)
151 return ret;
152#endif
153
154 /* perform reset */
155 ret = reset_get_by_index(dev, 0, &rst_ctl);
156 if (ret < 0)
157 return ret;
158
159 ret = reset_deassert(&rst_ctl);
160 if (ret < 0)
161 return ret;
162
163 ret = reset_free(&rst_ctl);
164 if (ret < 0)
165 return ret;
166
167 /* enable usb_ref clock */
168 ret = clk_get_by_name(dev, "usb_ref", &clk);
169 if (!ret) {
170 ret = clk_enable(&clk);
171 if (ret < 0)
172 return ret;
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +0100173 }
174
175 mdelay(100);
176
177 return 0;
178}
179
180U_BOOT_DRIVER(bcm6368_usbh) = {
181 .name = "bcm6368-usbh",
182 .id = UCLASS_PHY,
183 .of_match = bcm6368_usbh_ids,
184 .ops = &bcm6368_usbh_ops,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700185 .priv_auto = sizeof(struct bcm6368_usbh_priv),
Álvaro Fernández Rojasdbb6baf2018-02-04 21:10:13 +0100186 .probe = bcm6368_usbh_probe,
187};