From 03e4b585ac947e2d422bedf03179bbfec3aca3cf Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 14 Nov 2022 12:51:59 +0100 Subject: [PATCH 09/29] 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: Linus Walleij Link: https://lore.kernel.org/r/20221114115201.302887-2-linus.walleij@linaro.org Signed-off-by: Greg Kroah-Hartman --- --- a/drivers/usb/fotg210/fotg210-udc.c +++ b/drivers/usb/fotg210/fotg210-udc.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "fotg210.h" #include "fotg210-udc.h" @@ -1022,10 +1024,18 @@ static int fotg210_udc_start(struct usb_ { struct fotg210_udc *fotg210 = gadget_to_fotg210(g); u32 value; + int ret; /* hook up the 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 */ value = ioread32(fotg210->reg + FOTG210_DMCR); value |= DMCR_GLINT_EN; @@ -1067,6 +1077,9 @@ static int fotg210_udc_stop(struct usb_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); @@ -1082,12 +1095,50 @@ static const struct usb_gadget_ops fotg2 .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); @@ -1127,6 +1178,22 @@ int fotg210_udc_probe(struct platform_de 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) @@ -1200,6 +1267,9 @@ int fotg210_udc_probe(struct platform_de 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; @@ -1209,6 +1279,8 @@ int fotg210_udc_probe(struct platform_de 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: --- a/drivers/usb/fotg210/fotg210-udc.h +++ b/drivers/usb/fotg210/fotg210-udc.h @@ -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;