1 /* 2 * Tahvo USB transceiver driver 3 * 4 * Copyright (C) 2005-2006 Nokia Corporation 5 * 6 * Parts copied from isp1301_omap.c. 7 * Copyright (C) 2004 Texas Instruments 8 * Copyright (C) 2004 David Brownell 9 * 10 * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs. 11 * Modified for Retu/Tahvo MFD by Aaro Koskinen. 12 * 13 * This file is subject to the terms and conditions of the GNU General 14 * Public License. See the file "COPYING" in the main directory of this 15 * archive for more details. 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 23 #include <linux/io.h> 24 #include <linux/clk.h> 25 #include <linux/usb.h> 26 #include <linux/extcon.h> 27 #include <linux/kernel.h> 28 #include <linux/module.h> 29 #include <linux/usb/otg.h> 30 #include <linux/mfd/retu.h> 31 #include <linux/usb/gadget.h> 32 #include <linux/platform_device.h> 33 34 #define DRIVER_NAME "tahvo-usb" 35 36 #define TAHVO_REG_IDSR 0x02 37 #define TAHVO_REG_USBR 0x06 38 39 #define USBR_SLAVE_CONTROL (1 << 8) 40 #define USBR_VPPVIO_SW (1 << 7) 41 #define USBR_SPEED (1 << 6) 42 #define USBR_REGOUT (1 << 5) 43 #define USBR_MASTER_SW2 (1 << 4) 44 #define USBR_MASTER_SW1 (1 << 3) 45 #define USBR_SLAVE_SW (1 << 2) 46 #define USBR_NSUSPEND (1 << 1) 47 #define USBR_SEMODE (1 << 0) 48 49 #define TAHVO_MODE_HOST 0 50 #define TAHVO_MODE_PERIPHERAL 1 51 52 struct tahvo_usb { 53 struct platform_device *pt_dev; 54 struct usb_phy phy; 55 int vbus_state; 56 struct mutex serialize; 57 struct clk *ick; 58 int irq; 59 int tahvo_mode; 60 struct extcon_dev extcon; 61 }; 62 63 static const char *tahvo_cable[] = { 64 "USB-HOST", 65 "USB", 66 NULL, 67 }; 68 69 static ssize_t vbus_state_show(struct device *device, 70 struct device_attribute *attr, char *buf) 71 { 72 struct tahvo_usb *tu = dev_get_drvdata(device); 73 return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off"); 74 } 75 static DEVICE_ATTR(vbus, 0444, vbus_state_show, NULL); 76 77 static void check_vbus_state(struct tahvo_usb *tu) 78 { 79 struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 80 int reg, prev_state; 81 82 reg = retu_read(rdev, TAHVO_REG_IDSR); 83 if (reg & TAHVO_STAT_VBUS) { 84 switch (tu->phy.otg->state) { 85 case OTG_STATE_B_IDLE: 86 /* Enable the gadget driver */ 87 if (tu->phy.otg->gadget) 88 usb_gadget_vbus_connect(tu->phy.otg->gadget); 89 tu->phy.otg->state = OTG_STATE_B_PERIPHERAL; 90 usb_phy_set_event(&tu->phy, USB_EVENT_ENUMERATED); 91 break; 92 case OTG_STATE_A_IDLE: 93 /* 94 * Session is now valid assuming the USB hub is driving 95 * Vbus. 96 */ 97 tu->phy.otg->state = OTG_STATE_A_HOST; 98 break; 99 default: 100 break; 101 } 102 dev_info(&tu->pt_dev->dev, "USB cable connected\n"); 103 } else { 104 switch (tu->phy.otg->state) { 105 case OTG_STATE_B_PERIPHERAL: 106 if (tu->phy.otg->gadget) 107 usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 108 tu->phy.otg->state = OTG_STATE_B_IDLE; 109 usb_phy_set_event(&tu->phy, USB_EVENT_NONE); 110 break; 111 case OTG_STATE_A_HOST: 112 tu->phy.otg->state = OTG_STATE_A_IDLE; 113 break; 114 default: 115 break; 116 } 117 dev_info(&tu->pt_dev->dev, "USB cable disconnected\n"); 118 } 119 120 prev_state = tu->vbus_state; 121 tu->vbus_state = reg & TAHVO_STAT_VBUS; 122 if (prev_state != tu->vbus_state) { 123 extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); 124 sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state"); 125 } 126 } 127 128 static void tahvo_usb_become_host(struct tahvo_usb *tu) 129 { 130 struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 131 132 extcon_set_cable_state(&tu->extcon, "USB-HOST", true); 133 134 /* Power up the transceiver in USB host mode */ 135 retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND | 136 USBR_MASTER_SW2 | USBR_MASTER_SW1); 137 tu->phy.otg->state = OTG_STATE_A_IDLE; 138 139 check_vbus_state(tu); 140 } 141 142 static void tahvo_usb_stop_host(struct tahvo_usb *tu) 143 { 144 tu->phy.otg->state = OTG_STATE_A_IDLE; 145 } 146 147 static void tahvo_usb_become_peripheral(struct tahvo_usb *tu) 148 { 149 struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 150 151 extcon_set_cable_state(&tu->extcon, "USB-HOST", false); 152 153 /* Power up transceiver and set it in USB peripheral mode */ 154 retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT | 155 USBR_NSUSPEND | USBR_SLAVE_SW); 156 tu->phy.otg->state = OTG_STATE_B_IDLE; 157 158 check_vbus_state(tu); 159 } 160 161 static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu) 162 { 163 if (tu->phy.otg->gadget) 164 usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 165 tu->phy.otg->state = OTG_STATE_B_IDLE; 166 } 167 168 static void tahvo_usb_power_off(struct tahvo_usb *tu) 169 { 170 struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 171 172 /* Disable gadget controller if any */ 173 if (tu->phy.otg->gadget) 174 usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 175 176 /* Power off transceiver */ 177 retu_write(rdev, TAHVO_REG_USBR, 0); 178 tu->phy.otg->state = OTG_STATE_UNDEFINED; 179 } 180 181 static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend) 182 { 183 struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy); 184 struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 185 u16 w; 186 187 dev_dbg(&tu->pt_dev->dev, "%s\n", __func__); 188 189 w = retu_read(rdev, TAHVO_REG_USBR); 190 if (suspend) 191 w &= ~USBR_NSUSPEND; 192 else 193 w |= USBR_NSUSPEND; 194 retu_write(rdev, TAHVO_REG_USBR, w); 195 196 return 0; 197 } 198 199 static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host) 200 { 201 struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb, 202 phy); 203 204 dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host); 205 206 mutex_lock(&tu->serialize); 207 208 if (host == NULL) { 209 if (tu->tahvo_mode == TAHVO_MODE_HOST) 210 tahvo_usb_power_off(tu); 211 otg->host = NULL; 212 mutex_unlock(&tu->serialize); 213 return 0; 214 } 215 216 if (tu->tahvo_mode == TAHVO_MODE_HOST) { 217 otg->host = NULL; 218 tahvo_usb_become_host(tu); 219 } 220 221 otg->host = host; 222 223 mutex_unlock(&tu->serialize); 224 225 return 0; 226 } 227 228 static int tahvo_usb_set_peripheral(struct usb_otg *otg, 229 struct usb_gadget *gadget) 230 { 231 struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb, 232 phy); 233 234 dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget); 235 236 mutex_lock(&tu->serialize); 237 238 if (!gadget) { 239 if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 240 tahvo_usb_power_off(tu); 241 tu->phy.otg->gadget = NULL; 242 mutex_unlock(&tu->serialize); 243 return 0; 244 } 245 246 tu->phy.otg->gadget = gadget; 247 if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 248 tahvo_usb_become_peripheral(tu); 249 250 mutex_unlock(&tu->serialize); 251 252 return 0; 253 } 254 255 static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu) 256 { 257 struct tahvo_usb *tu = _tu; 258 259 mutex_lock(&tu->serialize); 260 check_vbus_state(tu); 261 mutex_unlock(&tu->serialize); 262 263 return IRQ_HANDLED; 264 } 265 266 static ssize_t otg_mode_show(struct device *device, 267 struct device_attribute *attr, char *buf) 268 { 269 struct tahvo_usb *tu = dev_get_drvdata(device); 270 271 switch (tu->tahvo_mode) { 272 case TAHVO_MODE_HOST: 273 return sprintf(buf, "host\n"); 274 case TAHVO_MODE_PERIPHERAL: 275 return sprintf(buf, "peripheral\n"); 276 } 277 278 return -EINVAL; 279 } 280 281 static ssize_t otg_mode_store(struct device *device, 282 struct device_attribute *attr, 283 const char *buf, size_t count) 284 { 285 struct tahvo_usb *tu = dev_get_drvdata(device); 286 int r; 287 288 mutex_lock(&tu->serialize); 289 if (count >= 4 && strncmp(buf, "host", 4) == 0) { 290 if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 291 tahvo_usb_stop_peripheral(tu); 292 tu->tahvo_mode = TAHVO_MODE_HOST; 293 if (tu->phy.otg->host) { 294 dev_info(device, "HOST mode: host controller present\n"); 295 tahvo_usb_become_host(tu); 296 } else { 297 dev_info(device, "HOST mode: no host controller, powering off\n"); 298 tahvo_usb_power_off(tu); 299 } 300 r = strlen(buf); 301 } else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) { 302 if (tu->tahvo_mode == TAHVO_MODE_HOST) 303 tahvo_usb_stop_host(tu); 304 tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; 305 if (tu->phy.otg->gadget) { 306 dev_info(device, "PERIPHERAL mode: gadget driver present\n"); 307 tahvo_usb_become_peripheral(tu); 308 } else { 309 dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n"); 310 tahvo_usb_power_off(tu); 311 } 312 r = strlen(buf); 313 } else { 314 r = -EINVAL; 315 } 316 mutex_unlock(&tu->serialize); 317 318 return r; 319 } 320 static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store); 321 322 static struct attribute *tahvo_attributes[] = { 323 &dev_attr_vbus.attr, 324 &dev_attr_otg_mode.attr, 325 NULL 326 }; 327 328 static struct attribute_group tahvo_attr_group = { 329 .attrs = tahvo_attributes, 330 }; 331 332 static int tahvo_usb_probe(struct platform_device *pdev) 333 { 334 struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent); 335 struct tahvo_usb *tu; 336 int ret; 337 338 tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL); 339 if (!tu) 340 return -ENOMEM; 341 342 tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg), 343 GFP_KERNEL); 344 if (!tu->phy.otg) 345 return -ENOMEM; 346 347 tu->pt_dev = pdev; 348 349 /* Default mode */ 350 #ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT 351 tu->tahvo_mode = TAHVO_MODE_HOST; 352 #else 353 tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; 354 #endif 355 356 mutex_init(&tu->serialize); 357 358 tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick"); 359 if (!IS_ERR(tu->ick)) 360 clk_enable(tu->ick); 361 362 /* 363 * Set initial state, so that we generate kevents only on state changes. 364 */ 365 tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS; 366 367 tu->extcon.name = DRIVER_NAME; 368 tu->extcon.supported_cable = tahvo_cable; 369 tu->extcon.dev.parent = &pdev->dev; 370 371 ret = extcon_dev_register(&tu->extcon); 372 if (ret) { 373 dev_err(&pdev->dev, "could not register extcon device: %d\n", 374 ret); 375 goto err_disable_clk; 376 } 377 378 /* Set the initial cable state. */ 379 extcon_set_cable_state(&tu->extcon, "USB-HOST", 380 tu->tahvo_mode == TAHVO_MODE_HOST); 381 extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); 382 383 /* Create OTG interface */ 384 tahvo_usb_power_off(tu); 385 tu->phy.dev = &pdev->dev; 386 tu->phy.otg->state = OTG_STATE_UNDEFINED; 387 tu->phy.label = DRIVER_NAME; 388 tu->phy.set_suspend = tahvo_usb_set_suspend; 389 390 tu->phy.otg->usb_phy = &tu->phy; 391 tu->phy.otg->set_host = tahvo_usb_set_host; 392 tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral; 393 394 ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2); 395 if (ret < 0) { 396 dev_err(&pdev->dev, "cannot register USB transceiver: %d\n", 397 ret); 398 goto err_extcon_unreg; 399 } 400 401 dev_set_drvdata(&pdev->dev, tu); 402 403 tu->irq = platform_get_irq(pdev, 0); 404 ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, 0, 405 "tahvo-vbus", tu); 406 if (ret) { 407 dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n", 408 ret); 409 goto err_remove_phy; 410 } 411 412 /* Attributes */ 413 ret = sysfs_create_group(&pdev->dev.kobj, &tahvo_attr_group); 414 if (ret) { 415 dev_err(&pdev->dev, "cannot create sysfs group: %d\n", ret); 416 goto err_free_irq; 417 } 418 419 return 0; 420 421 err_free_irq: 422 free_irq(tu->irq, tu); 423 err_remove_phy: 424 usb_remove_phy(&tu->phy); 425 err_extcon_unreg: 426 extcon_dev_unregister(&tu->extcon); 427 err_disable_clk: 428 if (!IS_ERR(tu->ick)) 429 clk_disable(tu->ick); 430 431 return ret; 432 } 433 434 static int tahvo_usb_remove(struct platform_device *pdev) 435 { 436 struct tahvo_usb *tu = platform_get_drvdata(pdev); 437 438 sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group); 439 free_irq(tu->irq, tu); 440 usb_remove_phy(&tu->phy); 441 extcon_dev_unregister(&tu->extcon); 442 if (!IS_ERR(tu->ick)) 443 clk_disable(tu->ick); 444 445 return 0; 446 } 447 448 static struct platform_driver tahvo_usb_driver = { 449 .probe = tahvo_usb_probe, 450 .remove = tahvo_usb_remove, 451 .driver = { 452 .name = "tahvo-usb", 453 }, 454 }; 455 module_platform_driver(tahvo_usb_driver); 456 457 MODULE_DESCRIPTION("Tahvo USB transceiver driver"); 458 MODULE_LICENSE("GPL"); 459 MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs"); 460 MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); 461