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 52struct 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 63static const char *tahvo_cable[] = { 64 "USB-HOST", 65 "USB", 66 NULL, 67}; 68 69static 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} 75static DEVICE_ATTR(vbus, 0444, vbus_state_show, NULL); 76 77static 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 128static 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 142static void tahvo_usb_stop_host(struct tahvo_usb *tu) 143{ 144 tu->phy.otg->state = OTG_STATE_A_IDLE; 145} 146 147static 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 161static 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 168static 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 181static 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 199static 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 228static 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 255static 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 266static 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 281static 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} 320static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store); 321 322static struct attribute *tahvo_attributes[] = { 323 &dev_attr_vbus.attr, 324 &dev_attr_otg_mode.attr, 325 NULL 326}; 327 328static struct attribute_group tahvo_attr_group = { 329 .attrs = tahvo_attributes, 330}; 331 332static 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, 405 IRQF_ONESHOT, 406 "tahvo-vbus", tu); 407 if (ret) { 408 dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n", 409 ret); 410 goto err_remove_phy; 411 } 412 413 /* Attributes */ 414 ret = sysfs_create_group(&pdev->dev.kobj, &tahvo_attr_group); 415 if (ret) { 416 dev_err(&pdev->dev, "cannot create sysfs group: %d\n", ret); 417 goto err_free_irq; 418 } 419 420 return 0; 421 422err_free_irq: 423 free_irq(tu->irq, tu); 424err_remove_phy: 425 usb_remove_phy(&tu->phy); 426err_extcon_unreg: 427 extcon_dev_unregister(&tu->extcon); 428err_disable_clk: 429 if (!IS_ERR(tu->ick)) 430 clk_disable(tu->ick); 431 432 return ret; 433} 434 435static int tahvo_usb_remove(struct platform_device *pdev) 436{ 437 struct tahvo_usb *tu = platform_get_drvdata(pdev); 438 439 sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group); 440 free_irq(tu->irq, tu); 441 usb_remove_phy(&tu->phy); 442 extcon_dev_unregister(&tu->extcon); 443 if (!IS_ERR(tu->ick)) 444 clk_disable(tu->ick); 445 446 return 0; 447} 448 449static struct platform_driver tahvo_usb_driver = { 450 .probe = tahvo_usb_probe, 451 .remove = tahvo_usb_remove, 452 .driver = { 453 .name = "tahvo-usb", 454 }, 455}; 456module_platform_driver(tahvo_usb_driver); 457 458MODULE_DESCRIPTION("Tahvo USB transceiver driver"); 459MODULE_LICENSE("GPL"); 460MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs"); 461MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); 462