blob: 43171678ee1772ef35990f0968884b184afec6e1 [file] [log] [blame]
Vignesh Raghavendraa9ca4192019-10-01 17:26:35 +05301// SPDX-License-Identifier: GPL-2.0
2/**
3 * cdns_ti-ti.c - TI specific Glue layer for Cadence USB Controller
4 *
5 * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com
6 */
7
8#include <common.h>
9#include <asm-generic/io.h>
10#include <clk.h>
11#include <dm.h>
Simon Glass336d4612020-02-03 07:36:16 -070012#include <dm/device_compat.h>
Simon Glasscd93d622020-05-10 11:40:13 -060013#include <linux/bitops.h>
Vignesh Raghavendraa9ca4192019-10-01 17:26:35 +053014#include <linux/io.h>
15#include <linux/usb/otg.h>
16#include <malloc.h>
17
18#include "core.h"
19
20/* USB Wrapper register offsets */
21#define USBSS_PID 0x0
22#define USBSS_W1 0x4
23#define USBSS_STATIC_CONFIG 0x8
24#define USBSS_PHY_TEST 0xc
25#define USBSS_DEBUG_CTRL 0x10
26#define USBSS_DEBUG_INFO 0x14
27#define USBSS_DEBUG_LINK_STATE 0x18
28#define USBSS_DEVICE_CTRL 0x1c
29
30/* Wrapper 1 register bits */
31#define USBSS_W1_PWRUP_RST BIT(0)
32#define USBSS_W1_OVERCURRENT_SEL BIT(8)
33#define USBSS_W1_MODESTRAP_SEL BIT(9)
34#define USBSS_W1_OVERCURRENT BIT(16)
35#define USBSS_W1_MODESTRAP_MASK GENMASK(18, 17)
36#define USBSS_W1_MODESTRAP_SHIFT 17
37#define USBSS_W1_USB2_ONLY BIT(19)
38
39/* Static config register bits */
40#define USBSS1_STATIC_PLL_REF_SEL_MASK GENMASK(8, 5)
41#define USBSS1_STATIC_PLL_REF_SEL_SHIFT 5
42#define USBSS1_STATIC_LOOPBACK_MODE_MASK GENMASK(4, 3)
43#define USBSS1_STATIC_LOOPBACK_MODE_SHIFT 3
44#define USBSS1_STATIC_VBUS_SEL_MASK GENMASK(2, 1)
45#define USBSS1_STATIC_VBUS_SEL_SHIFT 1
46#define USBSS1_STATIC_LANE_REVERSE BIT(0)
47
48/* Modestrap modes */
49enum modestrap_mode { USBSS_MODESTRAP_MODE_NONE,
50 USBSS_MODESTRAP_MODE_HOST,
51 USBSS_MODESTRAP_MODE_PERIPHERAL};
52
53struct cdns_ti {
54 struct udevice *dev;
55 void __iomem *usbss;
56 int usb2_only:1;
57 int vbus_divider:1;
58 struct clk *usb2_refclk;
59 struct clk *lpm_clk;
60};
61
62static const int cdns_ti_rate_table[] = { /* in KHZ */
63 9600,
64 10000,
65 12000,
66 19200,
67 20000,
68 24000,
69 25000,
70 26000,
71 38400,
72 40000,
73 58000,
74 50000,
75 52000,
76};
77
78static inline u32 cdns_ti_readl(struct cdns_ti *data, u32 offset)
79{
80 return readl(data->usbss + offset);
81}
82
83static inline void cdns_ti_writel(struct cdns_ti *data, u32 offset, u32 value)
84{
85 writel(value, data->usbss + offset);
86}
87
88static int cdns_ti_probe(struct udevice *dev)
89{
Simon Glassc69cda22020-12-03 16:55:20 -070090 struct cdns_ti *data = dev_get_plat(dev);
Vignesh Raghavendraa9ca4192019-10-01 17:26:35 +053091 struct clk usb2_refclk;
92 int modestrap_mode;
93 unsigned long rate;
94 int rate_code, i;
95 u32 reg;
96 int ret;
97
98 data->dev = dev;
99
100 data->usbss = dev_remap_addr_index(dev, 0);
101 if (!data->usbss)
102 return -EINVAL;
103
Kishon Vijay Abraham Ic2d21e02021-06-01 16:26:16 +0530104 ret = clk_get_by_name(dev, "ref", &usb2_refclk);
Vignesh Raghavendraa9ca4192019-10-01 17:26:35 +0530105 if (ret) {
106 dev_err(dev, "Failed to get usb2_refclk\n");
107 return ret;
108 }
109
110 rate = clk_get_rate(&usb2_refclk);
111 rate /= 1000; /* To KHz */
112 for (i = 0; i < ARRAY_SIZE(cdns_ti_rate_table); i++) {
113 if (cdns_ti_rate_table[i] == rate)
114 break;
115 }
116
117 if (i == ARRAY_SIZE(cdns_ti_rate_table)) {
118 dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate);
119 return -EINVAL;
120 }
121
122 rate_code = i;
123
124 /* assert RESET */
125 reg = cdns_ti_readl(data, USBSS_W1);
126 reg &= ~USBSS_W1_PWRUP_RST;
127 cdns_ti_writel(data, USBSS_W1, reg);
128
129 /* set static config */
130 reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
131 reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
132 reg |= rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
133
134 reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
135 data->vbus_divider = dev_read_bool(dev, "ti,vbus-divider");
136 if (data->vbus_divider)
137 reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
138
139 cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
140 reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
141
142 /* set USB2_ONLY mode if requested */
143 reg = cdns_ti_readl(data, USBSS_W1);
144 data->usb2_only = dev_read_bool(dev, "ti,usb2-only");
145 if (data->usb2_only)
146 reg |= USBSS_W1_USB2_ONLY;
147
148 /* set modestrap */
149 if (dev_read_bool(dev, "ti,modestrap-host"))
150 modestrap_mode = USBSS_MODESTRAP_MODE_HOST;
151 else if (dev_read_bool(dev, "ti,modestrap-peripheral"))
152 modestrap_mode = USBSS_MODESTRAP_MODE_PERIPHERAL;
153 else
154 modestrap_mode = USBSS_MODESTRAP_MODE_NONE;
155
156 reg |= USBSS_W1_MODESTRAP_SEL;
157 reg &= ~USBSS_W1_MODESTRAP_MASK;
158 reg |= modestrap_mode << USBSS_W1_MODESTRAP_SHIFT;
159 cdns_ti_writel(data, USBSS_W1, reg);
160
161 /* de-assert RESET */
162 reg |= USBSS_W1_PWRUP_RST;
163 cdns_ti_writel(data, USBSS_W1, reg);
164
165 return 0;
166}
167
168static int cdns_ti_remove(struct udevice *dev)
169{
Simon Glassc69cda22020-12-03 16:55:20 -0700170 struct cdns_ti *data = dev_get_plat(dev);
Vignesh Raghavendraa9ca4192019-10-01 17:26:35 +0530171 u32 reg;
172
173 /* put device back to RESET*/
174 reg = cdns_ti_readl(data, USBSS_W1);
175 reg &= ~USBSS_W1_PWRUP_RST;
176 cdns_ti_writel(data, USBSS_W1, reg);
177
178 return 0;
179}
180
181static const struct udevice_id cdns_ti_of_match[] = {
182 { .compatible = "ti,j721e-usb", },
183 {},
184};
185
186U_BOOT_DRIVER(cdns_ti) = {
187 .name = "cdns-ti",
188 .id = UCLASS_NOP,
189 .of_match = cdns_ti_of_match,
190 .bind = cdns3_bind,
191 .probe = cdns_ti_probe,
192 .remove = cdns_ti_remove,
Simon Glasscaa4daa2020-12-03 16:55:18 -0700193 .plat_auto = sizeof(struct cdns_ti),
Vignesh Raghavendraa9ca4192019-10-01 17:26:35 +0530194 .flags = DM_FLAG_OS_PREPARE,
195};