usb: renesas_usbhs: add R-Car Gen. 2 init and power control
authorUlrich Hecht <ulrich.hecht@gmail.com>
Thu, 10 Jul 2014 07:53:59 +0000 (09:53 +0200)
committerSimon Horman <horms@verge.net.au>
Fri, 5 Dec 2014 00:24:19 +0000 (09:24 +0900)
In preparation for DT conversion to reduce reliance on platform device
callbacks.

Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
(cherry picked from commit 8ecef00fe1f33658ee36e902dba6850b51312073)
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
drivers/usb/renesas_usbhs/Makefile
drivers/usb/renesas_usbhs/common.c
drivers/usb/renesas_usbhs/common.h
drivers/usb/renesas_usbhs/rcar2.c [new file with mode: 0644]
drivers/usb/renesas_usbhs/rcar2.h [new file with mode: 0644]
include/linux/usb/renesas_usbhs.h

index bc8aef4..9e47f47 100644 (file)
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_USB_RENESAS_USBHS)        += renesas_usbhs.o
 
-renesas_usbhs-y                        := common.o mod.o pipe.o fifo.o
+renesas_usbhs-y                        := common.o mod.o pipe.o fifo.o rcar2.o
 
 ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),)
        renesas_usbhs-y         += mod_host.o
index 17267b0..1b9bf8d 100644 (file)
  *
  */
 #include <linux/err.h>
+#include <linux/gpio.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/sysfs.h>
 #include "common.h"
+#include "rcar2.h"
 
 /*
  *             image of renesas_usbhs
@@ -284,6 +286,8 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv)
 /*
  *             platform default param
  */
