19ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen/* 29ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Tahvo USB transceiver driver 39ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * 49ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Copyright (C) 2005-2006 Nokia Corporation 59ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * 69ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Parts copied from isp1301_omap.c. 79ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Copyright (C) 2004 Texas Instruments 89ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Copyright (C) 2004 David Brownell 99ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * 109ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs. 119ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Modified for Retu/Tahvo MFD by Aaro Koskinen. 129ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * 139ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * This file is subject to the terms and conditions of the GNU General 149ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Public License. See the file "COPYING" in the main directory of this 159ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * archive for more details. 169ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * 179ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * This program is distributed in the hope that it will be useful, 189ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * but WITHOUT ANY WARRANTY; without even the implied warranty of 199ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 209ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * GNU General Public License for more details. 219ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen */ 229ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 239ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#include <linux/io.h> 249ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#include <linux/clk.h> 259ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#include <linux/usb.h> 269ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#include <linux/extcon.h> 279ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#include <linux/kernel.h> 289ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#include <linux/module.h> 299ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#include <linux/usb/otg.h> 309ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#include <linux/mfd/retu.h> 319ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#include <linux/usb/gadget.h> 329ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#include <linux/platform_device.h> 339ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 349ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define DRIVER_NAME "tahvo-usb" 359ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 369ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define TAHVO_REG_IDSR 0x02 379ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define TAHVO_REG_USBR 0x06 389ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 399ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define USBR_SLAVE_CONTROL (1 << 8) 409ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define USBR_VPPVIO_SW (1 << 7) 419ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define USBR_SPEED (1 << 6) 429ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define USBR_REGOUT (1 << 5) 439ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define USBR_MASTER_SW2 (1 << 4) 449ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define USBR_MASTER_SW1 (1 << 3) 459ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define USBR_SLAVE_SW (1 << 2) 469ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define USBR_NSUSPEND (1 << 1) 479ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define USBR_SEMODE (1 << 0) 489ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 499ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define TAHVO_MODE_HOST 0 509ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#define TAHVO_MODE_PERIPHERAL 1 519ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 529ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstruct tahvo_usb { 539ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct platform_device *pt_dev; 549ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct usb_phy phy; 559ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen int vbus_state; 569ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct mutex serialize; 579ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct clk *ick; 589ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen int irq; 599ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen int tahvo_mode; 609ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct extcon_dev extcon; 619ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen}; 629ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 639ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic const char *tahvo_cable[] = { 649ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen "USB-HOST", 659ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen "USB", 669ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen NULL, 679ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen}; 689ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 699ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic ssize_t vbus_state_show(struct device *device, 709ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct device_attribute *attr, char *buf) 719ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 729ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct tahvo_usb *tu = dev_get_drvdata(device); 739ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off"); 749ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 759ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic DEVICE_ATTR(vbus, 0444, vbus_state_show, NULL); 769ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 779ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic void check_vbus_state(struct tahvo_usb *tu) 789ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 799ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 809ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen int reg, prev_state; 819ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 829ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen reg = retu_read(rdev, TAHVO_REG_IDSR); 839ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (reg & TAHVO_STAT_VBUS) { 849ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen switch (tu->phy.state) { 859ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen case OTG_STATE_B_IDLE: 869ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* Enable the gadget driver */ 879ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->phy.otg->gadget) 889ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen usb_gadget_vbus_connect(tu->phy.otg->gadget); 899ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.state = OTG_STATE_B_PERIPHERAL; 909ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen break; 919ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen case OTG_STATE_A_IDLE: 929ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* 939ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Session is now valid assuming the USB hub is driving 949ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Vbus. 959ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen */ 969ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.state = OTG_STATE_A_HOST; 979ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen break; 989ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen default: 999ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen break; 1009ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 1019ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_info(&tu->pt_dev->dev, "USB cable connected\n"); 1029ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } else { 1039ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen switch (tu->phy.state) { 1049ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen case OTG_STATE_B_PERIPHERAL: 1059ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->phy.otg->gadget) 1069ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 1079ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.state = OTG_STATE_B_IDLE; 1089ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen break; 1099ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen case OTG_STATE_A_HOST: 1109ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.state = OTG_STATE_A_IDLE; 1119ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen break; 1129ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen default: 1139ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen break; 1149ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 1159ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_info(&tu->pt_dev->dev, "USB cable disconnected\n"); 1169ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 1179ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1189ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen prev_state = tu->vbus_state; 1199ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->vbus_state = reg & TAHVO_STAT_VBUS; 1209ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (prev_state != tu->vbus_state) { 1219ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); 1229ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state"); 1239ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 1249ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 1259ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1269ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic void tahvo_usb_become_host(struct tahvo_usb *tu) 1279ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 1289ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 1299ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1309ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen extcon_set_cable_state(&tu->extcon, "USB-HOST", true); 1319ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1329ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* Power up the transceiver in USB host mode */ 1339ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND | 1349ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen USBR_MASTER_SW2 | USBR_MASTER_SW1); 1359ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.state = OTG_STATE_A_IDLE; 1369ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1379ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen check_vbus_state(tu); 1389ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 1399ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1409ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic void tahvo_usb_stop_host(struct tahvo_usb *tu) 1419ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 1429ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.state = OTG_STATE_A_IDLE; 1439ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 1449ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1459ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic void tahvo_usb_become_peripheral(struct tahvo_usb *tu) 1469ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 1479ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 1489ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1499ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen extcon_set_cable_state(&tu->extcon, "USB-HOST", false); 1509ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1519ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* Power up transceiver and set it in USB peripheral mode */ 1529ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT | 1539ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen USBR_NSUSPEND | USBR_SLAVE_SW); 1549ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.state = OTG_STATE_B_IDLE; 1559ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1569ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen check_vbus_state(tu); 1579ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 1589ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1599ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic void tahvo_usb_stop_peripheral(struct tahvo_usb *tu) 1609ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 1619ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->phy.otg->gadget) 1629ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 1639ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.state = OTG_STATE_B_IDLE; 1649ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 1659ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1669ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic void tahvo_usb_power_off(struct tahvo_usb *tu) 1679ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 1689ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 1699ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1709ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* Disable gadget controller if any */ 1719ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->phy.otg->gadget) 1729ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 1739ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1749ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* Power off transceiver */ 1759ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen retu_write(rdev, TAHVO_REG_USBR, 0); 1769ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.state = OTG_STATE_UNDEFINED; 1779ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 1789ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1799ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend) 1809ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 1819ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy); 1829ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 1839ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen u16 w; 1849ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1859ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_dbg(&tu->pt_dev->dev, "%s\n", __func__); 1869ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1879ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen w = retu_read(rdev, TAHVO_REG_USBR); 1889ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (suspend) 1899ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen w &= ~USBR_NSUSPEND; 1909ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen else 1919ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen w |= USBR_NSUSPEND; 1929ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen retu_write(rdev, TAHVO_REG_USBR, w); 1939ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1949ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return 0; 1959ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 1969ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 1979ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host) 1989ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 1999ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy); 2009ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2019ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host); 2029ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2039ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_lock(&tu->serialize); 2049ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2059ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (host == NULL) { 2069ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->tahvo_mode == TAHVO_MODE_HOST) 2079ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_power_off(tu); 2089ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen otg->host = NULL; 2099ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_unlock(&tu->serialize); 2109ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return 0; 2119ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 2129ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2139ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->tahvo_mode == TAHVO_MODE_HOST) { 2149ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen otg->host = NULL; 2159ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_become_host(tu); 2169ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 2179ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2189ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen otg->host = host; 2199ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2209ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_unlock(&tu->serialize); 2219ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2229ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return 0; 2239ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 2249ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2259ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic int tahvo_usb_set_peripheral(struct usb_otg *otg, 2269ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct usb_gadget *gadget) 2279ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 2289ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy); 2299ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2309ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget); 2319ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2329ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_lock(&tu->serialize); 2339ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2349ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (!gadget) { 2359ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 2369ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_power_off(tu); 2379ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.otg->gadget = NULL; 2389ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_unlock(&tu->serialize); 2399ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return 0; 2409ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 2419ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2429ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.otg->gadget = gadget; 2439ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 2449ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_become_peripheral(tu); 2459ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2469ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_unlock(&tu->serialize); 2479ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2489ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return 0; 2499ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 2509ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2519ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu) 2529ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 2539ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct tahvo_usb *tu = _tu; 2549ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2559ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_lock(&tu->serialize); 2569ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen check_vbus_state(tu); 2579ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_unlock(&tu->serialize); 2589ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2599ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return IRQ_HANDLED; 2609ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 2619ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2629ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic ssize_t otg_mode_show(struct device *device, 2639ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct device_attribute *attr, char *buf) 2649ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 2659ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct tahvo_usb *tu = dev_get_drvdata(device); 2669ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2679ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen switch (tu->tahvo_mode) { 2689ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen case TAHVO_MODE_HOST: 2699ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return sprintf(buf, "host\n"); 2709ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen case TAHVO_MODE_PERIPHERAL: 2719ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return sprintf(buf, "peripheral\n"); 2729ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 2739ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2749ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return -EINVAL; 2759ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 2769ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2779ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic ssize_t otg_mode_store(struct device *device, 2789ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct device_attribute *attr, 2799ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen const char *buf, size_t count) 2809ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 2819ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct tahvo_usb *tu = dev_get_drvdata(device); 2829ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen int r; 2839ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 2849ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_lock(&tu->serialize); 2859ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (count >= 4 && strncmp(buf, "host", 4) == 0) { 2869ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 2879ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_stop_peripheral(tu); 2889ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->tahvo_mode = TAHVO_MODE_HOST; 2899ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->phy.otg->host) { 2909ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_info(device, "HOST mode: host controller present\n"); 2919ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_become_host(tu); 2929ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } else { 2939ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_info(device, "HOST mode: no host controller, powering off\n"); 2949ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_power_off(tu); 2959ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 2969ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen r = strlen(buf); 2979ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) { 2989ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->tahvo_mode == TAHVO_MODE_HOST) 2999ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_stop_host(tu); 3009ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; 3019ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (tu->phy.otg->gadget) { 3029ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_info(device, "PERIPHERAL mode: gadget driver present\n"); 3039ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_become_peripheral(tu); 3049ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } else { 3059ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n"); 3069ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_power_off(tu); 3079ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 3089ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen r = strlen(buf); 3099ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } else { 3109ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen r = -EINVAL; 3119ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 3129ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_unlock(&tu->serialize); 3139ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3149ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return r; 3159ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 3169ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store); 3179ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3189ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic struct attribute *tahvo_attributes[] = { 3199ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen &dev_attr_vbus.attr, 3209ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen &dev_attr_otg_mode.attr, 3219ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen NULL 3229ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen}; 3239ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3249ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic struct attribute_group tahvo_attr_group = { 3259ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen .attrs = tahvo_attributes, 3269ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen}; 3279ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3289ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic int tahvo_usb_probe(struct platform_device *pdev) 3299ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 3309ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent); 3319ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct tahvo_usb *tu; 3329ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen int ret; 3339ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3349ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL); 3359ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (!tu) 3369ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return -ENOMEM; 3379ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3389ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg), 3399ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen GFP_KERNEL); 3409ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (!tu->phy.otg) 3419ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return -ENOMEM; 3429ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3439ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->pt_dev = pdev; 3449ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3459ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* Default mode */ 3469ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT 3479ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->tahvo_mode = TAHVO_MODE_HOST; 3489ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#else 3499ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; 3509ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen#endif 3519ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3529ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen mutex_init(&tu->serialize); 3539ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3549ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick"); 3559ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (!IS_ERR(tu->ick)) 3569ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen clk_enable(tu->ick); 3579ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3589ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* 3599ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen * Set initial state, so that we generate kevents only on state changes. 3609ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen */ 3619ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS; 3629ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3639ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->extcon.name = DRIVER_NAME; 3649ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->extcon.supported_cable = tahvo_cable; 3659ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->extcon.dev.parent = &pdev->dev; 3669ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3679ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen ret = extcon_dev_register(&tu->extcon); 3689ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (ret) { 3699ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_err(&pdev->dev, "could not register extcon device: %d\n", 3709ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen ret); 3719ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen goto err_disable_clk; 3729ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 3739ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3749ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* Set the initial cable state. */ 3759ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen extcon_set_cable_state(&tu->extcon, "USB-HOST", 3769ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->tahvo_mode == TAHVO_MODE_HOST); 3779ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); 3789ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3799ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* Create OTG interface */ 3809ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tahvo_usb_power_off(tu); 3819ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.dev = &pdev->dev; 3829ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.state = OTG_STATE_UNDEFINED; 3839ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.label = DRIVER_NAME; 3849ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.set_suspend = tahvo_usb_set_suspend; 3859ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3869ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.otg->phy = &tu->phy; 3879ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.otg->set_host = tahvo_usb_set_host; 3889ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral; 3899ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3909ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2); 3919ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (ret < 0) { 3929ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_err(&pdev->dev, "cannot register USB transceiver: %d\n", 3939ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen ret); 3949ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen goto err_extcon_unreg; 3959ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 3969ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3979ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_set_drvdata(&pdev->dev, tu); 3989ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 3999ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen tu->irq = platform_get_irq(pdev, 0); 4009ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, 0, 4019ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen "tahvo-vbus", tu); 4029ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (ret) { 4039ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n", 4049ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen ret); 4059ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen goto err_remove_phy; 4069ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 4079ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 4089ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen /* Attributes */ 4099ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen ret = sysfs_create_group(&pdev->dev.kobj, &tahvo_attr_group); 4109ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (ret) { 4119ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen dev_err(&pdev->dev, "cannot create sysfs group: %d\n", ret); 4129ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen goto err_free_irq; 4139ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen } 4149ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 4159ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return 0; 4169ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 4179ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenerr_free_irq: 4189ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen free_irq(tu->irq, tu); 4199ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenerr_remove_phy: 4209ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen usb_remove_phy(&tu->phy); 4219ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenerr_extcon_unreg: 4229ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen extcon_dev_unregister(&tu->extcon); 4239ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenerr_disable_clk: 4249ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (!IS_ERR(tu->ick)) 4259ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen clk_disable(tu->ick); 4269ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 4279ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return ret; 4289ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 4299ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 4309ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic int tahvo_usb_remove(struct platform_device *pdev) 4319ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen{ 4329ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen struct tahvo_usb *tu = platform_get_drvdata(pdev); 4339ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 4349ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group); 4359ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen free_irq(tu->irq, tu); 4369ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen usb_remove_phy(&tu->phy); 4379ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen extcon_dev_unregister(&tu->extcon); 4389ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen if (!IS_ERR(tu->ick)) 4399ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen clk_disable(tu->ick); 4409ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 4419ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen return 0; 4429ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen} 4439ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 4449ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenstatic struct platform_driver tahvo_usb_driver = { 4459ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen .probe = tahvo_usb_probe, 4469ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen .remove = tahvo_usb_remove, 4479ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen .driver = { 4489ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen .name = "tahvo-usb", 4499ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen .owner = THIS_MODULE, 4509ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen }, 4519ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen}; 4529ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinenmodule_platform_driver(tahvo_usb_driver); 4539ba96ae5074c9f15b357919e704ceba2bd34972dAaro Koskinen 4549ba96ae5074c9f15b357919e704ceba2bd34972dAaro KoskinenMODULE_DESCRIPTION("Tahvo USB transceiver driver"); 4559ba96ae5074c9f15b357919e704ceba2bd34972dAaro KoskinenMODULE_LICENSE("GPL"); 4569ba96ae5074c9f15b357919e704ceba2bd34972dAaro KoskinenMODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs"); 4579ba96ae5074c9f15b357919e704ceba2bd34972dAaro KoskinenMODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); 458