]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/usb/phy/phy-ulpi.c
usb: gadget: udc: renesas_usb3: should call pm_runtime_enable() before add udc
[mirror_ubuntu-bionic-kernel.git] / drivers / usb / phy / phy-ulpi.c
CommitLineData
5fd54ace 1// SPDX-License-Identifier: GPL-2.0+
2d57a95f
DM
2/*
3 * Generic ULPI USB transceiver support
4 *
5 * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de>
6 *
7 * Based on sources from
8 *
9 * Sascha Hauer <s.hauer@pengutronix.de>
10 * Freescale Semiconductors
2d57a95f
DM
11 */
12
13#include <linux/kernel.h>
5a0e3ad6 14#include <linux/slab.h>
f940fcd8 15#include <linux/export.h>
2d57a95f
DM
16#include <linux/usb.h>
17#include <linux/usb/otg.h>
18#include <linux/usb/ulpi.h>
19
96b9e832
IG
20
21struct ulpi_info {
22 unsigned int id;
23 char *name;
24};
25
2d57a95f 26#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
96b9e832
IG
27#define ULPI_INFO(_id, _name) \
28 { \
29 .id = (_id), \
30 .name = (_name), \
31 }
2d57a95f 32
2d57a95f 33/* ULPI hardcoded IDs, used for probing */
96b9e832
IG
34static struct ulpi_info ulpi_ids[] = {
35 ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
1e4cba8b 36 ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
ead5178b 37 ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"),
79cb5b53 38 ULPI_INFO(ULPI_ID(0x0424, 0x0009), "SMSC USB334x"),
ead5178b 39 ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"),
2d57a95f
DM
40};
41
298b083c 42static int ulpi_set_otg_flags(struct usb_phy *phy)
2d57a95f 43{
13dd0c97
IG
44 unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
45 ULPI_OTG_CTRL_DM_PULLDOWN;
2d57a95f 46
298b083c 47 if (phy->flags & ULPI_OTG_ID_PULLUP)
fc567f06 48 flags |= ULPI_OTG_CTRL_ID_PULLUP;
2d57a95f 49
13dd0c97
IG
50 /*
51 * ULPI Specification rev.1.1 default
52 * for Dp/DmPulldown is enabled.
53 */
298b083c 54 if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS)
13dd0c97 55 flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
2d57a95f 56
298b083c 57 if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS)
13dd0c97 58 flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
2d57a95f 59
298b083c 60 if (phy->flags & ULPI_OTG_EXTVBUSIND)
fc567f06 61 flags |= ULPI_OTG_CTRL_EXTVBUSIND;
2d57a95f 62
298b083c 63 return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
2d57a95f
DM
64}
65
298b083c 66static int ulpi_set_fc_flags(struct usb_phy *phy)
13dd0c97
IG
67{
68 unsigned int flags = 0;
69
70 /*
71 * ULPI Specification rev.1.1 default
72 * for XcvrSelect is Full Speed.
73 */
298b083c 74 if (phy->flags & ULPI_FC_HS)
13dd0c97 75 flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
298b083c 76 else if (phy->flags & ULPI_FC_LS)
13dd0c97 77 flags |= ULPI_FUNC_CTRL_LOW_SPEED;
298b083c 78 else if (phy->flags & ULPI_FC_FS4LS)
13dd0c97
IG
79 flags |= ULPI_FUNC_CTRL_FS4LS;
80 else
81 flags |= ULPI_FUNC_CTRL_FULL_SPEED;
82
298b083c 83 if (phy->flags & ULPI_FC_TERMSEL)
13dd0c97
IG
84 flags |= ULPI_FUNC_CTRL_TERMSELECT;
85
86 /*
87 * ULPI Specification rev.1.1 default
88 * for OpMode is Normal Operation.
89 */
298b083c 90 if (phy->flags & ULPI_FC_OP_NODRV)
13dd0c97 91 flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
298b083c 92 else if (phy->flags & ULPI_FC_OP_DIS_NRZI)
13dd0c97 93 flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
298b083c 94 else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP)
13dd0c97
IG
95 flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP;
96 else
97 flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
98
99 /*
100 * ULPI Specification rev.1.1 default
101 * for SuspendM is Powered.
102 */
103 flags |= ULPI_FUNC_CTRL_SUSPENDM;
104
298b083c 105 return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL);
13dd0c97
IG
106}
107
298b083c 108static int ulpi_set_ic_flags(struct usb_phy *phy)
13dd0c97
IG
109{
110 unsigned int flags = 0;
111
298b083c 112 if (phy->flags & ULPI_IC_AUTORESUME)
13dd0c97
IG
113 flags |= ULPI_IFC_CTRL_AUTORESUME;
114
298b083c 115 if (phy->flags & ULPI_IC_EXTVBUS_INDINV)
13dd0c97
IG
116 flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
117
298b083c 118 if (phy->flags & ULPI_IC_IND_PASSTHRU)
13dd0c97
IG
119 flags |= ULPI_IFC_CTRL_PASSTHRU;
120
298b083c 121 if (phy->flags & ULPI_IC_PROTECT_DIS)
13dd0c97
IG
122 flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
123
298b083c 124 return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
13dd0c97
IG
125}
126
298b083c 127static int ulpi_set_flags(struct usb_phy *phy)
13dd0c97
IG
128{
129 int ret;
130
298b083c 131 ret = ulpi_set_otg_flags(phy);
13dd0c97
IG
132 if (ret)
133 return ret;
134
298b083c 135 ret = ulpi_set_ic_flags(phy);
13dd0c97
IG
136 if (ret)
137 return ret;
138
298b083c 139 return ulpi_set_fc_flags(phy);
13dd0c97
IG
140}
141
298b083c 142static int ulpi_check_integrity(struct usb_phy *phy)
a9138192
IG
143{
144 int ret, i;
145 unsigned int val = 0x55;
146
147 for (i = 0; i < 2; i++) {
298b083c 148 ret = usb_phy_io_write(phy, val, ULPI_SCRATCH);
a9138192
IG
149 if (ret < 0)
150 return ret;
151
298b083c 152 ret = usb_phy_io_read(phy, ULPI_SCRATCH);
a9138192
IG
153 if (ret < 0)
154 return ret;
155
156 if (ret != val) {
157 pr_err("ULPI integrity check: failed!");
158 return -ENODEV;
159 }
160 val = val << 1;
161 }
162
163 pr_info("ULPI integrity check: passed.\n");
164
165 return 0;
166}
167
298b083c 168static int ulpi_init(struct usb_phy *phy)
2d57a95f 169{
7b4a0367
WS
170 int i, vid, pid, ret;
171 u32 ulpi_id = 0;
2d57a95f 172
7b4a0367 173 for (i = 0; i < 4; i++) {
298b083c 174 ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i);
7b4a0367
WS
175 if (ret < 0)
176 return ret;
177 ulpi_id = (ulpi_id << 8) | ret;
178 }
179 vid = ulpi_id & 0xffff;
180 pid = ulpi_id >> 16;
2d57a95f
DM
181
182 pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid);
183
96b9e832
IG
184 for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) {
185 if (ulpi_ids[i].id == ULPI_ID(vid, pid)) {
186 pr_info("Found %s ULPI transceiver.\n",
187 ulpi_ids[i].name);
a9138192 188 break;
96b9e832
IG
189 }
190 }
a9138192 191
298b083c 192 ret = ulpi_check_integrity(phy);
a9138192
IG
193 if (ret)
194 return ret;
2d57a95f 195
298b083c 196 return ulpi_set_flags(phy);
2d57a95f
DM
197}
198
298b083c 199static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
13dd0c97 200{
19c1eac2 201 struct usb_phy *phy = otg->usb_phy;
298b083c 202 unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL);
13dd0c97
IG
203
204 if (!host) {
205 otg->host = NULL;
206 return 0;
207 }
208
209 otg->host = host;
210
211 flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE |
212 ULPI_IFC_CTRL_3_PIN_SERIAL_MODE |
213 ULPI_IFC_CTRL_CARKITMODE);
214
298b083c 215 if (phy->flags & ULPI_IC_6PIN_SERIAL)
13dd0c97 216 flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
298b083c 217 else if (phy->flags & ULPI_IC_3PIN_SERIAL)
13dd0c97 218 flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
298b083c 219 else if (phy->flags & ULPI_IC_CARKIT)
13dd0c97
IG
220 flags |= ULPI_IFC_CTRL_CARKITMODE;
221
298b083c 222 return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
13dd0c97
IG
223}
224
298b083c 225static int ulpi_set_vbus(struct usb_otg *otg, bool on)
2d57a95f 226{
19c1eac2 227 struct usb_phy *phy = otg->usb_phy;
298b083c 228 unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
2d57a95f 229
fc567f06 230 flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
2d57a95f
DM
231
232 if (on) {
298b083c 233 if (phy->flags & ULPI_OTG_DRVVBUS)
fc567f06 234 flags |= ULPI_OTG_CTRL_DRVVBUS;
2d57a95f 235
298b083c 236 if (phy->flags & ULPI_OTG_DRVVBUS_EXT)
fc567f06 237 flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
2d57a95f
DM
238 }
239
298b083c 240 return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
2d57a95f
DM
241}
242
86753811 243struct usb_phy *
298b083c 244otg_ulpi_create(struct usb_phy_io_ops *ops,
2d57a95f
DM
245 unsigned int flags)
246{
298b083c
HK
247 struct usb_phy *phy;
248 struct usb_otg *otg;
249
250 phy = kzalloc(sizeof(*phy), GFP_KERNEL);
251 if (!phy)
252 return NULL;
2d57a95f
DM
253
254 otg = kzalloc(sizeof(*otg), GFP_KERNEL);
298b083c
HK
255 if (!otg) {
256 kfree(phy);
2d57a95f 257 return NULL;
298b083c
HK
258 }
259
260 phy->label = "ULPI";
261 phy->flags = flags;
262 phy->io_ops = ops;
263 phy->otg = otg;
264 phy->init = ulpi_init;
2d57a95f 265
19c1eac2 266 otg->usb_phy = phy;
13dd0c97 267 otg->set_host = ulpi_set_host;
2d57a95f
DM
268 otg->set_vbus = ulpi_set_vbus;
269
298b083c 270 return phy;
2d57a95f
DM
271}
272EXPORT_SYMBOL_GPL(otg_ulpi_create);
273