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