+
+/* commonly used on old SH-Mobile SoCs */
 static u32 usbhsc_default_pipe_type[] = {
                USB_ENDPOINT_XFER_CONTROL,
                USB_ENDPOINT_XFER_ISOC,
@@ -297,6 +301,26 @@ static u32 usbhsc_default_pipe_type[] = {
                USB_ENDPOINT_XFER_INT,
 };
 
+/* commonly used on newer SH-Mobile and R-Car SoCs */
+static u32 usbhsc_new_pipe_type[] = {
+               USB_ENDPOINT_XFER_CONTROL,
+               USB_ENDPOINT_XFER_ISOC,
+               USB_ENDPOINT_XFER_ISOC,
+               USB_ENDPOINT_XFER_BULK,
+               USB_ENDPOINT_XFER_BULK,
+               USB_ENDPOINT_XFER_BULK,
+               USB_ENDPOINT_XFER_INT,
+               USB_ENDPOINT_XFER_INT,
+               USB_ENDPOINT_XFER_INT,
+               USB_ENDPOINT_XFER_BULK,
+               USB_ENDPOINT_XFER_BULK,
+               USB_ENDPOINT_XFER_BULK,
+               USB_ENDPOINT_XFER_BULK,
+               USB_ENDPOINT_XFER_BULK,
+               USB_ENDPOINT_XFER_BULK,
+               USB_ENDPOINT_XFER_BULK,
+};
+
 /*
  *             power control
  */
@@ -423,8 +447,7 @@ static int usbhs_probe(struct platform_device *pdev)
        int ret;
 
        /* check platform information */
-       if (!info ||
-           !info->platform_callback.get_id) {
+       if (!info) {
                dev_err(&pdev->dev, "no platform information\n");
                return -EINVAL;
        }
@@ -451,13 +474,32 @@ static int usbhs_probe(struct platform_device *pdev)
        /*
         * care platform info
         */
-       memcpy(&priv->pfunc,
-              &info->platform_callback,
-              sizeof(struct renesas_usbhs_platform_callback));
+
        memcpy(&priv->dparam,
               &info->driver_param,
               sizeof(struct renesas_usbhs_driver_param));
 
+       switch (priv->dparam.type) {
+       case USBHS_TYPE_R8A7790:
+       case USBHS_TYPE_R8A7791:
+               priv->pfunc = usbhs_rcar2_ops;
+               if (!priv->dparam.pipe_type) {
+                       priv->dparam.pipe_type = usbhsc_new_pipe_type;
+                       priv->dparam.pipe_size =
+                               ARRAY_SIZE(usbhsc_new_pipe_type);
+               }
+               break;
+       default:
+               if (!info->platform_callback.get_id) {
+                       dev_err(&pdev->dev, "no platform callbacks");
+                       return -EINVAL;
+               }
+               memcpy(&priv->pfunc,
+                      &info->platform_callback,
+                      sizeof(struct renesas_usbhs_platform_callback));
+               break;
+       }
+
        /* set driver callback functions for platform */
        dfunc                   = &info->driver_callback;
        dfunc->notify_hotplug   = usbhsc_drvcllbck_notify_hotplug;
@@ -507,6 +549,20 @@ static int usbhs_probe(struct platform_device *pdev)
         */
        usbhs_sys_clock_ctrl(priv, 0);
 
+       /* check GPIO determining if USB function should be enabled */
+       if (priv->dparam.enable_gpio) {
+               gpio_request_one(priv->dparam.enable_gpio, GPIOF_IN, NULL);
+               ret = !gpio_get_value(priv->dparam.enable_gpio);
+               gpio_free(priv->dparam.enable_gpio);
+               if (ret) {
+                       dev_warn(&pdev->dev,
+                                "USB function not selected (GPIO %d)\n",
+                                priv->dparam.enable_gpio);
+                       ret = -ENOTSUPP;
+                       goto probe_end_mod_exit;
+               }
+       }
+
        /*
         * platform call
         *
index c69dd2f..a7996da 100644 (file)
@@ -268,6 +268,8 @@ struct usbhs_priv {
         * fifo control
         */
        struct usbhs_fifo_info fifo_info;
+
+       struct usb_phy *phy;
 };
 
 /*
diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c
new file mode 100644 (file)
index 0000000..e6b9dcc
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Renesas USB driver R-Car Gen. 2 initialization and power control
+ *
+ * Copyright (C) 2014 Ulrich Hecht
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/gpio-rcar.h>
+#include <linux/usb/phy.h>
+#include "common.h"
+#include "rcar2.h"
+
+static int usbhs_rcar2_hardware_init(struct platform_device *pdev)
+{
+       struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+       struct usb_phy *phy;
+
+       phy = usb_get_phy_dev(&pdev->dev, 0);
+       if (IS_ERR(phy))
+               return PTR_ERR(phy);
+
+       priv->phy = phy;
+       return 0;
+}
+
+static int usbhs_rcar2_hardware_exit(struct platform_device *pdev)
+{
+       struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+       if (!priv->phy)
+               return 0;
+
+       usb_put_phy(priv->phy);
+       priv->phy = NULL;
+
+       return 0;
+}
+
+static int usbhs_rcar2_power_ctrl(struct platform_device *pdev,
+                               void __iomem *base, int enable)
+{
+       struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+       if (!priv->phy)
+               return -ENODEV;
+
+       if (enable) {
+               int retval = usb_phy_init(priv->phy);
+
+               if (!retval)
+                       retval = usb_phy_set_suspend(priv->phy, 0);
+               return retval;
+       }
+
+       usb_phy_set_suspend(priv->phy, 1);
+       usb_phy_shutdown(priv->phy);
+       return 0;
+}
+
+static int usbhs_rcar2_get_id(struct platform_device *pdev)
+{
+       return USBHS_GADGET;
+}
+
+const struct renesas_usbhs_platform_callback usbhs_rcar2_ops = {
+       .hardware_init = usbhs_rcar2_hardware_init,
+       .hardware_exit = usbhs_rcar2_hardware_exit,
+       .power_ctrl = usbhs_rcar2_power_ctrl,
+       .get_id = usbhs_rcar2_get_id,
+};
diff --git a/drivers/usb/renesas_usbhs/rcar2.h b/drivers/usb/renesas_usbhs/rcar2.h
new file mode 100644 (file)
index 0000000..f07f10d
--- /dev/null
@@ -0,0 +1,4 @@
+#include "common.h"
+
+extern const struct renesas_usbhs_platform_callback
+       usbhs_rcar2_ops;
index e452ba6..d5952bb 100644 (file)
@@ -153,6 +153,9 @@ struct renesas_usbhs_driver_param {
         */
        int pio_dma_border; /* default is 64byte */
 
+       u32 type;
+       u32 enable_gpio;
+
        /*
         * option:
         */
@@ -160,6 +163,9 @@ struct renesas_usbhs_driver_param {
        u32 has_sudmac:1; /* for SUDMAC */
 };
 
+#define USBHS_TYPE_R8A7790 1
+#define USBHS_TYPE_R8A7791 2
+
 /*
  * option:
  *