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 21 struct 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 */ 34 static 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 42 static 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 66 static 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 108 static 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 127 static 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 142 static 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 168 static 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 199 static 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 225 static 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 243 struct usb_phy * 244 otg_ulpi_create(struct usb_phy_io_ops *ops, 245 unsigned int flags) 246 { 247 struct usb_phy *phy; 248 struct usb_otg *otg; 249 250 phy = kzalloc(sizeof(*phy), GFP_KERNEL); 251 if (!phy) 252 return NULL; 253 254 otg = kzalloc(sizeof(*otg), GFP_KERNEL); 255 if (!otg) { 256 kfree(phy); 257 return NULL; 258 } 259 260 phy->label = "ULPI"; 261 phy->flags = flags; 262 phy->io_ops = ops; 263 phy->otg = otg; 264 phy->init = ulpi_init; 265 266 otg->usb_phy = phy; 267 otg->set_host = ulpi_set_host; 268 otg->set_vbus = ulpi_set_vbus; 269 270 return phy; 271 } 272 EXPORT_SYMBOL_GPL(otg_ulpi_create); 273 274