162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NOP USB transceiver for all USB transceiver which are either built-in
462306a36Sopenharmony_ci * into USB IP or which are mostly autonomous.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2009 Texas Instruments Inc
762306a36Sopenharmony_ci * Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Current status:
1062306a36Sopenharmony_ci *	This provides a "nop" transceiver for PHYs which are
1162306a36Sopenharmony_ci *	autonomous such as isp1504, isp1707, etc.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1762306a36Sopenharmony_ci#include <linux/usb/gadget.h>
1862306a36Sopenharmony_ci#include <linux/usb/otg.h>
1962306a36Sopenharmony_ci#include <linux/usb/usb_phy_generic.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/clk.h>
2262306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2362306a36Sopenharmony_ci#include <linux/of.h>
2462306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2562306a36Sopenharmony_ci#include <linux/delay.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "phy-generic.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define VBUS_IRQ_FLAGS \
3062306a36Sopenharmony_ci	(IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | \
3162306a36Sopenharmony_ci		IRQF_ONESHOT)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct platform_device *usb_phy_generic_register(void)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	return platform_device_register_simple("usb_phy_generic",
3662306a36Sopenharmony_ci			PLATFORM_DEVID_AUTO, NULL, 0);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_phy_generic_register);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_civoid usb_phy_generic_unregister(struct platform_device *pdev)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	platform_device_unregister(pdev);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_phy_generic_unregister);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int nop_set_suspend(struct usb_phy *x, int suspend)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct usb_phy_generic *nop = dev_get_drvdata(x->dev);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (!IS_ERR(nop->clk)) {
5162306a36Sopenharmony_ci		if (suspend)
5262306a36Sopenharmony_ci			clk_disable_unprepare(nop->clk);
5362306a36Sopenharmony_ci		else
5462306a36Sopenharmony_ci			clk_prepare_enable(nop->clk);
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return 0;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void nop_reset(struct usb_phy_generic *nop)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	if (!nop->gpiod_reset)
6362306a36Sopenharmony_ci		return;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	gpiod_set_value_cansleep(nop->gpiod_reset, 1);
6662306a36Sopenharmony_ci	usleep_range(10000, 20000);
6762306a36Sopenharmony_ci	gpiod_set_value_cansleep(nop->gpiod_reset, 0);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* interface to regulator framework */
7162306a36Sopenharmony_cistatic void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct regulator *vbus_draw = nop->vbus_draw;
7462306a36Sopenharmony_ci	int enabled;
7562306a36Sopenharmony_ci	int ret;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!vbus_draw)
7862306a36Sopenharmony_ci		return;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	enabled = nop->vbus_draw_enabled;
8162306a36Sopenharmony_ci	if (mA) {
8262306a36Sopenharmony_ci		regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
8362306a36Sopenharmony_ci		if (!enabled) {
8462306a36Sopenharmony_ci			ret = regulator_enable(vbus_draw);
8562306a36Sopenharmony_ci			if (ret < 0)
8662306a36Sopenharmony_ci				return;
8762306a36Sopenharmony_ci			nop->vbus_draw_enabled = 1;
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci	} else {
9062306a36Sopenharmony_ci		if (enabled) {
9162306a36Sopenharmony_ci			ret = regulator_disable(vbus_draw);
9262306a36Sopenharmony_ci			if (ret < 0)
9362306a36Sopenharmony_ci				return;
9462306a36Sopenharmony_ci			nop->vbus_draw_enabled = 0;
9562306a36Sopenharmony_ci		}
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	nop->mA = mA;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct usb_phy_generic *nop = data;
10462306a36Sopenharmony_ci	struct usb_otg *otg = nop->phy.otg;
10562306a36Sopenharmony_ci	int vbus, status;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	vbus = gpiod_get_value(nop->gpiod_vbus);
10862306a36Sopenharmony_ci	if ((vbus ^ nop->vbus) == 0)
10962306a36Sopenharmony_ci		return IRQ_HANDLED;
11062306a36Sopenharmony_ci	nop->vbus = vbus;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (vbus) {
11362306a36Sopenharmony_ci		status = USB_EVENT_VBUS;
11462306a36Sopenharmony_ci		otg->state = OTG_STATE_B_PERIPHERAL;
11562306a36Sopenharmony_ci		nop->phy.last_event = status;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		/* drawing a "unit load" is *always* OK, except for OTG */
11862306a36Sopenharmony_ci		nop_set_vbus_draw(nop, 100);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		atomic_notifier_call_chain(&nop->phy.notifier, status,
12162306a36Sopenharmony_ci					   otg->gadget);
12262306a36Sopenharmony_ci	} else {
12362306a36Sopenharmony_ci		nop_set_vbus_draw(nop, 0);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		status = USB_EVENT_NONE;
12662306a36Sopenharmony_ci		otg->state = OTG_STATE_B_IDLE;
12762306a36Sopenharmony_ci		nop->phy.last_event = status;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		atomic_notifier_call_chain(&nop->phy.notifier, status,
13062306a36Sopenharmony_ci					   otg->gadget);
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci	return IRQ_HANDLED;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciint usb_gen_phy_init(struct usb_phy *phy)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
13862306a36Sopenharmony_ci	int ret;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (!IS_ERR(nop->vcc)) {
14162306a36Sopenharmony_ci		if (regulator_enable(nop->vcc))
14262306a36Sopenharmony_ci			dev_err(phy->dev, "Failed to enable power\n");
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (!IS_ERR(nop->clk)) {
14662306a36Sopenharmony_ci		ret = clk_prepare_enable(nop->clk);
14762306a36Sopenharmony_ci		if (ret)
14862306a36Sopenharmony_ci			return ret;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	nop_reset(nop);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_gen_phy_init);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_civoid usb_gen_phy_shutdown(struct usb_phy *phy)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	gpiod_set_value_cansleep(nop->gpiod_reset, 1);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (!IS_ERR(nop->clk))
16462306a36Sopenharmony_ci		clk_disable_unprepare(nop->clk);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (!IS_ERR(nop->vcc)) {
16762306a36Sopenharmony_ci		if (regulator_disable(nop->vcc))
16862306a36Sopenharmony_ci			dev_err(phy->dev, "Failed to disable power\n");
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_gen_phy_shutdown);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	if (!otg)
17662306a36Sopenharmony_ci		return -ENODEV;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (!gadget) {
17962306a36Sopenharmony_ci		otg->gadget = NULL;
18062306a36Sopenharmony_ci		return -ENODEV;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	otg->gadget = gadget;
18462306a36Sopenharmony_ci	if (otg->state == OTG_STATE_B_PERIPHERAL)
18562306a36Sopenharmony_ci		atomic_notifier_call_chain(&otg->usb_phy->notifier,
18662306a36Sopenharmony_ci					   USB_EVENT_VBUS, otg->gadget);
18762306a36Sopenharmony_ci	else
18862306a36Sopenharmony_ci		otg->state = OTG_STATE_B_IDLE;
18962306a36Sopenharmony_ci	return 0;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	if (!otg)
19562306a36Sopenharmony_ci		return -ENODEV;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (!host) {
19862306a36Sopenharmony_ci		otg->host = NULL;
19962306a36Sopenharmony_ci		return -ENODEV;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	otg->host = host;
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ciint usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	enum usb_phy_type type = USB_PHY_TYPE_USB2;
20962306a36Sopenharmony_ci	int err = 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	u32 clk_rate = 0;
21262306a36Sopenharmony_ci	bool needs_clk = false;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (dev->of_node) {
21562306a36Sopenharmony_ci		struct device_node *node = dev->of_node;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		if (of_property_read_u32(node, "clock-frequency", &clk_rate))
21862306a36Sopenharmony_ci			clk_rate = 0;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		needs_clk = of_property_read_bool(node, "clocks");
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
22362306a36Sopenharmony_ci						   GPIOD_ASIS);
22462306a36Sopenharmony_ci	err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
22562306a36Sopenharmony_ci	if (!err) {
22662306a36Sopenharmony_ci		nop->gpiod_vbus = devm_gpiod_get_optional(dev,
22762306a36Sopenharmony_ci						 "vbus-detect",
22862306a36Sopenharmony_ci						 GPIOD_ASIS);
22962306a36Sopenharmony_ci		err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (err)
23362306a36Sopenharmony_ci		return dev_err_probe(dev, err,
23462306a36Sopenharmony_ci				     "Error requesting RESET or VBUS GPIO\n");
23562306a36Sopenharmony_ci	if (nop->gpiod_reset)
23662306a36Sopenharmony_ci		gpiod_direction_output(nop->gpiod_reset, 1);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
23962306a36Sopenharmony_ci			GFP_KERNEL);
24062306a36Sopenharmony_ci	if (!nop->phy.otg)
24162306a36Sopenharmony_ci		return -ENOMEM;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	nop->clk = devm_clk_get(dev, "main_clk");
24462306a36Sopenharmony_ci	if (IS_ERR(nop->clk)) {
24562306a36Sopenharmony_ci		dev_dbg(dev, "Can't get phy clock: %ld\n",
24662306a36Sopenharmony_ci					PTR_ERR(nop->clk));
24762306a36Sopenharmony_ci		if (needs_clk)
24862306a36Sopenharmony_ci			return PTR_ERR(nop->clk);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (!IS_ERR(nop->clk) && clk_rate) {
25262306a36Sopenharmony_ci		err = clk_set_rate(nop->clk, clk_rate);
25362306a36Sopenharmony_ci		if (err) {
25462306a36Sopenharmony_ci			dev_err(dev, "Error setting clock rate\n");
25562306a36Sopenharmony_ci			return err;
25662306a36Sopenharmony_ci		}
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	nop->vcc = devm_regulator_get_optional(dev, "vcc");
26062306a36Sopenharmony_ci	if (IS_ERR(nop->vcc) && PTR_ERR(nop->vcc) != -ENODEV)
26162306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(nop->vcc),
26262306a36Sopenharmony_ci				     "could not get vcc regulator\n");
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	nop->vbus_draw = devm_regulator_get_exclusive(dev, "vbus");
26562306a36Sopenharmony_ci	if (PTR_ERR(nop->vbus_draw) == -ENODEV)
26662306a36Sopenharmony_ci		nop->vbus_draw = NULL;
26762306a36Sopenharmony_ci	if (IS_ERR(nop->vbus_draw))
26862306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(nop->vbus_draw),
26962306a36Sopenharmony_ci				     "could not get vbus regulator\n");
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	nop->vbus_draw = devm_regulator_get_exclusive(dev, "vbus");
27262306a36Sopenharmony_ci	if (PTR_ERR(nop->vbus_draw) == -ENODEV)
27362306a36Sopenharmony_ci		nop->vbus_draw = NULL;
27462306a36Sopenharmony_ci	if (IS_ERR(nop->vbus_draw))
27562306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(nop->vbus_draw),
27662306a36Sopenharmony_ci				     "could not get vbus regulator\n");
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	nop->dev		= dev;
27962306a36Sopenharmony_ci	nop->phy.dev		= nop->dev;
28062306a36Sopenharmony_ci	nop->phy.label		= "nop-xceiv";
28162306a36Sopenharmony_ci	nop->phy.set_suspend	= nop_set_suspend;
28262306a36Sopenharmony_ci	nop->phy.type		= type;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	nop->phy.otg->state		= OTG_STATE_UNDEFINED;
28562306a36Sopenharmony_ci	nop->phy.otg->usb_phy		= &nop->phy;
28662306a36Sopenharmony_ci	nop->phy.otg->set_host		= nop_set_host;
28762306a36Sopenharmony_ci	nop->phy.otg->set_peripheral	= nop_set_peripheral;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return 0;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int usb_phy_generic_probe(struct platform_device *pdev)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
29662306a36Sopenharmony_ci	struct device_node *dn = dev->of_node;
29762306a36Sopenharmony_ci	struct usb_phy_generic	*nop;
29862306a36Sopenharmony_ci	int err;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL);
30162306a36Sopenharmony_ci	if (!nop)
30262306a36Sopenharmony_ci		return -ENOMEM;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	err = usb_phy_gen_create_phy(dev, nop);
30562306a36Sopenharmony_ci	if (err)
30662306a36Sopenharmony_ci		return err;
30762306a36Sopenharmony_ci	if (nop->gpiod_vbus) {
30862306a36Sopenharmony_ci		err = devm_request_threaded_irq(&pdev->dev,
30962306a36Sopenharmony_ci						gpiod_to_irq(nop->gpiod_vbus),
31062306a36Sopenharmony_ci						NULL, nop_gpio_vbus_thread,
31162306a36Sopenharmony_ci						VBUS_IRQ_FLAGS, "vbus_detect",
31262306a36Sopenharmony_ci						nop);
31362306a36Sopenharmony_ci		if (err) {
31462306a36Sopenharmony_ci			dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
31562306a36Sopenharmony_ci				gpiod_to_irq(nop->gpiod_vbus), err);
31662306a36Sopenharmony_ci			return err;
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci		nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ?
31962306a36Sopenharmony_ci			OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	nop->phy.init		= usb_gen_phy_init;
32362306a36Sopenharmony_ci	nop->phy.shutdown	= usb_gen_phy_shutdown;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	err = usb_add_phy_dev(&nop->phy);
32662306a36Sopenharmony_ci	if (err) {
32762306a36Sopenharmony_ci		dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
32862306a36Sopenharmony_ci			err);
32962306a36Sopenharmony_ci		return err;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	platform_set_drvdata(pdev, nop);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	device_set_wakeup_capable(&pdev->dev,
33562306a36Sopenharmony_ci				  of_property_read_bool(dn, "wakeup-source"));
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic void usb_phy_generic_remove(struct platform_device *pdev)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct usb_phy_generic *nop = platform_get_drvdata(pdev);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	usb_remove_phy(&nop->phy);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic const struct of_device_id nop_xceiv_dt_ids[] = {
34862306a36Sopenharmony_ci	{ .compatible = "usb-nop-xceiv" },
34962306a36Sopenharmony_ci	{ }
35062306a36Sopenharmony_ci};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic struct platform_driver usb_phy_generic_driver = {
35562306a36Sopenharmony_ci	.probe		= usb_phy_generic_probe,
35662306a36Sopenharmony_ci	.remove_new	= usb_phy_generic_remove,
35762306a36Sopenharmony_ci	.driver		= {
35862306a36Sopenharmony_ci		.name	= "usb_phy_generic",
35962306a36Sopenharmony_ci		.of_match_table = nop_xceiv_dt_ids,
36062306a36Sopenharmony_ci	},
36162306a36Sopenharmony_ci};
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int __init usb_phy_generic_init(void)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	return platform_driver_register(&usb_phy_generic_driver);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_cisubsys_initcall(usb_phy_generic_init);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic void __exit usb_phy_generic_exit(void)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	platform_driver_unregister(&usb_phy_generic_driver);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_cimodule_exit(usb_phy_generic_exit);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ciMODULE_ALIAS("platform:usb_phy_generic");
37662306a36Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Inc");
37762306a36Sopenharmony_ciMODULE_DESCRIPTION("NOP USB Transceiver driver");
37862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
379