gemini: Add kernel v6.1 patches
[openwrt/openwrt.git] / target / linux / gemini / patches-6.1 / 0009-fotg210-udc-Support-optional-external-PHY.patch
diff --git a/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch b/target/linux/gemini/patches-6.1/0009-fotg210-udc-Support-optional-external-PHY.patch
new file mode 100644 (file)
index 0000000..498875c
--- /dev/null
@@ -0,0 +1,158 @@
+From 03e4b585ac947e2d422bedf03179bbfec3aca3cf Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+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 <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221114115201.302887-2-linus.walleij@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+--- a/drivers/usb/fotg210/fotg210-udc.c
++++ b/drivers/usb/fotg210/fotg210-udc.c
+@@ -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"
+@@ -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;