[go: nahoru, domu]

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