Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: omap1: Tahvo USB transceiver driver

Add Tahvo USB transceiver driver.

Based on old code from linux-omap tree. The original driver was written
by Juha Yrjölä, Tony Lindgren, and Timo Teräs.

Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Felipe Balbi <balbi@ti.com>

authored by

Aaro Koskinen and committed by
Felipe Balbi
9ba96ae5 449d2ba6

+495
+16
Documentation/ABI/testing/sysfs-platform-tahvo-usb
··· 1 + What: /sys/bus/platform/devices/tahvo-usb/otg_mode 2 + Date: December 2013 3 + Contact: Aaro Koskinen <aaro.koskinen@iki.fi> 4 + Description: 5 + Set or read the current OTG mode. Valid values are "host" and 6 + "peripheral". 7 + 8 + Reading: returns the current mode. 9 + 10 + What: /sys/bus/platform/devices/tahvo-usb/vbus 11 + Date: December 2013 12 + Contact: Aaro Koskinen <aaro.koskinen@iki.fi> 13 + Description: 14 + Read the current VBUS state. 15 + 16 + Reading: returns "on" or "off".
+15
drivers/usb/phy/Kconfig
··· 152 152 This driver can also be built as a module. If so, the module 153 153 will be called omap-otg. 154 154 155 + config TAHVO_USB 156 + tristate "Tahvo USB transceiver driver" 157 + depends on MFD_RETU && EXTCON 158 + select USB_PHY 159 + help 160 + Enable this to support USB transceiver on Tahvo. This is used 161 + at least on Nokia 770. 162 + 163 + config TAHVO_USB_HOST_BY_DEFAULT 164 + depends on TAHVO_USB 165 + boolean "Device in USB host mode by default" 166 + help 167 + Say Y here, if you want the device to enter USB host mode 168 + by default on bootup. 169 + 155 170 config USB_ISP1301 156 171 tristate "NXP ISP1301 USB transceiver support" 157 172 depends on USB || USB_GADGET
+1
drivers/usb/phy/Makefile
··· 12 12 obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o 13 13 obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o 14 14 obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o 15 + obj-$(CONFIG_TAHVO_USB) += phy-tahvo.o 15 16 obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o 16 17 obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o 17 18 obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o
+463
drivers/usb/phy/phy-tahvo.c
··· 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.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.state = OTG_STATE_B_PERIPHERAL; 90 + break; 91 + case OTG_STATE_A_IDLE: 92 + /* 93 + * Session is now valid assuming the USB hub is driving 94 + * Vbus. 95 + */ 96 + tu->phy.state = OTG_STATE_A_HOST; 97 + break; 98 + default: 99 + break; 100 + } 101 + dev_info(&tu->pt_dev->dev, "USB cable connected\n"); 102 + } else { 103 + switch (tu->phy.state) { 104 + case OTG_STATE_B_PERIPHERAL: 105 + if (tu->phy.otg->gadget) 106 + usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 107 + tu->phy.state = OTG_STATE_B_IDLE; 108 + break; 109 + case OTG_STATE_A_HOST: 110 + tu->phy.state = OTG_STATE_A_IDLE; 111 + break; 112 + default: 113 + break; 114 + } 115 + dev_info(&tu->pt_dev->dev, "USB cable disconnected\n"); 116 + } 117 + 118 + prev_state = tu->vbus_state; 119 + tu->vbus_state = reg & TAHVO_STAT_VBUS; 120 + if (prev_state != tu->vbus_state) { 121 + extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); 122 + sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state"); 123 + } 124 + } 125 + 126 + static void tahvo_usb_become_host(struct tahvo_usb *tu) 127 + { 128 + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 129 + 130 + extcon_set_cable_state(&tu->extcon, "USB-HOST", true); 131 + 132 + /* Power up the transceiver in USB host mode */ 133 + retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND | 134 + USBR_MASTER_SW2 | USBR_MASTER_SW1); 135 + tu->phy.state = OTG_STATE_A_IDLE; 136 + 137 + check_vbus_state(tu); 138 + } 139 + 140 + static void tahvo_usb_stop_host(struct tahvo_usb *tu) 141 + { 142 + tu->phy.state = OTG_STATE_A_IDLE; 143 + } 144 + 145 + static void tahvo_usb_become_peripheral(struct tahvo_usb *tu) 146 + { 147 + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 148 + 149 + extcon_set_cable_state(&tu->extcon, "USB-HOST", false); 150 + 151 + /* Power up transceiver and set it in USB peripheral mode */ 152 + retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT | 153 + USBR_NSUSPEND | USBR_SLAVE_SW); 154 + tu->phy.state = OTG_STATE_B_IDLE; 155 + 156 + check_vbus_state(tu); 157 + } 158 + 159 + static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu) 160 + { 161 + if (tu->phy.otg->gadget) 162 + usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 163 + tu->phy.state = OTG_STATE_B_IDLE; 164 + } 165 + 166 + static void tahvo_usb_power_off(struct tahvo_usb *tu) 167 + { 168 + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 169 + 170 + /* Disable gadget controller if any */ 171 + if (tu->phy.otg->gadget) 172 + usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 173 + 174 + /* Power off transceiver */ 175 + retu_write(rdev, TAHVO_REG_USBR, 0); 176 + tu->phy.state = OTG_STATE_UNDEFINED; 177 + } 178 + 179 + static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend) 180 + { 181 + struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy); 182 + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 183 + u16 w; 184 + 185 + dev_dbg(&tu->pt_dev->dev, "%s\n", __func__); 186 + 187 + w = retu_read(rdev, TAHVO_REG_USBR); 188 + if (suspend) 189 + w &= ~USBR_NSUSPEND; 190 + else 191 + w |= USBR_NSUSPEND; 192 + retu_write(rdev, TAHVO_REG_USBR, w); 193 + 194 + return 0; 195 + } 196 + 197 + static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host) 198 + { 199 + struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy); 200 + 201 + dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host); 202 + 203 + if (otg == NULL) 204 + return -ENODEV; 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->phy, struct tahvo_usb, phy); 232 + 233 + dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget); 234 + 235 + if (!otg) 236 + return -ENODEV; 237 + 238 + mutex_lock(&tu->serialize); 239 + 240 + if (!gadget) { 241 + if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 242 + tahvo_usb_power_off(tu); 243 + tu->phy.otg->gadget = NULL; 244 + mutex_unlock(&tu->serialize); 245 + return 0; 246 + } 247 + 248 + tu->phy.otg->gadget = gadget; 249 + if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 250 + tahvo_usb_become_peripheral(tu); 251 + 252 + mutex_unlock(&tu->serialize); 253 + 254 + return 0; 255 + } 256 + 257 + static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu) 258 + { 259 + struct tahvo_usb *tu = _tu; 260 + 261 + mutex_lock(&tu->serialize); 262 + check_vbus_state(tu); 263 + mutex_unlock(&tu->serialize); 264 + 265 + return IRQ_HANDLED; 266 + } 267 + 268 + static ssize_t otg_mode_show(struct device *device, 269 + struct device_attribute *attr, char *buf) 270 + { 271 + struct tahvo_usb *tu = dev_get_drvdata(device); 272 + 273 + switch (tu->tahvo_mode) { 274 + case TAHVO_MODE_HOST: 275 + return sprintf(buf, "host\n"); 276 + case TAHVO_MODE_PERIPHERAL: 277 + return sprintf(buf, "peripheral\n"); 278 + } 279 + 280 + return -EINVAL; 281 + } 282 + 283 + static ssize_t otg_mode_store(struct device *device, 284 + struct device_attribute *attr, 285 + const char *buf, size_t count) 286 + { 287 + struct tahvo_usb *tu = dev_get_drvdata(device); 288 + int r; 289 + 290 + mutex_lock(&tu->serialize); 291 + if (count >= 4 && strncmp(buf, "host", 4) == 0) { 292 + if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 293 + tahvo_usb_stop_peripheral(tu); 294 + tu->tahvo_mode = TAHVO_MODE_HOST; 295 + if (tu->phy.otg->host) { 296 + dev_info(device, "HOST mode: host controller present\n"); 297 + tahvo_usb_become_host(tu); 298 + } else { 299 + dev_info(device, "HOST mode: no host controller, powering off\n"); 300 + tahvo_usb_power_off(tu); 301 + } 302 + r = strlen(buf); 303 + } else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) { 304 + if (tu->tahvo_mode == TAHVO_MODE_HOST) 305 + tahvo_usb_stop_host(tu); 306 + tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; 307 + if (tu->phy.otg->gadget) { 308 + dev_info(device, "PERIPHERAL mode: gadget driver present\n"); 309 + tahvo_usb_become_peripheral(tu); 310 + } else { 311 + dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n"); 312 + tahvo_usb_power_off(tu); 313 + } 314 + r = strlen(buf); 315 + } else { 316 + r = -EINVAL; 317 + } 318 + mutex_unlock(&tu->serialize); 319 + 320 + return r; 321 + } 322 + static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store); 323 + 324 + static struct attribute *tahvo_attributes[] = { 325 + &dev_attr_vbus.attr, 326 + &dev_attr_otg_mode.attr, 327 + NULL 328 + }; 329 + 330 + static struct attribute_group tahvo_attr_group = { 331 + .attrs = tahvo_attributes, 332 + }; 333 + 334 + static int tahvo_usb_probe(struct platform_device *pdev) 335 + { 336 + struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent); 337 + struct tahvo_usb *tu; 338 + int ret; 339 + 340 + tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL); 341 + if (!tu) 342 + return -ENOMEM; 343 + 344 + tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg), 345 + GFP_KERNEL); 346 + if (!tu->phy.otg) 347 + return -ENOMEM; 348 + 349 + tu->pt_dev = pdev; 350 + 351 + /* Default mode */ 352 + #ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT 353 + tu->tahvo_mode = TAHVO_MODE_HOST; 354 + #else 355 + tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; 356 + #endif 357 + 358 + mutex_init(&tu->serialize); 359 + 360 + tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick"); 361 + if (!IS_ERR(tu->ick)) 362 + clk_enable(tu->ick); 363 + 364 + /* 365 + * Set initial state, so that we generate kevents only on state changes. 366 + */ 367 + tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS; 368 + 369 + tu->extcon.name = DRIVER_NAME; 370 + tu->extcon.supported_cable = tahvo_cable; 371 + tu->extcon.dev.parent = &pdev->dev; 372 + 373 + ret = extcon_dev_register(&tu->extcon); 374 + if (ret) { 375 + dev_err(&pdev->dev, "could not register extcon device: %d\n", 376 + ret); 377 + goto err_disable_clk; 378 + } 379 + 380 + /* Set the initial cable state. */ 381 + extcon_set_cable_state(&tu->extcon, "USB-HOST", 382 + tu->tahvo_mode == TAHVO_MODE_HOST); 383 + extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); 384 + 385 + /* Create OTG interface */ 386 + tahvo_usb_power_off(tu); 387 + tu->phy.dev = &pdev->dev; 388 + tu->phy.state = OTG_STATE_UNDEFINED; 389 + tu->phy.label = DRIVER_NAME; 390 + tu->phy.set_suspend = tahvo_usb_set_suspend; 391 + 392 + tu->phy.otg->phy = &tu->phy; 393 + tu->phy.otg->set_host = tahvo_usb_set_host; 394 + tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral; 395 + 396 + ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2); 397 + if (ret < 0) { 398 + dev_err(&pdev->dev, "cannot register USB transceiver: %d\n", 399 + ret); 400 + goto err_extcon_unreg; 401 + } 402 + 403 + dev_set_drvdata(&pdev->dev, tu); 404 + 405 + tu->irq = platform_get_irq(pdev, 0); 406 + ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, 0, 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 + .owner = THIS_MODULE, 456 + }, 457 + }; 458 + module_platform_driver(tahvo_usb_driver); 459 + 460 + MODULE_DESCRIPTION("Tahvo USB transceiver driver"); 461 + MODULE_LICENSE("GPL"); 462 + MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs"); 463 + MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");