blob: 1a594b356ad828ef7ec89bc133a6ce2c156f4167 [file] [log] [blame]
Greg Kroah-Hartman5fd54ac2017-11-03 11:28:30 +01001// SPDX-License-Identifier: GPL-2.0+
Daniel Mack2d57a952009-10-15 17:09:35 +03002/*
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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090028#include <linux/slab.h>
Paul Gortmakerf940fcd2011-05-27 09:56:31 -040029#include <linux/export.h>
Daniel Mack2d57a952009-10-15 17:09:35 +030030#include <linux/usb.h>
31#include <linux/usb/otg.h>
32#include <linux/usb/ulpi.h>
33
Igor Grinberg96b9e832010-10-10 17:59:19 +020034
35struct ulpi_info {
36 unsigned int id;
37 char *name;
38};
39
Daniel Mack2d57a952009-10-15 17:09:35 +030040#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
Igor Grinberg96b9e832010-10-10 17:59:19 +020041#define ULPI_INFO(_id, _name) \
42 { \
43 .id = (_id), \
44 .name = (_name), \
45 }
Daniel Mack2d57a952009-10-15 17:09:35 +030046
Daniel Mack2d57a952009-10-15 17:09:35 +030047/* ULPI hardcoded IDs, used for probing */
Igor Grinberg96b9e832010-10-10 17:59:19 +020048static struct ulpi_info ulpi_ids[] = {
49 ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
Fabio Estevam1e4cba82010-12-20 21:54:30 -020050 ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
Michal Simekead51782014-03-11 13:23:14 +010051 ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"),
Liviu Dudau79cb5b52014-05-14 21:32:39 +010052 ULPI_INFO(ULPI_ID(0x0424, 0x0009), "SMSC USB334x"),
Michal Simekead51782014-03-11 13:23:14 +010053 ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"),
Daniel Mack2d57a952009-10-15 17:09:35 +030054};
55
Heikki Krogerus298b0832012-02-13 13:24:13 +020056static int ulpi_set_otg_flags(struct usb_phy *phy)
Daniel Mack2d57a952009-10-15 17:09:35 +030057{
Igor Grinberg13dd0c92010-07-15 16:00:16 +030058 unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
59 ULPI_OTG_CTRL_DM_PULLDOWN;
Daniel Mack2d57a952009-10-15 17:09:35 +030060
Heikki Krogerus298b0832012-02-13 13:24:13 +020061 if (phy->flags & ULPI_OTG_ID_PULLUP)
Heikki Krogerusfc567f02010-05-03 09:13:02 +030062 flags |= ULPI_OTG_CTRL_ID_PULLUP;
Daniel Mack2d57a952009-10-15 17:09:35 +030063
Igor Grinberg13dd0c92010-07-15 16:00:16 +030064 /*
65 * ULPI Specification rev.1.1 default
66 * for Dp/DmPulldown is enabled.
67 */
Heikki Krogerus298b0832012-02-13 13:24:13 +020068 if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030069 flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
Daniel Mack2d57a952009-10-15 17:09:35 +030070
Heikki Krogerus298b0832012-02-13 13:24:13 +020071 if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030072 flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
Daniel Mack2d57a952009-10-15 17:09:35 +030073
Heikki Krogerus298b0832012-02-13 13:24:13 +020074 if (phy->flags & ULPI_OTG_EXTVBUSIND)
Heikki Krogerusfc567f02010-05-03 09:13:02 +030075 flags |= ULPI_OTG_CTRL_EXTVBUSIND;
Daniel Mack2d57a952009-10-15 17:09:35 +030076
Heikki Krogerus298b0832012-02-13 13:24:13 +020077 return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
Daniel Mack2d57a952009-10-15 17:09:35 +030078}
79
Heikki Krogerus298b0832012-02-13 13:24:13 +020080static int ulpi_set_fc_flags(struct usb_phy *phy)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030081{
82 unsigned int flags = 0;
83
84 /*
85 * ULPI Specification rev.1.1 default
86 * for XcvrSelect is Full Speed.
87 */
Heikki Krogerus298b0832012-02-13 13:24:13 +020088 if (phy->flags & ULPI_FC_HS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030089 flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
Heikki Krogerus298b0832012-02-13 13:24:13 +020090 else if (phy->flags & ULPI_FC_LS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030091 flags |= ULPI_FUNC_CTRL_LOW_SPEED;
Heikki Krogerus298b0832012-02-13 13:24:13 +020092 else if (phy->flags & ULPI_FC_FS4LS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030093 flags |= ULPI_FUNC_CTRL_FS4LS;
94 else
95 flags |= ULPI_FUNC_CTRL_FULL_SPEED;
96
Heikki Krogerus298b0832012-02-13 13:24:13 +020097 if (phy->flags & ULPI_FC_TERMSEL)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030098 flags |= ULPI_FUNC_CTRL_TERMSELECT;
99
100 /*
101 * ULPI Specification rev.1.1 default
102 * for OpMode is Normal Operation.
103 */
Heikki Krogerus298b0832012-02-13 13:24:13 +0200104 if (phy->flags & ULPI_FC_OP_NODRV)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300105 flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200106 else if (phy->flags & ULPI_FC_OP_DIS_NRZI)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300107 flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200108 else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300109 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
Heikki Krogerus298b0832012-02-13 13:24:13 +0200119 return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300120}
121
Heikki Krogerus298b0832012-02-13 13:24:13 +0200122static int ulpi_set_ic_flags(struct usb_phy *phy)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300123{
124 unsigned int flags = 0;
125
Heikki Krogerus298b0832012-02-13 13:24:13 +0200126 if (phy->flags & ULPI_IC_AUTORESUME)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300127 flags |= ULPI_IFC_CTRL_AUTORESUME;
128
Heikki Krogerus298b0832012-02-13 13:24:13 +0200129 if (phy->flags & ULPI_IC_EXTVBUS_INDINV)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300130 flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
131
Heikki Krogerus298b0832012-02-13 13:24:13 +0200132 if (phy->flags & ULPI_IC_IND_PASSTHRU)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300133 flags |= ULPI_IFC_CTRL_PASSTHRU;
134
Heikki Krogerus298b0832012-02-13 13:24:13 +0200135 if (phy->flags & ULPI_IC_PROTECT_DIS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300136 flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
137
Heikki Krogerus298b0832012-02-13 13:24:13 +0200138 return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300139}
140
Heikki Krogerus298b0832012-02-13 13:24:13 +0200141static int ulpi_set_flags(struct usb_phy *phy)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300142{
143 int ret;
144
Heikki Krogerus298b0832012-02-13 13:24:13 +0200145 ret = ulpi_set_otg_flags(phy);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300146 if (ret)
147 return ret;
148
Heikki Krogerus298b0832012-02-13 13:24:13 +0200149 ret = ulpi_set_ic_flags(phy);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300150 if (ret)
151 return ret;
152
Heikki Krogerus298b0832012-02-13 13:24:13 +0200153 return ulpi_set_fc_flags(phy);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300154}
155
Heikki Krogerus298b0832012-02-13 13:24:13 +0200156static int ulpi_check_integrity(struct usb_phy *phy)
Igor Grinberga9138192010-10-10 17:59:18 +0200157{
158 int ret, i;
159 unsigned int val = 0x55;
160
161 for (i = 0; i < 2; i++) {
Heikki Krogerus298b0832012-02-13 13:24:13 +0200162 ret = usb_phy_io_write(phy, val, ULPI_SCRATCH);
Igor Grinberga9138192010-10-10 17:59:18 +0200163 if (ret < 0)
164 return ret;
165
Heikki Krogerus298b0832012-02-13 13:24:13 +0200166 ret = usb_phy_io_read(phy, ULPI_SCRATCH);
Igor Grinberga9138192010-10-10 17:59:18 +0200167 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
Heikki Krogerus298b0832012-02-13 13:24:13 +0200182static int ulpi_init(struct usb_phy *phy)
Daniel Mack2d57a952009-10-15 17:09:35 +0300183{
Wolfram Sang7b4a0362010-06-15 12:34:22 +0200184 int i, vid, pid, ret;
185 u32 ulpi_id = 0;
Daniel Mack2d57a952009-10-15 17:09:35 +0300186
Wolfram Sang7b4a0362010-06-15 12:34:22 +0200187 for (i = 0; i < 4; i++) {
Heikki Krogerus298b0832012-02-13 13:24:13 +0200188 ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i);
Wolfram Sang7b4a0362010-06-15 12:34:22 +0200189 if (ret < 0)
190 return ret;
191 ulpi_id = (ulpi_id << 8) | ret;
192 }
193 vid = ulpi_id & 0xffff;
194 pid = ulpi_id >> 16;
Daniel Mack2d57a952009-10-15 17:09:35 +0300195
196 pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid);
197
Igor Grinberg96b9e832010-10-10 17:59:19 +0200198 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);
Igor Grinberga9138192010-10-10 17:59:18 +0200202 break;
Igor Grinberg96b9e832010-10-10 17:59:19 +0200203 }
204 }
Daniel Mack2d57a952009-10-15 17:09:35 +0300205
Heikki Krogerus298b0832012-02-13 13:24:13 +0200206 ret = ulpi_check_integrity(phy);
Igor Grinberga9138192010-10-10 17:59:18 +0200207 if (ret)
208 return ret;
209
Heikki Krogerus298b0832012-02-13 13:24:13 +0200210 return ulpi_set_flags(phy);
Daniel Mack2d57a952009-10-15 17:09:35 +0300211}
212
Heikki Krogerus298b0832012-02-13 13:24:13 +0200213static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300214{
Antoine Tenart19c1eac2014-10-30 18:41:14 +0100215 struct usb_phy *phy = otg->usb_phy;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200216 unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300217
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
Heikki Krogerus298b0832012-02-13 13:24:13 +0200229 if (phy->flags & ULPI_IC_6PIN_SERIAL)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300230 flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200231 else if (phy->flags & ULPI_IC_3PIN_SERIAL)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300232 flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200233 else if (phy->flags & ULPI_IC_CARKIT)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300234 flags |= ULPI_IFC_CTRL_CARKITMODE;
235
Heikki Krogerus298b0832012-02-13 13:24:13 +0200236 return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300237}
238
Heikki Krogerus298b0832012-02-13 13:24:13 +0200239static int ulpi_set_vbus(struct usb_otg *otg, bool on)
Daniel Mack2d57a952009-10-15 17:09:35 +0300240{
Antoine Tenart19c1eac2014-10-30 18:41:14 +0100241 struct usb_phy *phy = otg->usb_phy;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200242 unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
Daniel Mack2d57a952009-10-15 17:09:35 +0300243
Heikki Krogerusfc567f02010-05-03 09:13:02 +0300244 flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
Daniel Mack2d57a952009-10-15 17:09:35 +0300245
246 if (on) {
Heikki Krogerus298b0832012-02-13 13:24:13 +0200247 if (phy->flags & ULPI_OTG_DRVVBUS)
Heikki Krogerusfc567f02010-05-03 09:13:02 +0300248 flags |= ULPI_OTG_CTRL_DRVVBUS;
Daniel Mack2d57a952009-10-15 17:09:35 +0300249
Heikki Krogerus298b0832012-02-13 13:24:13 +0200250 if (phy->flags & ULPI_OTG_DRVVBUS_EXT)
Heikki Krogerusfc567f02010-05-03 09:13:02 +0300251 flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
Daniel Mack2d57a952009-10-15 17:09:35 +0300252 }
253
Heikki Krogerus298b0832012-02-13 13:24:13 +0200254 return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
Daniel Mack2d57a952009-10-15 17:09:35 +0300255}
256
Heikki Krogerus86753812012-02-13 13:24:02 +0200257struct usb_phy *
Heikki Krogerus298b0832012-02-13 13:24:13 +0200258otg_ulpi_create(struct usb_phy_io_ops *ops,
Daniel Mack2d57a952009-10-15 17:09:35 +0300259 unsigned int flags)
260{
Heikki Krogerus298b0832012-02-13 13:24:13 +0200261 struct usb_phy *phy;
262 struct usb_otg *otg;
Daniel Mack2d57a952009-10-15 17:09:35 +0300263
Heikki Krogerus298b0832012-02-13 13:24:13 +0200264 phy = kzalloc(sizeof(*phy), GFP_KERNEL);
265 if (!phy)
Daniel Mack2d57a952009-10-15 17:09:35 +0300266 return NULL;
267
Heikki Krogerus298b0832012-02-13 13:24:13 +0200268 otg = kzalloc(sizeof(*otg), GFP_KERNEL);
269 if (!otg) {
270 kfree(phy);
271 return NULL;
272 }
273
274 phy->label = "ULPI";
275 phy->flags = flags;
276 phy->io_ops = ops;
277 phy->otg = otg;
278 phy->init = ulpi_init;
279
Antoine Tenart19c1eac2014-10-30 18:41:14 +0100280 otg->usb_phy = phy;
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300281 otg->set_host = ulpi_set_host;
Daniel Mack2d57a952009-10-15 17:09:35 +0300282 otg->set_vbus = ulpi_set_vbus;
283
Heikki Krogerus298b0832012-02-13 13:24:13 +0200284 return phy;
Daniel Mack2d57a952009-10-15 17:09:35 +0300285}
286EXPORT_SYMBOL_GPL(otg_ulpi_create);
287