xref: /openbmc/linux/drivers/usb/phy/phy-tahvo.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
29ba96ae5SAaro Koskinen /*
39ba96ae5SAaro Koskinen  * Tahvo USB transceiver driver
49ba96ae5SAaro Koskinen  *
59ba96ae5SAaro Koskinen  * Copyright (C) 2005-2006 Nokia Corporation
69ba96ae5SAaro Koskinen  *
79ba96ae5SAaro Koskinen  * Parts copied from isp1301_omap.c.
89ba96ae5SAaro Koskinen  * Copyright (C) 2004 Texas Instruments
99ba96ae5SAaro Koskinen  * Copyright (C) 2004 David Brownell
109ba96ae5SAaro Koskinen  *
119ba96ae5SAaro Koskinen  * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs.
129ba96ae5SAaro Koskinen  * Modified for Retu/Tahvo MFD by Aaro Koskinen.
139ba96ae5SAaro Koskinen  */
149ba96ae5SAaro Koskinen 
159ba96ae5SAaro Koskinen #include <linux/io.h>
169ba96ae5SAaro Koskinen #include <linux/clk.h>
179ba96ae5SAaro Koskinen #include <linux/usb.h>
18176aa360SChanwoo Choi #include <linux/extcon-provider.h>
199ba96ae5SAaro Koskinen #include <linux/kernel.h>
209ba96ae5SAaro Koskinen #include <linux/module.h>
219ba96ae5SAaro Koskinen #include <linux/usb/otg.h>
229ba96ae5SAaro Koskinen #include <linux/mfd/retu.h>
239ba96ae5SAaro Koskinen #include <linux/usb/gadget.h>
249ba96ae5SAaro Koskinen #include <linux/platform_device.h>
259ba96ae5SAaro Koskinen 
269ba96ae5SAaro Koskinen #define DRIVER_NAME     "tahvo-usb"
279ba96ae5SAaro Koskinen 
289ba96ae5SAaro Koskinen #define TAHVO_REG_IDSR	0x02
299ba96ae5SAaro Koskinen #define TAHVO_REG_USBR	0x06
309ba96ae5SAaro Koskinen 
319ba96ae5SAaro Koskinen #define USBR_SLAVE_CONTROL	(1 << 8)
329ba96ae5SAaro Koskinen #define USBR_VPPVIO_SW		(1 << 7)
339ba96ae5SAaro Koskinen #define USBR_SPEED		(1 << 6)
349ba96ae5SAaro Koskinen #define USBR_REGOUT		(1 << 5)
359ba96ae5SAaro Koskinen #define USBR_MASTER_SW2		(1 << 4)
369ba96ae5SAaro Koskinen #define USBR_MASTER_SW1		(1 << 3)
379ba96ae5SAaro Koskinen #define USBR_SLAVE_SW		(1 << 2)
389ba96ae5SAaro Koskinen #define USBR_NSUSPEND		(1 << 1)
399ba96ae5SAaro Koskinen #define USBR_SEMODE		(1 << 0)
409ba96ae5SAaro Koskinen 
419ba96ae5SAaro Koskinen #define TAHVO_MODE_HOST		0
429ba96ae5SAaro Koskinen #define TAHVO_MODE_PERIPHERAL	1
439ba96ae5SAaro Koskinen 
449ba96ae5SAaro Koskinen struct tahvo_usb {
459ba96ae5SAaro Koskinen 	struct platform_device	*pt_dev;
469ba96ae5SAaro Koskinen 	struct usb_phy		phy;
479ba96ae5SAaro Koskinen 	int			vbus_state;
489ba96ae5SAaro Koskinen 	struct mutex		serialize;
499ba96ae5SAaro Koskinen 	struct clk		*ick;
509ba96ae5SAaro Koskinen 	int			irq;
519ba96ae5SAaro Koskinen 	int			tahvo_mode;
52860d2686SChanwoo Choi 	struct extcon_dev	*extcon;
539ba96ae5SAaro Koskinen };
549ba96ae5SAaro Koskinen 
5573b6ecdbSChanwoo Choi static const unsigned int tahvo_cable[] = {
562a9de9c0SChanwoo Choi 	EXTCON_USB,
572a9de9c0SChanwoo Choi 	EXTCON_USB_HOST,
582a9de9c0SChanwoo Choi 
592a9de9c0SChanwoo Choi 	EXTCON_NONE,
609ba96ae5SAaro Koskinen };
619ba96ae5SAaro Koskinen 
vbus_show(struct device * device,struct device_attribute * attr,char * buf)627f26ee4bSGreg Kroah-Hartman static ssize_t vbus_show(struct device *device,
639ba96ae5SAaro Koskinen 			       struct device_attribute *attr, char *buf)
649ba96ae5SAaro Koskinen {
659ba96ae5SAaro Koskinen 	struct tahvo_usb *tu = dev_get_drvdata(device);
669ba96ae5SAaro Koskinen 	return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off");
679ba96ae5SAaro Koskinen }
687f26ee4bSGreg Kroah-Hartman static DEVICE_ATTR_RO(vbus);
699ba96ae5SAaro Koskinen 
check_vbus_state(struct tahvo_usb * tu)709ba96ae5SAaro Koskinen static void check_vbus_state(struct tahvo_usb *tu)
719ba96ae5SAaro Koskinen {
729ba96ae5SAaro Koskinen 	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
739ba96ae5SAaro Koskinen 	int reg, prev_state;
749ba96ae5SAaro Koskinen 
759ba96ae5SAaro Koskinen 	reg = retu_read(rdev, TAHVO_REG_IDSR);
769ba96ae5SAaro Koskinen 	if (reg & TAHVO_STAT_VBUS) {
77e47d9254SAntoine Tenart 		switch (tu->phy.otg->state) {
789ba96ae5SAaro Koskinen 		case OTG_STATE_B_IDLE:
799ba96ae5SAaro Koskinen 			/* Enable the gadget driver */
809ba96ae5SAaro Koskinen 			if (tu->phy.otg->gadget)
819ba96ae5SAaro Koskinen 				usb_gadget_vbus_connect(tu->phy.otg->gadget);
82e47d9254SAntoine Tenart 			tu->phy.otg->state = OTG_STATE_B_PERIPHERAL;
83b20f3f9eSKiran Raparthy 			usb_phy_set_event(&tu->phy, USB_EVENT_ENUMERATED);
849ba96ae5SAaro Koskinen 			break;
859ba96ae5SAaro Koskinen 		case OTG_STATE_A_IDLE:
869ba96ae5SAaro Koskinen 			/*
879ba96ae5SAaro Koskinen 			 * Session is now valid assuming the USB hub is driving
889ba96ae5SAaro Koskinen 			 * Vbus.
899ba96ae5SAaro Koskinen 			 */
90e47d9254SAntoine Tenart 			tu->phy.otg->state = OTG_STATE_A_HOST;
919ba96ae5SAaro Koskinen 			break;
929ba96ae5SAaro Koskinen 		default:
939ba96ae5SAaro Koskinen 			break;
949ba96ae5SAaro Koskinen 		}
959ba96ae5SAaro Koskinen 		dev_info(&tu->pt_dev->dev, "USB cable connected\n");
969ba96ae5SAaro Koskinen 	} else {
97e47d9254SAntoine Tenart 		switch (tu->phy.otg->state) {
989ba96ae5SAaro Koskinen 		case OTG_STATE_B_PERIPHERAL:
999ba96ae5SAaro Koskinen 			if (tu->phy.otg->gadget)
1009ba96ae5SAaro Koskinen 				usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
101e47d9254SAntoine Tenart 			tu->phy.otg->state = OTG_STATE_B_IDLE;
102b20f3f9eSKiran Raparthy 			usb_phy_set_event(&tu->phy, USB_EVENT_NONE);
1039ba96ae5SAaro Koskinen 			break;
1049ba96ae5SAaro Koskinen 		case OTG_STATE_A_HOST:
105e47d9254SAntoine Tenart 			tu->phy.otg->state = OTG_STATE_A_IDLE;
1069ba96ae5SAaro Koskinen 			break;
1079ba96ae5SAaro Koskinen 		default:
1089ba96ae5SAaro Koskinen 			break;
1099ba96ae5SAaro Koskinen 		}
1109ba96ae5SAaro Koskinen 		dev_info(&tu->pt_dev->dev, "USB cable disconnected\n");
1119ba96ae5SAaro Koskinen 	}
1129ba96ae5SAaro Koskinen 
1139ba96ae5SAaro Koskinen 	prev_state = tu->vbus_state;
1149ba96ae5SAaro Koskinen 	tu->vbus_state = reg & TAHVO_STAT_VBUS;
1159ba96ae5SAaro Koskinen 	if (prev_state != tu->vbus_state) {
116746c9085SChanwoo Choi 		extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
1179ba96ae5SAaro Koskinen 		sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state");
1189ba96ae5SAaro Koskinen 	}
1199ba96ae5SAaro Koskinen }
1209ba96ae5SAaro Koskinen 
tahvo_usb_become_host(struct tahvo_usb * tu)1219ba96ae5SAaro Koskinen static void tahvo_usb_become_host(struct tahvo_usb *tu)
1229ba96ae5SAaro Koskinen {
1239ba96ae5SAaro Koskinen 	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
1249ba96ae5SAaro Koskinen 
125746c9085SChanwoo Choi 	extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, true);
1269ba96ae5SAaro Koskinen 
1279ba96ae5SAaro Koskinen 	/* Power up the transceiver in USB host mode */
1289ba96ae5SAaro Koskinen 	retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
1299ba96ae5SAaro Koskinen 		   USBR_MASTER_SW2 | USBR_MASTER_SW1);
130e47d9254SAntoine Tenart 	tu->phy.otg->state = OTG_STATE_A_IDLE;
1319ba96ae5SAaro Koskinen 
1329ba96ae5SAaro Koskinen 	check_vbus_state(tu);
1339ba96ae5SAaro Koskinen }
1349ba96ae5SAaro Koskinen 
tahvo_usb_stop_host(struct tahvo_usb * tu)1359ba96ae5SAaro Koskinen static void tahvo_usb_stop_host(struct tahvo_usb *tu)
1369ba96ae5SAaro Koskinen {
137e47d9254SAntoine Tenart 	tu->phy.otg->state = OTG_STATE_A_IDLE;
1389ba96ae5SAaro Koskinen }
1399ba96ae5SAaro Koskinen 
tahvo_usb_become_peripheral(struct tahvo_usb * tu)1409ba96ae5SAaro Koskinen static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
1419ba96ae5SAaro Koskinen {
1429ba96ae5SAaro Koskinen 	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
1439ba96ae5SAaro Koskinen 
144746c9085SChanwoo Choi 	extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, false);
1459ba96ae5SAaro Koskinen 
1469ba96ae5SAaro Koskinen 	/* Power up transceiver and set it in USB peripheral mode */
1479ba96ae5SAaro Koskinen 	retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT |
1489ba96ae5SAaro Koskinen 		   USBR_NSUSPEND | USBR_SLAVE_SW);
149e47d9254SAntoine Tenart 	tu->phy.otg->state = OTG_STATE_B_IDLE;
1509ba96ae5SAaro Koskinen 
1519ba96ae5SAaro Koskinen 	check_vbus_state(tu);
1529ba96ae5SAaro Koskinen }
1539ba96ae5SAaro Koskinen 
tahvo_usb_stop_peripheral(struct tahvo_usb * tu)1549ba96ae5SAaro Koskinen static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu)
1559ba96ae5SAaro Koskinen {
1569ba96ae5SAaro Koskinen 	if (tu->phy.otg->gadget)
1579ba96ae5SAaro Koskinen 		usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
158e47d9254SAntoine Tenart 	tu->phy.otg->state = OTG_STATE_B_IDLE;
1599ba96ae5SAaro Koskinen }
1609ba96ae5SAaro Koskinen 
tahvo_usb_power_off(struct tahvo_usb * tu)1619ba96ae5SAaro Koskinen static void tahvo_usb_power_off(struct tahvo_usb *tu)
1629ba96ae5SAaro Koskinen {
1639ba96ae5SAaro Koskinen 	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
1649ba96ae5SAaro Koskinen 
1659ba96ae5SAaro Koskinen 	/* Disable gadget controller if any */
1669ba96ae5SAaro Koskinen 	if (tu->phy.otg->gadget)
1679ba96ae5SAaro Koskinen 		usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
1689ba96ae5SAaro Koskinen 
1699ba96ae5SAaro Koskinen 	/* Power off transceiver */
1709ba96ae5SAaro Koskinen 	retu_write(rdev, TAHVO_REG_USBR, 0);
171e47d9254SAntoine Tenart 	tu->phy.otg->state = OTG_STATE_UNDEFINED;
1729ba96ae5SAaro Koskinen }
1739ba96ae5SAaro Koskinen 
tahvo_usb_set_suspend(struct usb_phy * dev,int suspend)1749ba96ae5SAaro Koskinen static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend)
1759ba96ae5SAaro Koskinen {
1769ba96ae5SAaro Koskinen 	struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy);
1779ba96ae5SAaro Koskinen 	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
1789ba96ae5SAaro Koskinen 	u16 w;
1799ba96ae5SAaro Koskinen 
1809ba96ae5SAaro Koskinen 	dev_dbg(&tu->pt_dev->dev, "%s\n", __func__);
1819ba96ae5SAaro Koskinen 
1829ba96ae5SAaro Koskinen 	w = retu_read(rdev, TAHVO_REG_USBR);
1839ba96ae5SAaro Koskinen 	if (suspend)
1849ba96ae5SAaro Koskinen 		w &= ~USBR_NSUSPEND;
1859ba96ae5SAaro Koskinen 	else
1869ba96ae5SAaro Koskinen 		w |= USBR_NSUSPEND;
1879ba96ae5SAaro Koskinen 	retu_write(rdev, TAHVO_REG_USBR, w);
1889ba96ae5SAaro Koskinen 
1899ba96ae5SAaro Koskinen 	return 0;
1909ba96ae5SAaro Koskinen }
1919ba96ae5SAaro Koskinen 
tahvo_usb_set_host(struct usb_otg * otg,struct usb_bus * host)1929ba96ae5SAaro Koskinen static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
1939ba96ae5SAaro Koskinen {
19419c1eac2SAntoine Tenart 	struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
19519c1eac2SAntoine Tenart 					    phy);
1969ba96ae5SAaro Koskinen 
1979ba96ae5SAaro Koskinen 	mutex_lock(&tu->serialize);
1989ba96ae5SAaro Koskinen 
1999ba96ae5SAaro Koskinen 	if (host == NULL) {
2009ba96ae5SAaro Koskinen 		if (tu->tahvo_mode == TAHVO_MODE_HOST)
2019ba96ae5SAaro Koskinen 			tahvo_usb_power_off(tu);
2029ba96ae5SAaro Koskinen 		otg->host = NULL;
2039ba96ae5SAaro Koskinen 		mutex_unlock(&tu->serialize);
2049ba96ae5SAaro Koskinen 		return 0;
2059ba96ae5SAaro Koskinen 	}
2069ba96ae5SAaro Koskinen 
2079ba96ae5SAaro Koskinen 	if (tu->tahvo_mode == TAHVO_MODE_HOST) {
2089ba96ae5SAaro Koskinen 		otg->host = NULL;
2099ba96ae5SAaro Koskinen 		tahvo_usb_become_host(tu);
2109ba96ae5SAaro Koskinen 	}
2119ba96ae5SAaro Koskinen 
2129ba96ae5SAaro Koskinen 	otg->host = host;
2139ba96ae5SAaro Koskinen 
2149ba96ae5SAaro Koskinen 	mutex_unlock(&tu->serialize);
2159ba96ae5SAaro Koskinen 
2169ba96ae5SAaro Koskinen 	return 0;
2179ba96ae5SAaro Koskinen }
2189ba96ae5SAaro Koskinen 
tahvo_usb_set_peripheral(struct usb_otg * otg,struct usb_gadget * gadget)2199ba96ae5SAaro Koskinen static int tahvo_usb_set_peripheral(struct usb_otg *otg,
2209ba96ae5SAaro Koskinen 				    struct usb_gadget *gadget)
2219ba96ae5SAaro Koskinen {
22219c1eac2SAntoine Tenart 	struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
22319c1eac2SAntoine Tenart 					    phy);
2249ba96ae5SAaro Koskinen 
2259ba96ae5SAaro Koskinen 	mutex_lock(&tu->serialize);
2269ba96ae5SAaro Koskinen 
2279ba96ae5SAaro Koskinen 	if (!gadget) {
2289ba96ae5SAaro Koskinen 		if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
2299ba96ae5SAaro Koskinen 			tahvo_usb_power_off(tu);
2309ba96ae5SAaro Koskinen 		tu->phy.otg->gadget = NULL;
2319ba96ae5SAaro Koskinen 		mutex_unlock(&tu->serialize);
2329ba96ae5SAaro Koskinen 		return 0;
2339ba96ae5SAaro Koskinen 	}
2349ba96ae5SAaro Koskinen 
2359ba96ae5SAaro Koskinen 	tu->phy.otg->gadget = gadget;
2369ba96ae5SAaro Koskinen 	if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
2379ba96ae5SAaro Koskinen 		tahvo_usb_become_peripheral(tu);
2389ba96ae5SAaro Koskinen 
2399ba96ae5SAaro Koskinen 	mutex_unlock(&tu->serialize);
2409ba96ae5SAaro Koskinen 
2419ba96ae5SAaro Koskinen 	return 0;
2429ba96ae5SAaro Koskinen }
2439ba96ae5SAaro Koskinen 
tahvo_usb_vbus_interrupt(int irq,void * _tu)2449ba96ae5SAaro Koskinen static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu)
2459ba96ae5SAaro Koskinen {
2469ba96ae5SAaro Koskinen 	struct tahvo_usb *tu = _tu;
2479ba96ae5SAaro Koskinen 
2489ba96ae5SAaro Koskinen 	mutex_lock(&tu->serialize);
2499ba96ae5SAaro Koskinen 	check_vbus_state(tu);
2509ba96ae5SAaro Koskinen 	mutex_unlock(&tu->serialize);
2519ba96ae5SAaro Koskinen 
2529ba96ae5SAaro Koskinen 	return IRQ_HANDLED;
2539ba96ae5SAaro Koskinen }
2549ba96ae5SAaro Koskinen 
otg_mode_show(struct device * device,struct device_attribute * attr,char * buf)2559ba96ae5SAaro Koskinen static ssize_t otg_mode_show(struct device *device,
2569ba96ae5SAaro Koskinen 			     struct device_attribute *attr, char *buf)
2579ba96ae5SAaro Koskinen {
2589ba96ae5SAaro Koskinen 	struct tahvo_usb *tu = dev_get_drvdata(device);
2599ba96ae5SAaro Koskinen 
2609ba96ae5SAaro Koskinen 	switch (tu->tahvo_mode) {
2619ba96ae5SAaro Koskinen 	case TAHVO_MODE_HOST:
2629ba96ae5SAaro Koskinen 		return sprintf(buf, "host\n");
2639ba96ae5SAaro Koskinen 	case TAHVO_MODE_PERIPHERAL:
2649ba96ae5SAaro Koskinen 		return sprintf(buf, "peripheral\n");
2659ba96ae5SAaro Koskinen 	}
2669ba96ae5SAaro Koskinen 
2679ba96ae5SAaro Koskinen 	return -EINVAL;
2689ba96ae5SAaro Koskinen }
2699ba96ae5SAaro Koskinen 
otg_mode_store(struct device * device,struct device_attribute * attr,const char * buf,size_t count)2709ba96ae5SAaro Koskinen static ssize_t otg_mode_store(struct device *device,
2719ba96ae5SAaro Koskinen 			      struct device_attribute *attr,
2729ba96ae5SAaro Koskinen 			      const char *buf, size_t count)
2739ba96ae5SAaro Koskinen {
2749ba96ae5SAaro Koskinen 	struct tahvo_usb *tu = dev_get_drvdata(device);
2759ba96ae5SAaro Koskinen 	int r;
2769ba96ae5SAaro Koskinen 
2779ba96ae5SAaro Koskinen 	mutex_lock(&tu->serialize);
2789ba96ae5SAaro Koskinen 	if (count >= 4 && strncmp(buf, "host", 4) == 0) {
2799ba96ae5SAaro Koskinen 		if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
2809ba96ae5SAaro Koskinen 			tahvo_usb_stop_peripheral(tu);
2819ba96ae5SAaro Koskinen 		tu->tahvo_mode = TAHVO_MODE_HOST;
2829ba96ae5SAaro Koskinen 		if (tu->phy.otg->host) {
2839ba96ae5SAaro Koskinen 			dev_info(device, "HOST mode: host controller present\n");
2849ba96ae5SAaro Koskinen 			tahvo_usb_become_host(tu);
2859ba96ae5SAaro Koskinen 		} else {
2869ba96ae5SAaro Koskinen 			dev_info(device, "HOST mode: no host controller, powering off\n");
2879ba96ae5SAaro Koskinen 			tahvo_usb_power_off(tu);
2889ba96ae5SAaro Koskinen 		}
2899ba96ae5SAaro Koskinen 		r = strlen(buf);
2909ba96ae5SAaro Koskinen 	} else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) {
2919ba96ae5SAaro Koskinen 		if (tu->tahvo_mode == TAHVO_MODE_HOST)
2929ba96ae5SAaro Koskinen 			tahvo_usb_stop_host(tu);
2939ba96ae5SAaro Koskinen 		tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
2949ba96ae5SAaro Koskinen 		if (tu->phy.otg->gadget) {
2959ba96ae5SAaro Koskinen 			dev_info(device, "PERIPHERAL mode: gadget driver present\n");
2969ba96ae5SAaro Koskinen 			tahvo_usb_become_peripheral(tu);
2979ba96ae5SAaro Koskinen 		} else {
2989ba96ae5SAaro Koskinen 			dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n");
2999ba96ae5SAaro Koskinen 			tahvo_usb_power_off(tu);
3009ba96ae5SAaro Koskinen 		}
3019ba96ae5SAaro Koskinen 		r = strlen(buf);
3029ba96ae5SAaro Koskinen 	} else {
3039ba96ae5SAaro Koskinen 		r = -EINVAL;
3049ba96ae5SAaro Koskinen 	}
3059ba96ae5SAaro Koskinen 	mutex_unlock(&tu->serialize);
3069ba96ae5SAaro Koskinen 
3079ba96ae5SAaro Koskinen 	return r;
3089ba96ae5SAaro Koskinen }
309ed5bd7a4SGreg Kroah-Hartman static DEVICE_ATTR_RW(otg_mode);
3109ba96ae5SAaro Koskinen 
311f4d09e9fSGreg Kroah-Hartman static struct attribute *tahvo_attrs[] = {
3129ba96ae5SAaro Koskinen 	&dev_attr_vbus.attr,
3139ba96ae5SAaro Koskinen 	&dev_attr_otg_mode.attr,
3149ba96ae5SAaro Koskinen 	NULL
3159ba96ae5SAaro Koskinen };
316f4d09e9fSGreg Kroah-Hartman ATTRIBUTE_GROUPS(tahvo);
3179ba96ae5SAaro Koskinen 
tahvo_usb_probe(struct platform_device * pdev)3189ba96ae5SAaro Koskinen static int tahvo_usb_probe(struct platform_device *pdev)
3199ba96ae5SAaro Koskinen {
3209ba96ae5SAaro Koskinen 	struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
3219ba96ae5SAaro Koskinen 	struct tahvo_usb *tu;
3229ba96ae5SAaro Koskinen 	int ret;
3239ba96ae5SAaro Koskinen 
3249ba96ae5SAaro Koskinen 	tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL);
3259ba96ae5SAaro Koskinen 	if (!tu)
3269ba96ae5SAaro Koskinen 		return -ENOMEM;
3279ba96ae5SAaro Koskinen 
3289ba96ae5SAaro Koskinen 	tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg),
3299ba96ae5SAaro Koskinen 				   GFP_KERNEL);
3309ba96ae5SAaro Koskinen 	if (!tu->phy.otg)
3319ba96ae5SAaro Koskinen 		return -ENOMEM;
3329ba96ae5SAaro Koskinen 
3339ba96ae5SAaro Koskinen 	tu->pt_dev = pdev;
3349ba96ae5SAaro Koskinen 
3359ba96ae5SAaro Koskinen 	/* Default mode */
3369ba96ae5SAaro Koskinen #ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT
3379ba96ae5SAaro Koskinen 	tu->tahvo_mode = TAHVO_MODE_HOST;
3389ba96ae5SAaro Koskinen #else
3399ba96ae5SAaro Koskinen 	tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
3409ba96ae5SAaro Koskinen #endif
3419ba96ae5SAaro Koskinen 
3429ba96ae5SAaro Koskinen 	mutex_init(&tu->serialize);
3439ba96ae5SAaro Koskinen 
3449ba96ae5SAaro Koskinen 	tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick");
3459ba96ae5SAaro Koskinen 	if (!IS_ERR(tu->ick))
3469ba96ae5SAaro Koskinen 		clk_enable(tu->ick);
3479ba96ae5SAaro Koskinen 
3489ba96ae5SAaro Koskinen 	/*
3499ba96ae5SAaro Koskinen 	 * Set initial state, so that we generate kevents only on state changes.
3509ba96ae5SAaro Koskinen 	 */
3519ba96ae5SAaro Koskinen 	tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS;
3529ba96ae5SAaro Koskinen 
353860d2686SChanwoo Choi 	tu->extcon = devm_extcon_dev_allocate(&pdev->dev, tahvo_cable);
354860d2686SChanwoo Choi 	if (IS_ERR(tu->extcon)) {
355860d2686SChanwoo Choi 		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
356ce035409SAlexey Khoroshilov 		ret = PTR_ERR(tu->extcon);
357ce035409SAlexey Khoroshilov 		goto err_disable_clk;
358860d2686SChanwoo Choi 	}
3599ba96ae5SAaro Koskinen 
360860d2686SChanwoo Choi 	ret = devm_extcon_dev_register(&pdev->dev, tu->extcon);
3619ba96ae5SAaro Koskinen 	if (ret) {
3629ba96ae5SAaro Koskinen 		dev_err(&pdev->dev, "could not register extcon device: %d\n",
3639ba96ae5SAaro Koskinen 			ret);
3649ba96ae5SAaro Koskinen 		goto err_disable_clk;
3659ba96ae5SAaro Koskinen 	}
3669ba96ae5SAaro Koskinen 
3679ba96ae5SAaro Koskinen 	/* Set the initial cable state. */
368746c9085SChanwoo Choi 	extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST,
3699ba96ae5SAaro Koskinen 			       tu->tahvo_mode == TAHVO_MODE_HOST);
370746c9085SChanwoo Choi 	extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
3719ba96ae5SAaro Koskinen 
3729ba96ae5SAaro Koskinen 	/* Create OTG interface */
3739ba96ae5SAaro Koskinen 	tahvo_usb_power_off(tu);
3749ba96ae5SAaro Koskinen 	tu->phy.dev = &pdev->dev;
375e47d9254SAntoine Tenart 	tu->phy.otg->state = OTG_STATE_UNDEFINED;
3769ba96ae5SAaro Koskinen 	tu->phy.label = DRIVER_NAME;
3779ba96ae5SAaro Koskinen 	tu->phy.set_suspend = tahvo_usb_set_suspend;
3789ba96ae5SAaro Koskinen 
37919c1eac2SAntoine Tenart 	tu->phy.otg->usb_phy = &tu->phy;
3809ba96ae5SAaro Koskinen 	tu->phy.otg->set_host = tahvo_usb_set_host;
3819ba96ae5SAaro Koskinen 	tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral;
3829ba96ae5SAaro Koskinen 
3839ba96ae5SAaro Koskinen 	ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2);
3849ba96ae5SAaro Koskinen 	if (ret < 0) {
3859ba96ae5SAaro Koskinen 		dev_err(&pdev->dev, "cannot register USB transceiver: %d\n",
3869ba96ae5SAaro Koskinen 			ret);
387860d2686SChanwoo Choi 		goto err_disable_clk;
3889ba96ae5SAaro Koskinen 	}
3899ba96ae5SAaro Koskinen 
3909ba96ae5SAaro Koskinen 	dev_set_drvdata(&pdev->dev, tu);
3919ba96ae5SAaro Koskinen 
3920d45a137SSergey Shtylyov 	tu->irq = ret = platform_get_irq(pdev, 0);
3930d45a137SSergey Shtylyov 	if (ret < 0)
3940d45a137SSergey Shtylyov 		goto err_remove_phy;
395a81df9eeSFabio Estevam 	ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt,
396a81df9eeSFabio Estevam 				   IRQF_ONESHOT,
3979ba96ae5SAaro Koskinen 				   "tahvo-vbus", tu);
3989ba96ae5SAaro Koskinen 	if (ret) {
3999ba96ae5SAaro Koskinen 		dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n",
4009ba96ae5SAaro Koskinen 			ret);
4019ba96ae5SAaro Koskinen 		goto err_remove_phy;
4029ba96ae5SAaro Koskinen 	}
4039ba96ae5SAaro Koskinen 
4049ba96ae5SAaro Koskinen 	return 0;
4059ba96ae5SAaro Koskinen 
4069ba96ae5SAaro Koskinen err_remove_phy:
4079ba96ae5SAaro Koskinen 	usb_remove_phy(&tu->phy);
4089ba96ae5SAaro Koskinen err_disable_clk:
4099ba96ae5SAaro Koskinen 	if (!IS_ERR(tu->ick))
4109ba96ae5SAaro Koskinen 		clk_disable(tu->ick);
4119ba96ae5SAaro Koskinen 
4129ba96ae5SAaro Koskinen 	return ret;
4139ba96ae5SAaro Koskinen }
4149ba96ae5SAaro Koskinen 
tahvo_usb_remove(struct platform_device * pdev)415*e5c1b349SUwe Kleine-König static void tahvo_usb_remove(struct platform_device *pdev)
4169ba96ae5SAaro Koskinen {
4179ba96ae5SAaro Koskinen 	struct tahvo_usb *tu = platform_get_drvdata(pdev);
4189ba96ae5SAaro Koskinen 
4199ba96ae5SAaro Koskinen 	free_irq(tu->irq, tu);
4209ba96ae5SAaro Koskinen 	usb_remove_phy(&tu->phy);
4219ba96ae5SAaro Koskinen 	if (!IS_ERR(tu->ick))
4229ba96ae5SAaro Koskinen 		clk_disable(tu->ick);
4239ba96ae5SAaro Koskinen }
4249ba96ae5SAaro Koskinen 
4259ba96ae5SAaro Koskinen static struct platform_driver tahvo_usb_driver = {
4269ba96ae5SAaro Koskinen 	.probe		= tahvo_usb_probe,
427*e5c1b349SUwe Kleine-König 	.remove_new	= tahvo_usb_remove,
4289ba96ae5SAaro Koskinen 	.driver		= {
4299ba96ae5SAaro Koskinen 		.name	= "tahvo-usb",
430f4d09e9fSGreg Kroah-Hartman 		.dev_groups = tahvo_groups,
4319ba96ae5SAaro Koskinen 	},
4329ba96ae5SAaro Koskinen };
4339ba96ae5SAaro Koskinen module_platform_driver(tahvo_usb_driver);
4349ba96ae5SAaro Koskinen 
4359ba96ae5SAaro Koskinen MODULE_DESCRIPTION("Tahvo USB transceiver driver");
4369ba96ae5SAaro Koskinen MODULE_LICENSE("GPL");
4379ba96ae5SAaro Koskinen MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs");
4389ba96ae5SAaro Koskinen MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
439