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

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.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d40eaada
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/usb/phy.h>
#include "fotg210.h" #include "fotg210.h"
#include "fotg210-udc.h" #include "fotg210-udc.h"
...@@ -1008,11 +1010,19 @@ static int fotg210_udc_start(struct usb_gadget *g, ...@@ -1008,11 +1010,19 @@ static int fotg210_udc_start(struct usb_gadget *g,
{ {
struct fotg210_udc *fotg210 = gadget_to_fotg210(g); struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
u32 value; u32 value;
int ret;
/* hook up the driver */ /* hook up the driver */
driver->driver.bus = NULL; driver->driver.bus = NULL;
fotg210->driver = driver; 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 */ /* enable device global interrupt */
value = ioread32(fotg210->reg + FOTG210_DMCR); value = ioread32(fotg210->reg + FOTG210_DMCR);
value |= DMCR_GLINT_EN; value |= DMCR_GLINT_EN;
...@@ -1054,6 +1064,9 @@ static int fotg210_udc_stop(struct usb_gadget *g) ...@@ -1054,6 +1064,9 @@ static int fotg210_udc_stop(struct usb_gadget *g)
struct fotg210_udc *fotg210 = gadget_to_fotg210(g); struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
unsigned long flags; unsigned long flags;
if (!IS_ERR_OR_NULL(fotg210->phy))
return otg_set_peripheral(fotg210->phy->otg, NULL);
spin_lock_irqsave(&fotg210->lock, flags); spin_lock_irqsave(&fotg210->lock, flags);
fotg210_init(fotg210); fotg210_init(fotg210);
...@@ -1069,12 +1082,50 @@ static const struct usb_gadget_ops fotg210_gadget_ops = { ...@@ -1069,12 +1082,50 @@ static const struct usb_gadget_ops fotg210_gadget_ops = {
.udc_stop = fotg210_udc_stop, .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) int fotg210_udc_remove(struct platform_device *pdev)
{ {
struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); struct fotg210_udc *fotg210 = platform_get_drvdata(pdev);
int i; int i;
usb_del_gadget_udc(&fotg210->gadget); 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); iounmap(fotg210->reg);
free_irq(platform_get_irq(pdev, 0), fotg210); free_irq(platform_get_irq(pdev, 0), fotg210);
...@@ -1114,6 +1165,22 @@ int fotg210_udc_probe(struct platform_device *pdev) ...@@ -1114,6 +1165,22 @@ int fotg210_udc_probe(struct platform_device *pdev)
if (fotg210 == NULL) if (fotg210 == NULL)
goto err; 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++) { for (i = 0; i < FOTG210_MAX_NUM_EP; i++) {
_ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL);
if (_ep[i] == NULL) if (_ep[i] == NULL)
...@@ -1185,6 +1252,9 @@ int fotg210_udc_probe(struct platform_device *pdev) ...@@ -1185,6 +1252,9 @@ int fotg210_udc_probe(struct platform_device *pdev)
goto err_req; 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); ret = usb_add_gadget_udc(dev, &fotg210->gadget);
if (ret) if (ret)
goto err_add_udc; goto err_add_udc;
...@@ -1194,6 +1264,8 @@ int fotg210_udc_probe(struct platform_device *pdev) ...@@ -1194,6 +1264,8 @@ int fotg210_udc_probe(struct platform_device *pdev)
return 0; return 0;
err_add_udc: err_add_udc:
if (!IS_ERR_OR_NULL(fotg210->phy))
usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier);
free_irq(ires->start, fotg210); free_irq(ires->start, fotg210);
err_req: err_req:
......
...@@ -234,6 +234,8 @@ struct fotg210_udc { ...@@ -234,6 +234,8 @@ struct fotg210_udc {
unsigned long irq_trigger; unsigned long irq_trigger;
struct device *dev;
struct usb_phy *phy;
struct usb_gadget gadget; struct usb_gadget gadget;
struct usb_gadget_driver *driver; struct usb_gadget_driver *driver;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment