Commit 5f217ccd authored by Linus Walleij's avatar Linus Walleij Committed by Greg Kroah-Hartman
Browse files

fotg210-udc: Support optional external PHY



This adds support for an optional external PHY to the FOTG210
UDC driver.

Tested with the GPIO VBUS PHY driver on the Gemini SoC.

Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20221114115201.302887-2-linus.walleij@linaro.org


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d40eaada
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
#include <linux/platform_device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/usb/phy.h>

#include "fotg210.h"
#include "fotg210-udc.h"
@@ -1008,11 +1010,19 @@ static int fotg210_udc_start(struct usb_gadget *g,
{
	struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
	u32 value;
	int ret;

	/* hook up the driver */
	driver->driver.bus = NULL;
	fotg210->driver = driver;

	if (!IS_ERR_OR_NULL(fotg210->phy)) {
		ret = otg_set_peripheral(fotg210->phy->otg,
					 &fotg210->gadget);
		if (ret)
			dev_err(fotg210->dev, "can't bind to phy\n");
	}

	/* enable device global interrupt */
	value = ioread32(fotg210->reg + FOTG210_DMCR);
	value |= DMCR_GLINT_EN;
@@ -1054,6 +1064,9 @@ static int fotg210_udc_stop(struct usb_gadget *g)
	struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
	unsigned long	flags;

	if (!IS_ERR_OR_NULL(fotg210->phy))
		return otg_set_peripheral(fotg210->phy->otg, NULL);

	spin_lock_irqsave(&fotg210->lock, flags);

	fotg210_init(fotg210);
@@ -1069,12 +1082,50 @@ static const struct usb_gadget_ops fotg210_gadget_ops = {
	.udc_stop		= fotg210_udc_stop,
};

/**
 * fotg210_phy_event - Called by phy upon VBus event
 * @nb: notifier block
 * @action: phy action, is vbus connect or disconnect
 * @data: the usb_gadget structure in fotg210
 *
 * Called by the USB Phy when a cable connect or disconnect is sensed.
 *
 * Returns NOTIFY_OK or NOTIFY_DONE
 */
static int fotg210_phy_event(struct notifier_block *nb, unsigned long action,
			     void *data)
{
	struct usb_gadget *gadget = data;

	if (!gadget)
		return NOTIFY_DONE;

	switch (action) {
	case USB_EVENT_VBUS:
		usb_gadget_vbus_connect(gadget);
		return NOTIFY_OK;
	case USB_EVENT_NONE:
		usb_gadget_vbus_disconnect(gadget);
		return NOTIFY_OK;
	default:
		return NOTIFY_DONE;
	}
}

static struct notifier_block fotg210_phy_notifier = {
	.notifier_call = fotg210_phy_event,
};

int fotg210_udc_remove(struct platform_device *pdev)
{
	struct fotg210_udc *fotg210 = platform_get_drvdata(pdev);
	int i;

	usb_del_gadget_udc(&fotg210->gadget);
	if (!IS_ERR_OR_NULL(fotg210->phy)) {
		usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier);
		usb_put_phy(fotg210->phy);
	}
	iounmap(fotg210->reg);
	free_irq(platform_get_irq(pdev, 0), fotg210);

@@ -1114,6 +1165,22 @@ int fotg210_udc_probe(struct platform_device *pdev)
	if (fotg210 == NULL)
		goto err;

	fotg210->dev = dev;

	fotg210->phy = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0);
	if (IS_ERR(fotg210->phy)) {
		ret = PTR_ERR(fotg210->phy);
		if (ret == -EPROBE_DEFER)
			goto err;
		dev_info(dev, "no PHY found\n");
		fotg210->phy = NULL;
	} else {
		ret = usb_phy_init(fotg210->phy);
		if (ret)
			goto err;
		dev_info(dev, "found and initialized PHY\n");
	}

	for (i = 0; i < FOTG210_MAX_NUM_EP; i++) {
		_ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL);
		if (_ep[i] == NULL)
@@ -1185,6 +1252,9 @@ int fotg210_udc_probe(struct platform_device *pdev)
		goto err_req;
	}

	if (!IS_ERR_OR_NULL(fotg210->phy))
		usb_register_notifier(fotg210->phy, &fotg210_phy_notifier);

	ret = usb_add_gadget_udc(dev, &fotg210->gadget);
	if (ret)
		goto err_add_udc;
@@ -1194,6 +1264,8 @@ int fotg210_udc_probe(struct platform_device *pdev)
	return 0;

err_add_udc:
	if (!IS_ERR_OR_NULL(fotg210->phy))
		usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier);
	free_irq(ires->start, fotg210);

err_req:
+2 −0
Original line number Diff line number Diff line
@@ -234,6 +234,8 @@ struct fotg210_udc {

	unsigned long		irq_trigger;

	struct device			*dev;
	struct usb_phy			*phy;
	struct usb_gadget		gadget;
	struct usb_gadget_driver	*driver;