phy-ulpi.c (6590B)
1// SPDX-License-Identifier: GPL-2.0+ 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 13#include <linux/kernel.h> 14#include <linux/slab.h> 15#include <linux/export.h> 16#include <linux/usb.h> 17#include <linux/usb/otg.h> 18#include <linux/usb/ulpi.h> 19 20 21struct ulpi_info { 22 unsigned int id; 23 char *name; 24}; 25 26#define ULPI_ID(vendor, product) (((vendor) << 16) | (product)) 27#define ULPI_INFO(_id, _name) \ 28 { \ 29 .id = (_id), \ 30 .name = (_name), \ 31 } 32 33/* ULPI hardcoded IDs, used for probing */ 34static struct ulpi_info ulpi_ids[] = { 35 ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"), 36 ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), 37 ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"), 38 ULPI_INFO(ULPI_ID(0x0424, 0x0009), "SMSC USB334x"), 39 ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"), 40}; 41 42static int ulpi_set_otg_flags(struct usb_phy *phy) 43{ 44 unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN | 45 ULPI_OTG_CTRL_DM_PULLDOWN; 46 47 if (phy->flags & ULPI_OTG_ID_PULLUP) 48 flags |= ULPI_OTG_CTRL_ID_PULLUP; 49 50 /* 51 * ULPI Specification rev.1.1 default 52 * for Dp/DmPulldown is enabled. 53 */ 54 if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS) 55 flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN; 56 57 if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS) 58 flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN; 59 60 if (phy->flags & ULPI_OTG_EXTVBUSIND) 61 flags |= ULPI_OTG_CTRL_EXTVBUSIND; 62 63 return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); 64} 65 66static int ulpi_set_fc_flags(struct usb_phy *phy) 67{ 68 unsigned int flags = 0; 69 70 /* 71 * ULPI Specification rev.1.1 default 72 * for XcvrSelect is Full Speed. 73 */ 74 if (phy->flags & ULPI_FC_HS) 75 flags |= ULPI_FUNC_CTRL_HIGH_SPEED; 76 else if (phy->flags & ULPI_FC_LS) 77 flags |= ULPI_FUNC_CTRL_LOW_SPEED; 78 else if (phy->flags & ULPI_FC_FS4LS) 79 flags |= ULPI_FUNC_CTRL_FS4LS; 80 else 81 flags |= ULPI_FUNC_CTRL_FULL_SPEED; 82 83 if (phy->flags & ULPI_FC_TERMSEL) 84 flags |= ULPI_FUNC_CTRL_TERMSELECT; 85 86 /* 87 * ULPI Specification rev.1.1 default 88 * for OpMode is Normal Operation. 89 */ 90 if (phy->flags & ULPI_FC_OP_NODRV) 91 flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; 92 else if (phy->flags & ULPI_FC_OP_DIS_NRZI) 93 flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI; 94 else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP) 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 105 return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL); 106} 107 108static int ulpi_set_ic_flags(struct usb_phy *phy) 109{ 110 unsigned int flags = 0; 111 112 if (phy->flags & ULPI_IC_AUTORESUME) 113 flags |= ULPI_IFC_CTRL_AUTORESUME; 114 115 if (phy->flags & ULPI_IC_EXTVBUS_INDINV) 116 flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS; 117 118 if (phy->flags & ULPI_IC_IND_PASSTHRU) 119 flags |= ULPI_IFC_CTRL_PASSTHRU; 120 121 if (phy->flags & ULPI_IC_PROTECT_DIS) 122 flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE; 123 124 return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); 125} 126 127static int ulpi_set_flags(struct usb_phy *phy) 128{ 129 int ret; 130 131 ret = ulpi_set_otg_flags(phy); 132 if (ret) 133 return ret; 134 135 ret = ulpi_set_ic_flags(phy); 136 if (ret) 137 return ret; 138 139 return ulpi_set_fc_flags(phy); 140} 141 142static int ulpi_check_integrity(struct usb_phy *phy) 143{ 144 int ret, i; 145 unsigned int val = 0x55; 146 147 for (i = 0; i < 2; i++) { 148 ret = usb_phy_io_write(phy, val, ULPI_SCRATCH); 149 if (ret < 0) 150 return ret; 151 152 ret = usb_phy_io_read(phy, ULPI_SCRATCH); 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 168static int ulpi_init(struct usb_phy *phy) 169{ 170 int i, vid, pid, ret; 171 u32 ulpi_id = 0; 172 173 for (i = 0; i < 4; i++) { 174 ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i); 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; 181 182 pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid); 183 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); 188 break; 189 } 190 } 191 192 ret = ulpi_check_integrity(phy); 193 if (ret) 194 return ret; 195 196 return ulpi_set_flags(phy); 197} 198 199static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host) 200{ 201 struct usb_phy *phy = otg->usb_phy; 202 unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL); 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 215 if (phy->flags & ULPI_IC_6PIN_SERIAL) 216 flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE; 217 else if (phy->flags & ULPI_IC_3PIN_SERIAL) 218 flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE; 219 else if (phy->flags & ULPI_IC_CARKIT) 220 flags |= ULPI_IFC_CTRL_CARKITMODE; 221 222 return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); 223} 224 225static int ulpi_set_vbus(struct usb_otg *otg, bool on) 226{ 227 struct usb_phy *phy = otg->usb_phy; 228 unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL); 229 230 flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT); 231 232 if (on) { 233 if (phy->flags & ULPI_OTG_DRVVBUS) 234 flags |= ULPI_OTG_CTRL_DRVVBUS; 235 236 if (phy->flags & ULPI_OTG_DRVVBUS_EXT) 237 flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; 238 } 239 240 return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); 241} 242 243static void otg_ulpi_init(struct usb_phy *phy, struct usb_otg *otg, 244 struct usb_phy_io_ops *ops, 245 unsigned int flags) 246{ 247 phy->label = "ULPI"; 248 phy->flags = flags; 249 phy->io_ops = ops; 250 phy->otg = otg; 251 phy->init = ulpi_init; 252 253 otg->usb_phy = phy; 254 otg->set_host = ulpi_set_host; 255 otg->set_vbus = ulpi_set_vbus; 256} 257 258struct usb_phy * 259otg_ulpi_create(struct usb_phy_io_ops *ops, 260 unsigned int flags) 261{ 262 struct usb_phy *phy; 263 struct usb_otg *otg; 264 265 phy = kzalloc(sizeof(*phy), GFP_KERNEL); 266 if (!phy) 267 return NULL; 268 269 otg = kzalloc(sizeof(*otg), GFP_KERNEL); 270 if (!otg) { 271 kfree(phy); 272 return NULL; 273 } 274 275 otg_ulpi_init(phy, otg, ops, flags); 276 277 return phy; 278} 279EXPORT_SYMBOL_GPL(otg_ulpi_create); 280 281struct usb_phy * 282devm_otg_ulpi_create(struct device *dev, 283 struct usb_phy_io_ops *ops, 284 unsigned int flags) 285{ 286 struct usb_phy *phy; 287 struct usb_otg *otg; 288 289 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 290 if (!phy) 291 return NULL; 292 293 otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); 294 if (!otg) { 295 devm_kfree(dev, phy); 296 return NULL; 297 } 298 299 otg_ulpi_init(phy, otg, ops, flags); 300 301 return phy; 302} 303EXPORT_SYMBOL_GPL(devm_otg_ulpi_create);