xref: /openbmc/linux/drivers/usb/phy/phy-generic.c (revision 56c5145b)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
23fa4d734SSebastian Andrzej Siewior /*
33fa4d734SSebastian Andrzej Siewior  * NOP USB transceiver for all USB transceiver which are either built-in
43fa4d734SSebastian Andrzej Siewior  * into USB IP or which are mostly autonomous.
53fa4d734SSebastian Andrzej Siewior  *
63fa4d734SSebastian Andrzej Siewior  * Copyright (C) 2009 Texas Instruments Inc
73fa4d734SSebastian Andrzej Siewior  * Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
83fa4d734SSebastian Andrzej Siewior  *
93fa4d734SSebastian Andrzej Siewior  * Current status:
103fa4d734SSebastian Andrzej Siewior  *	This provides a "nop" transceiver for PHYs which are
113fa4d734SSebastian Andrzej Siewior  *	autonomous such as isp1504, isp1707, etc.
123fa4d734SSebastian Andrzej Siewior  */
133fa4d734SSebastian Andrzej Siewior 
143fa4d734SSebastian Andrzej Siewior #include <linux/module.h>
153fa4d734SSebastian Andrzej Siewior #include <linux/platform_device.h>
163fa4d734SSebastian Andrzej Siewior #include <linux/dma-mapping.h>
177acc9973SRobert Jarzmik #include <linux/usb/gadget.h>
183fa4d734SSebastian Andrzej Siewior #include <linux/usb/otg.h>
19d7078df6SFelipe Balbi #include <linux/usb/usb_phy_generic.h>
203fa4d734SSebastian Andrzej Siewior #include <linux/slab.h>
213fa4d734SSebastian Andrzej Siewior #include <linux/clk.h>
223fa4d734SSebastian Andrzej Siewior #include <linux/regulator/consumer.h>
233fa4d734SSebastian Andrzej Siewior #include <linux/of.h>
24b267ddf6SLinus Walleij #include <linux/gpio/consumer.h>
25bd27fa44SRoger Quadros #include <linux/delay.h>
263fa4d734SSebastian Andrzej Siewior 
2753b6fc28SSebastian Andrzej Siewior #include "phy-generic.h"
283fa4d734SSebastian Andrzej Siewior 
297acc9973SRobert Jarzmik #define VBUS_IRQ_FLAGS \
300f4ff5f1SRobert Jarzmik 	(IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | \
310f4ff5f1SRobert Jarzmik 		IRQF_ONESHOT)
327acc9973SRobert Jarzmik 
usb_phy_generic_register(void)332f36ff69SFelipe Balbi struct platform_device *usb_phy_generic_register(void)
343fa4d734SSebastian Andrzej Siewior {
352f36ff69SFelipe Balbi 	return platform_device_register_simple("usb_phy_generic",
362f36ff69SFelipe Balbi 			PLATFORM_DEVID_AUTO, NULL, 0);
373fa4d734SSebastian Andrzej Siewior }
384525beebSFelipe Balbi EXPORT_SYMBOL_GPL(usb_phy_generic_register);
393fa4d734SSebastian Andrzej Siewior 
usb_phy_generic_unregister(struct platform_device * pdev)402f36ff69SFelipe Balbi void usb_phy_generic_unregister(struct platform_device *pdev)
413fa4d734SSebastian Andrzej Siewior {
422f36ff69SFelipe Balbi 	platform_device_unregister(pdev);
433fa4d734SSebastian Andrzej Siewior }
444525beebSFelipe Balbi EXPORT_SYMBOL_GPL(usb_phy_generic_unregister);
453fa4d734SSebastian Andrzej Siewior 
nop_set_suspend(struct usb_phy * x,int suspend)463fa4d734SSebastian Andrzej Siewior static int nop_set_suspend(struct usb_phy *x, int suspend)
473fa4d734SSebastian Andrzej Siewior {
48a0fe0415SPeter Chen 	struct usb_phy_generic *nop = dev_get_drvdata(x->dev);
49a0fe0415SPeter Chen 
50a0fe0415SPeter Chen 	if (!IS_ERR(nop->clk)) {
51a0fe0415SPeter Chen 		if (suspend)
52a0fe0415SPeter Chen 			clk_disable_unprepare(nop->clk);
53a0fe0415SPeter Chen 		else
54a0fe0415SPeter Chen 			clk_prepare_enable(nop->clk);
55a0fe0415SPeter Chen 	}
56a0fe0415SPeter Chen 
573fa4d734SSebastian Andrzej Siewior 	return 0;
583fa4d734SSebastian Andrzej Siewior }
593fa4d734SSebastian Andrzej Siewior 
nop_reset(struct usb_phy_generic * nop)60168bdb88SFabio Estevam static void nop_reset(struct usb_phy_generic *nop)
61bd27fa44SRoger Quadros {
6274379991SFabio Estevam 	if (!nop->gpiod_reset)
6374379991SFabio Estevam 		return;
64bd27fa44SRoger Quadros 
655864470aSMike Looijmans 	gpiod_set_value_cansleep(nop->gpiod_reset, 1);
66bd27fa44SRoger Quadros 	usleep_range(10000, 20000);
675864470aSMike Looijmans 	gpiod_set_value_cansleep(nop->gpiod_reset, 0);
68bd27fa44SRoger Quadros }
69bd27fa44SRoger Quadros 
707acc9973SRobert Jarzmik /* interface to regulator framework */
nop_set_vbus_draw(struct usb_phy_generic * nop,unsigned mA)717acc9973SRobert Jarzmik static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA)
727acc9973SRobert Jarzmik {
737acc9973SRobert Jarzmik 	struct regulator *vbus_draw = nop->vbus_draw;
747acc9973SRobert Jarzmik 	int enabled;
757acc9973SRobert Jarzmik 	int ret;
767acc9973SRobert Jarzmik 
777acc9973SRobert Jarzmik 	if (!vbus_draw)
787acc9973SRobert Jarzmik 		return;
797acc9973SRobert Jarzmik 
807acc9973SRobert Jarzmik 	enabled = nop->vbus_draw_enabled;
817acc9973SRobert Jarzmik 	if (mA) {
827acc9973SRobert Jarzmik 		regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
837acc9973SRobert Jarzmik 		if (!enabled) {
847acc9973SRobert Jarzmik 			ret = regulator_enable(vbus_draw);
857acc9973SRobert Jarzmik 			if (ret < 0)
867acc9973SRobert Jarzmik 				return;
877acc9973SRobert Jarzmik 			nop->vbus_draw_enabled = 1;
887acc9973SRobert Jarzmik 		}
897acc9973SRobert Jarzmik 	} else {
907acc9973SRobert Jarzmik 		if (enabled) {
917acc9973SRobert Jarzmik 			ret = regulator_disable(vbus_draw);
927acc9973SRobert Jarzmik 			if (ret < 0)
937acc9973SRobert Jarzmik 				return;
947acc9973SRobert Jarzmik 			nop->vbus_draw_enabled = 0;
957acc9973SRobert Jarzmik 		}
967acc9973SRobert Jarzmik 	}
977acc9973SRobert Jarzmik 	nop->mA = mA;
987acc9973SRobert Jarzmik }
997acc9973SRobert Jarzmik 
1007acc9973SRobert Jarzmik 
nop_gpio_vbus_thread(int irq,void * data)1017acc9973SRobert Jarzmik static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
1027acc9973SRobert Jarzmik {
1037acc9973SRobert Jarzmik 	struct usb_phy_generic *nop = data;
1047acc9973SRobert Jarzmik 	struct usb_otg *otg = nop->phy.otg;
1057acc9973SRobert Jarzmik 	int vbus, status;
1067acc9973SRobert Jarzmik 
1077acc9973SRobert Jarzmik 	vbus = gpiod_get_value(nop->gpiod_vbus);
1087acc9973SRobert Jarzmik 	if ((vbus ^ nop->vbus) == 0)
1097acc9973SRobert Jarzmik 		return IRQ_HANDLED;
1107acc9973SRobert Jarzmik 	nop->vbus = vbus;
1117acc9973SRobert Jarzmik 
1127acc9973SRobert Jarzmik 	if (vbus) {
1137acc9973SRobert Jarzmik 		status = USB_EVENT_VBUS;
1147acc9973SRobert Jarzmik 		otg->state = OTG_STATE_B_PERIPHERAL;
1157acc9973SRobert Jarzmik 		nop->phy.last_event = status;
1167acc9973SRobert Jarzmik 
1177acc9973SRobert Jarzmik 		/* drawing a "unit load" is *always* OK, except for OTG */
1187acc9973SRobert Jarzmik 		nop_set_vbus_draw(nop, 100);
1197acc9973SRobert Jarzmik 
1207acc9973SRobert Jarzmik 		atomic_notifier_call_chain(&nop->phy.notifier, status,
1217acc9973SRobert Jarzmik 					   otg->gadget);
1227acc9973SRobert Jarzmik 	} else {
1237acc9973SRobert Jarzmik 		nop_set_vbus_draw(nop, 0);
1247acc9973SRobert Jarzmik 
1257acc9973SRobert Jarzmik 		status = USB_EVENT_NONE;
1267acc9973SRobert Jarzmik 		otg->state = OTG_STATE_B_IDLE;
1277acc9973SRobert Jarzmik 		nop->phy.last_event = status;
1287acc9973SRobert Jarzmik 
1297acc9973SRobert Jarzmik 		atomic_notifier_call_chain(&nop->phy.notifier, status,
1307acc9973SRobert Jarzmik 					   otg->gadget);
1317acc9973SRobert Jarzmik 	}
1327acc9973SRobert Jarzmik 	return IRQ_HANDLED;
1337acc9973SRobert Jarzmik }
1347acc9973SRobert Jarzmik 
usb_gen_phy_init(struct usb_phy * phy)13553b6fc28SSebastian Andrzej Siewior int usb_gen_phy_init(struct usb_phy *phy)
1363fa4d734SSebastian Andrzej Siewior {
1374525beebSFelipe Balbi 	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
1387c113f7dSFabio Estevam 	int ret;
1393fa4d734SSebastian Andrzej Siewior 
1403fa4d734SSebastian Andrzej Siewior 	if (!IS_ERR(nop->vcc)) {
1413fa4d734SSebastian Andrzej Siewior 		if (regulator_enable(nop->vcc))
1423fa4d734SSebastian Andrzej Siewior 			dev_err(phy->dev, "Failed to enable power\n");
1433fa4d734SSebastian Andrzej Siewior 	}
1443fa4d734SSebastian Andrzej Siewior 
1457c113f7dSFabio Estevam 	if (!IS_ERR(nop->clk)) {
1467c113f7dSFabio Estevam 		ret = clk_prepare_enable(nop->clk);
1477c113f7dSFabio Estevam 		if (ret)
1487c113f7dSFabio Estevam 			return ret;
1497c113f7dSFabio Estevam 	}
1503fa4d734SSebastian Andrzej Siewior 
151168bdb88SFabio Estevam 	nop_reset(nop);
1523fa4d734SSebastian Andrzej Siewior 
1533fa4d734SSebastian Andrzej Siewior 	return 0;
1543fa4d734SSebastian Andrzej Siewior }
15553b6fc28SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_gen_phy_init);
1563fa4d734SSebastian Andrzej Siewior 
usb_gen_phy_shutdown(struct usb_phy * phy)15753b6fc28SSebastian Andrzej Siewior void usb_gen_phy_shutdown(struct usb_phy *phy)
1583fa4d734SSebastian Andrzej Siewior {
1594525beebSFelipe Balbi 	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
1603fa4d734SSebastian Andrzej Siewior 
1615864470aSMike Looijmans 	gpiod_set_value_cansleep(nop->gpiod_reset, 1);
1623fa4d734SSebastian Andrzej Siewior 
1633fa4d734SSebastian Andrzej Siewior 	if (!IS_ERR(nop->clk))
1644d175f34SMark Brown 		clk_disable_unprepare(nop->clk);
1653fa4d734SSebastian Andrzej Siewior 
1663fa4d734SSebastian Andrzej Siewior 	if (!IS_ERR(nop->vcc)) {
1673fa4d734SSebastian Andrzej Siewior 		if (regulator_disable(nop->vcc))
1683fa4d734SSebastian Andrzej Siewior 			dev_err(phy->dev, "Failed to disable power\n");
1693fa4d734SSebastian Andrzej Siewior 	}
1703fa4d734SSebastian Andrzej Siewior }
17153b6fc28SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_gen_phy_shutdown);
1723fa4d734SSebastian Andrzej Siewior 
nop_set_peripheral(struct usb_otg * otg,struct usb_gadget * gadget)1733fa4d734SSebastian Andrzej Siewior static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
1743fa4d734SSebastian Andrzej Siewior {
1753fa4d734SSebastian Andrzej Siewior 	if (!otg)
1763fa4d734SSebastian Andrzej Siewior 		return -ENODEV;
1773fa4d734SSebastian Andrzej Siewior 
1783fa4d734SSebastian Andrzej Siewior 	if (!gadget) {
1793fa4d734SSebastian Andrzej Siewior 		otg->gadget = NULL;
1803fa4d734SSebastian Andrzej Siewior 		return -ENODEV;
1813fa4d734SSebastian Andrzej Siewior 	}
1823fa4d734SSebastian Andrzej Siewior 
1833fa4d734SSebastian Andrzej Siewior 	otg->gadget = gadget;
1842eafe93bSMaarten ter Huurne 	if (otg->state == OTG_STATE_B_PERIPHERAL)
18577e012acSRobert Jarzmik 		atomic_notifier_call_chain(&otg->usb_phy->notifier,
18677e012acSRobert Jarzmik 					   USB_EVENT_VBUS, otg->gadget);
1872eafe93bSMaarten ter Huurne 	else
188e47d9254SAntoine Tenart 		otg->state = OTG_STATE_B_IDLE;
1893fa4d734SSebastian Andrzej Siewior 	return 0;
1903fa4d734SSebastian Andrzej Siewior }
1913fa4d734SSebastian Andrzej Siewior 
nop_set_host(struct usb_otg * otg,struct usb_bus * host)1923fa4d734SSebastian Andrzej Siewior static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
1933fa4d734SSebastian Andrzej Siewior {
1943fa4d734SSebastian Andrzej Siewior 	if (!otg)
1953fa4d734SSebastian Andrzej Siewior 		return -ENODEV;
1963fa4d734SSebastian Andrzej Siewior 
1973fa4d734SSebastian Andrzej Siewior 	if (!host) {
1983fa4d734SSebastian Andrzej Siewior 		otg->host = NULL;
1993fa4d734SSebastian Andrzej Siewior 		return -ENODEV;
2003fa4d734SSebastian Andrzej Siewior 	}
2013fa4d734SSebastian Andrzej Siewior 
2023fa4d734SSebastian Andrzej Siewior 	otg->host = host;
2033fa4d734SSebastian Andrzej Siewior 	return 0;
2043fa4d734SSebastian Andrzej Siewior }
2053fa4d734SSebastian Andrzej Siewior 
usb_phy_gen_create_phy(struct device * dev,struct usb_phy_generic * nop)206b267ddf6SLinus Walleij int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
20753b6fc28SSebastian Andrzej Siewior {
208af9f51c5SFelipe Balbi 	enum usb_phy_type type = USB_PHY_TYPE_USB2;
209e9f2cefbSRobert Jarzmik 	int err = 0;
21053b6fc28SSebastian Andrzej Siewior 
211af9f51c5SFelipe Balbi 	u32 clk_rate = 0;
21232c6fefbSSascha Hauer 	bool needs_clk = false;
213af9f51c5SFelipe Balbi 
214af9f51c5SFelipe Balbi 	if (dev->of_node) {
215af9f51c5SFelipe Balbi 		struct device_node *node = dev->of_node;
216af9f51c5SFelipe Balbi 
217af9f51c5SFelipe Balbi 		if (of_property_read_u32(node, "clock-frequency", &clk_rate))
218af9f51c5SFelipe Balbi 			clk_rate = 0;
219af9f51c5SFelipe Balbi 
220f9c5d1dbSLucas Stach 		needs_clk = of_property_read_bool(node, "clocks");
221b267ddf6SLinus Walleij 	}
22288167fc0SUwe Kleine-König 	nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
22388167fc0SUwe Kleine-König 						   GPIOD_ASIS);
2249eb07977SRobert Jarzmik 	err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
2257acc9973SRobert Jarzmik 	if (!err) {
226da89dba1SPaul Zimmerman 		nop->gpiod_vbus = devm_gpiod_get_optional(dev,
22788167fc0SUwe Kleine-König 						 "vbus-detect",
22888167fc0SUwe Kleine-König 						 GPIOD_ASIS);
2299eb07977SRobert Jarzmik 		err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
2307acc9973SRobert Jarzmik 	}
231e9f2cefbSRobert Jarzmik 
232e0b27d38SYang Yingliang 	if (err)
233e0b27d38SYang Yingliang 		return dev_err_probe(dev, err,
234e0b27d38SYang Yingliang 				     "Error requesting RESET or VBUS GPIO\n");
2359eb07977SRobert Jarzmik 	if (nop->gpiod_reset)
2369eb07977SRobert Jarzmik 		gpiod_direction_output(nop->gpiod_reset, 1);
237af9f51c5SFelipe Balbi 
23853b6fc28SSebastian Andrzej Siewior 	nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
23953b6fc28SSebastian Andrzej Siewior 			GFP_KERNEL);
24053b6fc28SSebastian Andrzej Siewior 	if (!nop->phy.otg)
24153b6fc28SSebastian Andrzej Siewior 		return -ENOMEM;
24253b6fc28SSebastian Andrzej Siewior 
24353b6fc28SSebastian Andrzej Siewior 	nop->clk = devm_clk_get(dev, "main_clk");
24453b6fc28SSebastian Andrzej Siewior 	if (IS_ERR(nop->clk)) {
24553b6fc28SSebastian Andrzej Siewior 		dev_dbg(dev, "Can't get phy clock: %ld\n",
24653b6fc28SSebastian Andrzej Siewior 					PTR_ERR(nop->clk));
247f9c5d1dbSLucas Stach 		if (needs_clk)
248f9c5d1dbSLucas Stach 			return PTR_ERR(nop->clk);
24953b6fc28SSebastian Andrzej Siewior 	}
25053b6fc28SSebastian Andrzej Siewior 
25153b6fc28SSebastian Andrzej Siewior 	if (!IS_ERR(nop->clk) && clk_rate) {
25253b6fc28SSebastian Andrzej Siewior 		err = clk_set_rate(nop->clk, clk_rate);
25353b6fc28SSebastian Andrzej Siewior 		if (err) {
25453b6fc28SSebastian Andrzej Siewior 			dev_err(dev, "Error setting clock rate\n");
25553b6fc28SSebastian Andrzej Siewior 			return err;
25653b6fc28SSebastian Andrzej Siewior 		}
25753b6fc28SSebastian Andrzej Siewior 	}
25853b6fc28SSebastian Andrzej Siewior 
25932c6fefbSSascha Hauer 	nop->vcc = devm_regulator_get_optional(dev, "vcc");
26032c6fefbSSascha Hauer 	if (IS_ERR(nop->vcc) && PTR_ERR(nop->vcc) != -ENODEV)
26132c6fefbSSascha Hauer 		return dev_err_probe(dev, PTR_ERR(nop->vcc),
26232c6fefbSSascha Hauer 				     "could not get vcc regulator\n");
26353b6fc28SSebastian Andrzej Siewior 
26403e607cbSSean Anderson 	nop->vbus_draw = devm_regulator_get_exclusive(dev, "vbus");
26503e607cbSSean Anderson 	if (PTR_ERR(nop->vbus_draw) == -ENODEV)
26603e607cbSSean Anderson 		nop->vbus_draw = NULL;
26703e607cbSSean Anderson 	if (IS_ERR(nop->vbus_draw))
26803e607cbSSean Anderson 		return dev_err_probe(dev, PTR_ERR(nop->vbus_draw),
26903e607cbSSean Anderson 				     "could not get vbus regulator\n");
27003e607cbSSean Anderson 
27153b6fc28SSebastian Andrzej Siewior 	nop->dev		= dev;
27253b6fc28SSebastian Andrzej Siewior 	nop->phy.dev		= nop->dev;
27353b6fc28SSebastian Andrzej Siewior 	nop->phy.label		= "nop-xceiv";
27453b6fc28SSebastian Andrzej Siewior 	nop->phy.set_suspend	= nop_set_suspend;
27553b6fc28SSebastian Andrzej Siewior 	nop->phy.type		= type;
27653b6fc28SSebastian Andrzej Siewior 
277e47d9254SAntoine Tenart 	nop->phy.otg->state		= OTG_STATE_UNDEFINED;
27819c1eac2SAntoine Tenart 	nop->phy.otg->usb_phy		= &nop->phy;
27953b6fc28SSebastian Andrzej Siewior 	nop->phy.otg->set_host		= nop_set_host;
28053b6fc28SSebastian Andrzej Siewior 	nop->phy.otg->set_peripheral	= nop_set_peripheral;
28153b6fc28SSebastian Andrzej Siewior 
28253b6fc28SSebastian Andrzej Siewior 	return 0;
28353b6fc28SSebastian Andrzej Siewior }
28453b6fc28SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
28553b6fc28SSebastian Andrzej Siewior 
usb_phy_generic_probe(struct platform_device * pdev)2864525beebSFelipe Balbi static int usb_phy_generic_probe(struct platform_device *pdev)
2873fa4d734SSebastian Andrzej Siewior {
2883fa4d734SSebastian Andrzej Siewior 	struct device *dev = &pdev->dev;
2894567d1a9SLi Jun 	struct device_node *dn = dev->of_node;
2904525beebSFelipe Balbi 	struct usb_phy_generic	*nop;
2913fa4d734SSebastian Andrzej Siewior 	int err;
2923fa4d734SSebastian Andrzej Siewior 
29353b6fc28SSebastian Andrzej Siewior 	nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL);
29453b6fc28SSebastian Andrzej Siewior 	if (!nop)
29553b6fc28SSebastian Andrzej Siewior 		return -ENOMEM;
2963fa4d734SSebastian Andrzej Siewior 
297b267ddf6SLinus Walleij 	err = usb_phy_gen_create_phy(dev, nop);
29853b6fc28SSebastian Andrzej Siewior 	if (err)
2993fa4d734SSebastian Andrzej Siewior 		return err;
3007acc9973SRobert Jarzmik 	if (nop->gpiod_vbus) {
3017acc9973SRobert Jarzmik 		err = devm_request_threaded_irq(&pdev->dev,
3027acc9973SRobert Jarzmik 						gpiod_to_irq(nop->gpiod_vbus),
3037acc9973SRobert Jarzmik 						NULL, nop_gpio_vbus_thread,
3047acc9973SRobert Jarzmik 						VBUS_IRQ_FLAGS, "vbus_detect",
3057acc9973SRobert Jarzmik 						nop);
3067acc9973SRobert Jarzmik 		if (err) {
3077acc9973SRobert Jarzmik 			dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
3087acc9973SRobert Jarzmik 				gpiod_to_irq(nop->gpiod_vbus), err);
3097acc9973SRobert Jarzmik 			return err;
3107acc9973SRobert Jarzmik 		}
3119835a6efSRobert Jarzmik 		nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ?
3129835a6efSRobert Jarzmik 			OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE;
3137acc9973SRobert Jarzmik 	}
3143fa4d734SSebastian Andrzej Siewior 
31553b6fc28SSebastian Andrzej Siewior 	nop->phy.init		= usb_gen_phy_init;
31653b6fc28SSebastian Andrzej Siewior 	nop->phy.shutdown	= usb_gen_phy_shutdown;
3173fa4d734SSebastian Andrzej Siewior 
3183fa4d734SSebastian Andrzej Siewior 	err = usb_add_phy_dev(&nop->phy);
3193fa4d734SSebastian Andrzej Siewior 	if (err) {
3203fa4d734SSebastian Andrzej Siewior 		dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
3213fa4d734SSebastian Andrzej Siewior 			err);
3224d175f34SMark Brown 		return err;
3233fa4d734SSebastian Andrzej Siewior 	}
3243fa4d734SSebastian Andrzej Siewior 
3253fa4d734SSebastian Andrzej Siewior 	platform_set_drvdata(pdev, nop);
3263fa4d734SSebastian Andrzej Siewior 
3274567d1a9SLi Jun 	device_set_wakeup_capable(&pdev->dev,
3284567d1a9SLi Jun 				  of_property_read_bool(dn, "wakeup-source"));
3294567d1a9SLi Jun 
3303fa4d734SSebastian Andrzej Siewior 	return 0;
3313fa4d734SSebastian Andrzej Siewior }
3323fa4d734SSebastian Andrzej Siewior 
usb_phy_generic_remove(struct platform_device * pdev)333*a8095f9cSUwe Kleine-König static void usb_phy_generic_remove(struct platform_device *pdev)
3343fa4d734SSebastian Andrzej Siewior {
3354525beebSFelipe Balbi 	struct usb_phy_generic *nop = platform_get_drvdata(pdev);
3363fa4d734SSebastian Andrzej Siewior 
3373fa4d734SSebastian Andrzej Siewior 	usb_remove_phy(&nop->phy);
3383fa4d734SSebastian Andrzej Siewior }
3393fa4d734SSebastian Andrzej Siewior 
3403fa4d734SSebastian Andrzej Siewior static const struct of_device_id nop_xceiv_dt_ids[] = {
3413fa4d734SSebastian Andrzej Siewior 	{ .compatible = "usb-nop-xceiv" },
3423fa4d734SSebastian Andrzej Siewior 	{ }
3433fa4d734SSebastian Andrzej Siewior };
3443fa4d734SSebastian Andrzej Siewior 
3453fa4d734SSebastian Andrzej Siewior MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids);
3463fa4d734SSebastian Andrzej Siewior 
3474525beebSFelipe Balbi static struct platform_driver usb_phy_generic_driver = {
3484525beebSFelipe Balbi 	.probe		= usb_phy_generic_probe,
349*a8095f9cSUwe Kleine-König 	.remove_new	= usb_phy_generic_remove,
3503fa4d734SSebastian Andrzej Siewior 	.driver		= {
3514525beebSFelipe Balbi 		.name	= "usb_phy_generic",
3523fa4d734SSebastian Andrzej Siewior 		.of_match_table = nop_xceiv_dt_ids,
3533fa4d734SSebastian Andrzej Siewior 	},
3543fa4d734SSebastian Andrzej Siewior };
3553fa4d734SSebastian Andrzej Siewior 
usb_phy_generic_init(void)3564525beebSFelipe Balbi static int __init usb_phy_generic_init(void)
3573fa4d734SSebastian Andrzej Siewior {
3584525beebSFelipe Balbi 	return platform_driver_register(&usb_phy_generic_driver);
3593fa4d734SSebastian Andrzej Siewior }
3604525beebSFelipe Balbi subsys_initcall(usb_phy_generic_init);
3613fa4d734SSebastian Andrzej Siewior 
usb_phy_generic_exit(void)3624525beebSFelipe Balbi static void __exit usb_phy_generic_exit(void)
3633fa4d734SSebastian Andrzej Siewior {
3644525beebSFelipe Balbi 	platform_driver_unregister(&usb_phy_generic_driver);
3653fa4d734SSebastian Andrzej Siewior }
3664525beebSFelipe Balbi module_exit(usb_phy_generic_exit);
3673fa4d734SSebastian Andrzej Siewior 
3684525beebSFelipe Balbi MODULE_ALIAS("platform:usb_phy_generic");
3693fa4d734SSebastian Andrzej Siewior MODULE_AUTHOR("Texas Instruments Inc");
3703fa4d734SSebastian Andrzej Siewior MODULE_DESCRIPTION("NOP USB Transceiver driver");
3713fa4d734SSebastian Andrzej Siewior MODULE_LICENSE("GPL");
372