blob: f48a7a21e3c2f6ffa5c7ee17c05ef47619ae89d6 [file] [log] [blame]
Daniel Mack2d57a952009-10-15 17:09:35 +03001/*
2 * Generic ULPI USB transceiver support
3 *
4 * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de>
5 *
6 * Based on sources from
7 *
8 * Sascha Hauer <s.hauer@pengutronix.de>
9 * Freescale Semiconductors
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26#include <linux/kernel.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090027#include <linux/slab.h>
Paul Gortmakerf940fcd2011-05-27 09:56:31 -040028#include <linux/export.h>
Daniel Mack2d57a952009-10-15 17:09:35 +030029#include <linux/usb.h>
30#include <linux/usb/otg.h>
31#include <linux/usb/ulpi.h>
32
Igor Grinberg96b9e832010-10-10 17:59:19 +020033
34struct ulpi_info {
35 unsigned int id;
36 char *name;
37};
38
Daniel Mack2d57a952009-10-15 17:09:35 +030039#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
Igor Grinberg96b9e832010-10-10 17:59:19 +020040#define ULPI_INFO(_id, _name) \
41 { \
42 .id = (_id), \
43 .name = (_name), \
44 }
Daniel Mack2d57a952009-10-15 17:09:35 +030045
Daniel Mack2d57a952009-10-15 17:09:35 +030046/* ULPI hardcoded IDs, used for probing */
Igor Grinberg96b9e832010-10-10 17:59:19 +020047static struct ulpi_info ulpi_ids[] = {
48 ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
Fabio Estevam1e4cba82010-12-20 21:54:30 -020049 ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
Michal Simekead51782014-03-11 13:23:14 +010050 ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"),
Liviu Dudau79cb5b52014-05-14 21:32:39 +010051 ULPI_INFO(ULPI_ID(0x0424, 0x0009), "SMSC USB334x"),
Michal Simekead51782014-03-11 13:23:14 +010052 ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"),
Daniel Mack2d57a952009-10-15 17:09:35 +030053};
54
Heikki Krogerus298b0832012-02-13 13:24:13 +020055static int ulpi_set_otg_flags(struct usb_phy *phy)
Daniel Mack2d57a952009-10-15 17:09:35 +030056{
Igor Grinberg13dd0c92010-07-15 16:00:16 +030057 unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
58 ULPI_OTG_CTRL_DM_PULLDOWN;
Daniel Mack2d57a952009-10-15 17:09:35 +030059
Heikki Krogerus298b0832012-02-13 13:24:13 +020060 if (phy->flags & ULPI_OTG_ID_PULLUP)
Heikki Krogerusfc567f02010-05-03 09:13:02 +030061 flags |= ULPI_OTG_CTRL_ID_PULLUP;
Daniel Mack2d57a952009-10-15 17:09:35 +030062
Igor Grinberg13dd0c92010-07-15 16:00:16 +030063 /*
64 * ULPI Specification rev.1.1 default
65 * for Dp/DmPulldown is enabled.
66 */
Heikki Krogerus298b0832012-02-13 13:24:13 +020067 if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030068 flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
Daniel Mack2d57a952009-10-15 17:09:35 +030069
Heikki Krogerus298b0832012-02-13 13:24:13 +020070 if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030071 flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
Daniel Mack2d57a952009-10-15 17:09:35 +030072
Heikki Krogerus298b0832012-02-13 13:24:13 +020073 if (phy->flags & ULPI_OTG_EXTVBUSIND)
Heikki Krogerusfc567f02010-05-03 09:13:02 +030074 flags |= ULPI_OTG_CTRL_EXTVBUSIND;
Daniel Mack2d57a952009-10-15 17:09:35 +030075
Heikki Krogerus298b0832012-02-13 13:24:13 +020076 return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
Daniel Mack2d57a952009-10-15 17:09:35 +030077}
78
Heikki Krogerus298b0832012-02-13 13:24:13 +020079static int ulpi_set_fc_flags(struct usb_phy *phy)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030080{
81 unsigned int flags = 0;
82
83 /*
84 * ULPI Specification rev.1.1 default
85 * for XcvrSelect is Full Speed.
86 */
Heikki Krogerus298b0832012-02-13 13:24:13 +020087 if (phy->flags & ULPI_FC_HS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030088 flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
Heikki Krogerus298b0832012-02-13 13:24:13 +020089 else if (phy->flags & ULPI_FC_LS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030090 flags |= ULPI_FUNC_CTRL_LOW_SPEED;
Heikki Krogerus298b0832012-02-13 13:24:13 +020091 else if (phy->flags & ULPI_FC_FS4LS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030092 flags |= ULPI_FUNC_CTRL_FS4LS;
93 else
94 flags |= ULPI_FUNC_CTRL_FULL_SPEED;
95
Heikki Krogerus298b0832012-02-13 13:24:13 +020096 if (phy->flags & ULPI_FC_TERMSEL)
Igor Grinberg13dd0c92010-07-15 16:00:16 +030097 flags |= ULPI_FUNC_CTRL_TERMSELECT;
98
99 /*
100 * ULPI Specification rev.1.1 default
101 * for OpMode is Normal Operation.
102 */
Heikki Krogerus298b0832012-02-13 13:24:13 +0200103 if (phy->flags & ULPI_FC_OP_NODRV)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300104 flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200105 else if (phy->flags & ULPI_FC_OP_DIS_NRZI)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300106 flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200107 else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300108 flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP;
109 else
110 flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
111
112 /*
113 * ULPI Specification rev.1.1 default
114 * for SuspendM is Powered.
115 */
116 flags |= ULPI_FUNC_CTRL_SUSPENDM;
117
Heikki Krogerus298b0832012-02-13 13:24:13 +0200118 return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300119}
120
Heikki Krogerus298b0832012-02-13 13:24:13 +0200121static int ulpi_set_ic_flags(struct usb_phy *phy)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300122{
123 unsigned int flags = 0;
124
Heikki Krogerus298b0832012-02-13 13:24:13 +0200125 if (phy->flags & ULPI_IC_AUTORESUME)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300126 flags |= ULPI_IFC_CTRL_AUTORESUME;
127
Heikki Krogerus298b0832012-02-13 13:24:13 +0200128 if (phy->flags & ULPI_IC_EXTVBUS_INDINV)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300129 flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
130
Heikki Krogerus298b0832012-02-13 13:24:13 +0200131 if (phy->flags & ULPI_IC_IND_PASSTHRU)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300132 flags |= ULPI_IFC_CTRL_PASSTHRU;
133
Heikki Krogerus298b0832012-02-13 13:24:13 +0200134 if (phy->flags & ULPI_IC_PROTECT_DIS)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300135 flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
136
Heikki Krogerus298b0832012-02-13 13:24:13 +0200137 return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300138}
139
Heikki Krogerus298b0832012-02-13 13:24:13 +0200140static int ulpi_set_flags(struct usb_phy *phy)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300141{
142 int ret;
143
Heikki Krogerus298b0832012-02-13 13:24:13 +0200144 ret = ulpi_set_otg_flags(phy);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300145 if (ret)
146 return ret;
147
Heikki Krogerus298b0832012-02-13 13:24:13 +0200148 ret = ulpi_set_ic_flags(phy);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300149 if (ret)
150 return ret;
151
Heikki Krogerus298b0832012-02-13 13:24:13 +0200152 return ulpi_set_fc_flags(phy);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300153}
154
Heikki Krogerus298b0832012-02-13 13:24:13 +0200155static int ulpi_check_integrity(struct usb_phy *phy)
Igor Grinberga9138192010-10-10 17:59:18 +0200156{
157 int ret, i;
158 unsigned int val = 0x55;
159
160 for (i = 0; i < 2; i++) {
Heikki Krogerus298b0832012-02-13 13:24:13 +0200161 ret = usb_phy_io_write(phy, val, ULPI_SCRATCH);
Igor Grinberga9138192010-10-10 17:59:18 +0200162 if (ret < 0)
163 return ret;
164
Heikki Krogerus298b0832012-02-13 13:24:13 +0200165 ret = usb_phy_io_read(phy, ULPI_SCRATCH);
Igor Grinberga9138192010-10-10 17:59:18 +0200166 if (ret < 0)
167 return ret;
168
169 if (ret != val) {
170 pr_err("ULPI integrity check: failed!");
171 return -ENODEV;
172 }
173 val = val << 1;
174 }
175
176 pr_info("ULPI integrity check: passed.\n");
177
178 return 0;
179}
180
Heikki Krogerus298b0832012-02-13 13:24:13 +0200181static int ulpi_init(struct usb_phy *phy)
Daniel Mack2d57a952009-10-15 17:09:35 +0300182{
Wolfram Sang7b4a0362010-06-15 12:34:22 +0200183 int i, vid, pid, ret;
184 u32 ulpi_id = 0;
Daniel Mack2d57a952009-10-15 17:09:35 +0300185
Wolfram Sang7b4a0362010-06-15 12:34:22 +0200186 for (i = 0; i < 4; i++) {
Heikki Krogerus298b0832012-02-13 13:24:13 +0200187 ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i);
Wolfram Sang7b4a0362010-06-15 12:34:22 +0200188 if (ret < 0)
189 return ret;
190 ulpi_id = (ulpi_id << 8) | ret;
191 }
192 vid = ulpi_id & 0xffff;
193 pid = ulpi_id >> 16;
Daniel Mack2d57a952009-10-15 17:09:35 +0300194
195 pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid);
196
Igor Grinberg96b9e832010-10-10 17:59:19 +0200197 for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) {
198 if (ulpi_ids[i].id == ULPI_ID(vid, pid)) {
199 pr_info("Found %s ULPI transceiver.\n",
200 ulpi_ids[i].name);
Igor Grinberga9138192010-10-10 17:59:18 +0200201 break;
Igor Grinberg96b9e832010-10-10 17:59:19 +0200202 }
203 }
Daniel Mack2d57a952009-10-15 17:09:35 +0300204
Heikki Krogerus298b0832012-02-13 13:24:13 +0200205 ret = ulpi_check_integrity(phy);
Igor Grinberga9138192010-10-10 17:59:18 +0200206 if (ret)
207 return ret;
208
Heikki Krogerus298b0832012-02-13 13:24:13 +0200209 return ulpi_set_flags(phy);
Daniel Mack2d57a952009-10-15 17:09:35 +0300210}
211
Heikki Krogerus298b0832012-02-13 13:24:13 +0200212static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300213{
Antoine Tenart19c1eac2014-10-30 18:41:14 +0100214 struct usb_phy *phy = otg->usb_phy;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200215 unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300216
217 if (!host) {
218 otg->host = NULL;
219 return 0;
220 }
221
222 otg->host = host;
223
224 flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE |
225 ULPI_IFC_CTRL_3_PIN_SERIAL_MODE |
226 ULPI_IFC_CTRL_CARKITMODE);
227
Heikki Krogerus298b0832012-02-13 13:24:13 +0200228 if (phy->flags & ULPI_IC_6PIN_SERIAL)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300229 flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200230 else if (phy->flags & ULPI_IC_3PIN_SERIAL)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300231 flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200232 else if (phy->flags & ULPI_IC_CARKIT)
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300233 flags |= ULPI_IFC_CTRL_CARKITMODE;
234
Heikki Krogerus298b0832012-02-13 13:24:13 +0200235 return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300236}
237
Heikki Krogerus298b0832012-02-13 13:24:13 +0200238static int ulpi_set_vbus(struct usb_otg *otg, bool on)
Daniel Mack2d57a952009-10-15 17:09:35 +0300239{
Antoine Tenart19c1eac2014-10-30 18:41:14 +0100240 struct usb_phy *phy = otg->usb_phy;
Heikki Krogerus298b0832012-02-13 13:24:13 +0200241 unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
Daniel Mack2d57a952009-10-15 17:09:35 +0300242
Heikki Krogerusfc567f02010-05-03 09:13:02 +0300243 flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
Daniel Mack2d57a952009-10-15 17:09:35 +0300244
245 if (on) {
Heikki Krogerus298b0832012-02-13 13:24:13 +0200246 if (phy->flags & ULPI_OTG_DRVVBUS)
Heikki Krogerusfc567f02010-05-03 09:13:02 +0300247 flags |= ULPI_OTG_CTRL_DRVVBUS;
Daniel Mack2d57a952009-10-15 17:09:35 +0300248
Heikki Krogerus298b0832012-02-13 13:24:13 +0200249 if (phy->flags & ULPI_OTG_DRVVBUS_EXT)
Heikki Krogerusfc567f02010-05-03 09:13:02 +0300250 flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
Daniel Mack2d57a952009-10-15 17:09:35 +0300251 }
252
Heikki Krogerus298b0832012-02-13 13:24:13 +0200253 return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
Daniel Mack2d57a952009-10-15 17:09:35 +0300254}
255
Heikki Krogerus86753812012-02-13 13:24:02 +0200256struct usb_phy *
Heikki Krogerus298b0832012-02-13 13:24:13 +0200257otg_ulpi_create(struct usb_phy_io_ops *ops,
Daniel Mack2d57a952009-10-15 17:09:35 +0300258 unsigned int flags)
259{
Heikki Krogerus298b0832012-02-13 13:24:13 +0200260 struct usb_phy *phy;
261 struct usb_otg *otg;
Daniel Mack2d57a952009-10-15 17:09:35 +0300262
Heikki Krogerus298b0832012-02-13 13:24:13 +0200263 phy = kzalloc(sizeof(*phy), GFP_KERNEL);
264 if (!phy)
Daniel Mack2d57a952009-10-15 17:09:35 +0300265 return NULL;
266
Heikki Krogerus298b0832012-02-13 13:24:13 +0200267 otg = kzalloc(sizeof(*otg), GFP_KERNEL);
268 if (!otg) {
269 kfree(phy);
270 return NULL;
271 }
272
273 phy->label = "ULPI";
274 phy->flags = flags;
275 phy->io_ops = ops;
276 phy->otg = otg;
277 phy->init = ulpi_init;
278
Antoine Tenart19c1eac2014-10-30 18:41:14 +0100279 otg->usb_phy = phy;
Igor Grinberg13dd0c92010-07-15 16:00:16 +0300280 otg->set_host = ulpi_set_host;
Daniel Mack2d57a952009-10-15 17:09:35 +0300281 otg->set_vbus = ulpi_set_vbus;
282
Heikki Krogerus298b0832012-02-13 13:24:13 +0200283 return phy;
Daniel Mack2d57a952009-10-15 17:09:35 +0300284}
285EXPORT_SYMBOL_GPL(otg_ulpi_create);
286