]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/usb/phy/phy-ulpi.c
USB: add SPDX identifiers to all remaining files in drivers/usb/
[mirror_ubuntu-jammy-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
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27#include <linux/kernel.h>
5a0e3ad6 28#include <linux/slab.h>
f940fcd8 29#include <linux/export.h>
2d57a95f
DM
30#include <linux/usb.h>
31#include <linux/usb/otg.h>
32#include <linux/usb/ulpi.h>
33
96b9e832
IG
34
35struct ulpi_info {
36 unsigned int id;
37 char *name;
38};
39
2d57a95f 40#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
96b9e832
IG
41#define ULPI_INFO(_id, _name) \
42 { \
43 .id = (_id), \
44 .name = (_name), \
45 }
2d57a95f 46
2d57a95f 47/* ULPI hardcoded IDs, used for probing */
96b9e832
IG
48static struct ulpi_info ulpi_ids[] = {
49 ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
1e4cba8b 50 ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
ead5178b 51 ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"),
79cb5b53 52 ULPI_INFO(ULPI_ID(0x0424, 0x0009), "SMSC USB334x"),
ead5178b 53 ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"),
2d57a95f
DM
54};
55
298b083c 56static int ulpi_set_otg_flags(struct usb_phy *phy)
2d57a95f 57{
13dd0c97
IG
58 unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
59 ULPI_OTG_CTRL_DM_PULLDOWN;
2d57a95f 60
298b083c 61 if (phy->flags & ULPI_OTG_ID_PULLUP)
fc567f06 62 flags |= ULPI_OTG_CTRL_ID_PULLUP;
2d57a95f 63
13dd0c97
IG
64 /*
65 * ULPI Specification rev.1.1 default
66 * for Dp/DmPulldown is enabled.
67 */
298b083c 68 if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS)
13dd0c97 69 flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
2d57a95f 70
298b083c 71 if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS)
13dd0c97 72 flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
2d57a95f 73
298b083c 74 if (phy->flags & ULPI_OTG_EXTVBUSIND)
fc567f06 75 flags |= ULPI_OTG_CTRL_EXTVBUSIND;
2d57a95f 76
298b083c 77 return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
2d57a95f
DM
78}
79
298b083c 80static int ulpi_set_fc_flags(struct usb_phy *phy)
13dd0c97
IG
81{
82 unsigned int flags = 0;
83
84 /*
85 * ULPI Specification rev.1.1 default
86 * for XcvrSelect is Full Speed.
87 */
298b083c 88 if (phy->flags & ULPI_FC_HS)
13dd0c97 89 flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
298b083c 90 else if (phy->flags & ULPI_FC_LS)
13dd0c97 91 flags |= ULPI_FUNC_CTRL_LOW_SPEED;
298b083c 92 else if (phy->flags & ULPI_FC_FS4LS)
13dd0c97
IG
93 flags |= ULPI_FUNC_CTRL_FS4LS;
94 else
95 flags |= ULPI_FUNC_CTRL_FULL_SPEED;
96
298b083c 97 if (phy->flags & ULPI_FC_TERMSEL)
13dd0c97
IG
98 flags |= ULPI_FUNC_CTRL_TERMSELECT;
99
100 /*
101 * ULPI Specification rev.1.1 default
102 * for OpMode is Normal Operation.
103 */
298b083c 104 if (phy->flags & ULPI_FC_OP_NODRV)
13dd0c97 105 flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
298b083c 106 else if (phy->flags & ULPI_FC_OP_DIS_NRZI)
13dd0c97 107 flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
298b083c 108 else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP)
13dd0c97
IG
109 flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP;
110 else
111 flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
112
113 /*
114 * ULPI Specification rev.1.1 default
115 * for SuspendM is Powered.
116 */
117 flags |= ULPI_FUNC_CTRL_SUSPENDM;
118
298b083c 119 return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL);
13dd0c97
IG
120}
121
298b083c 122static int ulpi_set_ic_flags(struct usb_phy *phy)
13dd0c97
IG
123{
124 unsigned int flags = 0;
125
298b083c 126 if (phy->flags & ULPI_IC_AUTORESUME)
13dd0c97
IG
127 flags |= ULPI_IFC_CTRL_AUTORESUME;
128
298b083c 129 if (phy->flags & ULPI_IC_EXTVBUS_INDINV)
13dd0c97
IG
130 flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
131
298b083c 132 if (phy->flags & ULPI_IC_IND_PASSTHRU)
13dd0c97
IG
133 flags |= ULPI_IFC_CTRL_PASSTHRU;
134
298b083c 135 if (phy->flags & ULPI_IC_PROTECT_DIS)
13dd0c97
IG
136 flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
137
298b083c 138 return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
13dd0c97
IG
139}
140
298b083c 141static int ulpi_set_flags(struct usb_phy *phy)
13dd0c97
IG
142{
143 int ret;
144
298b083c 145 ret = ulpi_set_otg_flags(phy);
13dd0c97
IG
146 if (ret)
147 return ret;
148
298b083c 149 ret = ulpi_set_ic_flags(phy);
13dd0c97
IG
150 if (ret)
151 return ret;
152
298b083c 153 return ulpi_set_fc_flags(phy);
13dd0c97
IG
154}
155
298b083c 156static int ulpi_check_integrity(struct usb_phy *phy)
a9138192
IG
157{
158 int ret, i;
159 unsigned int val = 0x55;
160
161 for (i = 0; i < 2; i++) {
298b083c 162 ret = usb_phy_io_write(phy, val, ULPI_SCRATCH);
a9138192
IG
163 if (ret < 0)
164 return ret;
165
298b083c 166 ret = usb_phy_io_read(phy, ULPI_SCRATCH);
a9138192
IG
167 if (ret < 0)
168 return ret;
169
170 if (ret != val) {
171 pr_err("ULPI integrity check: failed!");
172 return -ENODEV;
173 }
174 val = val << 1;
175 }
176
177 pr_info("ULPI integrity check: passed.\n");
178
179 return 0;
180}
181
298b083c 182static int ulpi_init(struct usb_phy *phy)
2d57a95f 183{
7b4a0367
WS
184 int i, vid, pid, ret;
185 u32 ulpi_id = 0;
2d57a95f 186
7b4a0367 187 for (i = 0; i < 4; i++) {
298b083c 188 ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i);
7b4a0367
WS
189 if (ret < 0)
190 return ret;
191 ulpi_id = (ulpi_id << 8) | ret;
192 }
193 vid = ulpi_id & 0xffff;
194 pid = ulpi_id >> 16;
2d57a95f
DM
195
196 pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid);
197
96b9e832
IG
198 for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) {
199 if (ulpi_ids[i].id == ULPI_ID(vid, pid)) {
200 pr_info("Found %s ULPI transceiver.\n",
201 ulpi_ids[i].name);
a9138192 202 break;
96b9e832
IG
203 }
204 }
a9138192 205
298b083c 206 ret = ulpi_check_integrity(phy);
a9138192
IG
207 if (ret)
208 return ret;
2d57a95f 209
298b083c 210 return ulpi_set_flags(phy);
2d57a95f
DM
211}
212
298b083c 213static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
13dd0c97 214{
19c1eac2 215 struct usb_phy *phy = otg->usb_phy;
298b083c 216 unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL);
13dd0c97
IG
217
218 if (!host) {
219 otg->host = NULL;
220 return 0;
221 }
222
223 otg->host = host;
224
225 flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE |
226 ULPI_IFC_CTRL_3_PIN_SERIAL_MODE |
227 ULPI_IFC_CTRL_CARKITMODE);
228
298b083c 229 if (phy->flags & ULPI_IC_6PIN_SERIAL)
13dd0c97 230 flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
298b083c 231 else if (phy->flags & ULPI_IC_3PIN_SERIAL)
13dd0c97 232 flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
298b083c 233 else if (phy->flags & ULPI_IC_CARKIT)
13dd0c97
IG
234 flags |= ULPI_IFC_CTRL_CARKITMODE;
235
298b083c 236 return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
13dd0c97
IG
237}
238
298b083c 239static int ulpi_set_vbus(struct usb_otg *otg, bool on)
2d57a95f 240{
19c1eac2 241 struct usb_phy *phy = otg->usb_phy;
298b083c 242 unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
2d57a95f 243
fc567f06 244 flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
2d57a95f
DM
245
246 if (on) {
298b083c 247 if (phy->flags & ULPI_OTG_DRVVBUS)
fc567f06 248 flags |= ULPI_OTG_CTRL_DRVVBUS;
2d57a95f 249
298b083c 250 if (phy->flags & ULPI_OTG_DRVVBUS_EXT)
fc567f06 251 flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
2d57a95f
DM
252 }
253
298b083c 254 return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
2d57a95f
DM
255}
256
86753811 257struct usb_phy *
298b083c 258otg_ulpi_create(struct usb_phy_io_ops *ops,
2d57a95f
DM
259 unsigned int flags)
260{
298b083c
HK
261 struct usb_phy *phy;
262 struct usb_otg *otg;
263
264 phy = kzalloc(sizeof(*phy), GFP_KERNEL);
265 if (!phy)
266 return NULL;
2d57a95f
DM
267
268 otg = kzalloc(sizeof(*otg), GFP_KERNEL);
298b083c
HK
269 if (!otg) {
270 kfree(phy);
2d57a95f 271 return NULL;
298b083c
HK
272 }
273
274 phy->label = "ULPI";
275 phy->flags = flags;
276 phy->io_ops = ops;
277 phy->otg = otg;
278 phy->init = ulpi_init;
2d57a95f 279
19c1eac2 280 otg->usb_phy = phy;
13dd0c97 281 otg->set_host = ulpi_set_host;
2d57a95f
DM
282 otg->set_vbus = ulpi_set_vbus;
283
298b083c 284 return phy;
2d57a95f
DM
285}
286EXPORT_SYMBOL_GPL(otg_ulpi_create);
287