blob: bc1e3ef44da3d96e9bc9ada9a6ec83928441ea31 [file] [log] [blame]
From 3ad40ac9b1a0583b3eac18fe0cc3f434cb85f385 Mon Sep 17 00:00:00 2001
From: Ulrich Hecht <ulrich.hecht@gmail.com>
Date: Thu, 10 Jul 2014 09:53:59 +0200
Subject: usb: renesas_usbhs: add R-Car Gen. 2 init and power control
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 | 2 +-
drivers/usb/renesas_usbhs/common.c | 66 +++++++++++++++++++++++++++++---
drivers/usb/renesas_usbhs/common.h | 2 +
drivers/usb/renesas_usbhs/rcar2.c | 77 ++++++++++++++++++++++++++++++++++++++
drivers/usb/renesas_usbhs/rcar2.h | 4 ++
include/linux/usb/renesas_usbhs.h | 6 +++
6 files changed, 151 insertions(+), 6 deletions(-)
create mode 100644 drivers/usb/renesas_usbhs/rcar2.c
create mode 100644 drivers/usb/renesas_usbhs/rcar2.h
diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile
index bc8aef4311a1..9e47f477b6d2 100644
--- a/drivers/usb/renesas_usbhs/Makefile
+++ b/drivers/usb/renesas_usbhs/Makefile
@@ -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
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 17267b0a2e95..1b9bf8d83235 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -15,12 +15,14 @@
*
*/
#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
*
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index c69dd2fba360..a7996da6a1bd 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -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
index 000000000000..e6b9dcc1c289
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rcar2.c
@@ -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
index 000000000000..f07f10d9b3b2
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rcar2.h
@@ -0,0 +1,4 @@
+#include "common.h"
+
+extern const struct renesas_usbhs_platform_callback
+ usbhs_rcar2_ops;
diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h
index e452ba6ec6bd..d5952bb66752 100644
--- a/include/linux/usb/renesas_usbhs.h
+++ b/include/linux/usb/renesas_usbhs.h
@@ -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:
*
--
2.1.2