diff mbox

[1/3] usb: host: f_usb20ho: add support for Fujitsu ehci/ohci USB 2.0 host controller

Message ID 1418695828-605-2-git-send-email-Sneeker.Yeh@tw.fujitsu.com
State Needs Review / ACK, archived
Headers show

Checks

Context Check Description
robh/checkpatch warning total: 1 errors, 0 warnings, 0 lines checked
robh/patch-applied success

Commit Message

Sneeker Yeh Dec. 16, 2014, 2:10 a.m. UTC
This patch adds support for EHCI compliant Host controller found
on Fujitsu Socs.

Signed-off-by: Sneeker Yeh <Sneeker.Yeh@tw.fujitsu.com>
---
 .../devicetree/bindings/usb/fujitsu-ehci.txt       |   22 ++
 drivers/usb/host/Kconfig                           |   11 +
 drivers/usb/host/Makefile                          |    1 +
 drivers/usb/host/f_usb20ho_hcd.c                   |  306 ++++++++++++++++++++
 drivers/usb/host/f_usb20ho_hcd.h                   |   35 +++
 5 files changed, 375 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/fujitsu-ehci.txt
 create mode 100644 drivers/usb/host/f_usb20ho_hcd.c
 create mode 100644 drivers/usb/host/f_usb20ho_hcd.h

Comments

Arnd Bergmann Dec. 16, 2014, 9:06 a.m. UTC | #1
On Tuesday 16 December 2014 10:10:26 Sneeker Yeh wrote:
> This patch adds support for EHCI compliant Host controller found
> on Fujitsu Socs.
> 
> Signed-off-by: Sneeker Yeh <Sneeker.Yeh@tw.fujitsu.com>
> ---
>  .../devicetree/bindings/usb/fujitsu-ehci.txt       |   22 ++
>  drivers/usb/host/Kconfig                           |   11 +
>  drivers/usb/host/Makefile                          |    1 +
>  drivers/usb/host/f_usb20ho_hcd.c                   |  306 ++++++++++++++++++++
>  drivers/usb/host/f_usb20ho_hcd.h                   |   35 +++
>  5 files changed, 375 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/usb/fujitsu-ehci.txt
>  create mode 100644 drivers/usb/host/f_usb20ho_hcd.c
>  create mode 100644 drivers/usb/host/f_usb20ho_hcd.h
> 
> diff --git a/Documentation/devicetree/bindings/usb/fujitsu-ehci.txt b/Documentation/devicetree/bindings/usb/fujitsu-ehci.txt
> new file mode 100644
> index 0000000..e180860
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/usb/fujitsu-ehci.txt
> @@ -0,0 +1,22 @@
> +FUJITSU GLUE COMPONENTS
> +
> +MB86S7x EHCI GLUE
> + - compatible : Should be "fujitsu,f_usb20ho_hcd"

Please try to use the binding from

Documentation/devicetree/bindings/usb/usb-ehci.txt and the respective
ohci binding first, and use two separate devic enodes.

> + - reg : Address and length of the register set for the device.
> + - interrupts : The irq number of this device that is used to interrupt the
> +   CPU
> + - clocks: from common clock binding, handle to usb clock.
> + - clock-names: from common clock binding.

You should always document the specific strings for a named property.

> + - #stream-id-cells : handle to use "arm,mmu-400" ARM IOMMU driver

Don't use that binding, we are trying to kill that off. Instead, use
an 'iommus' property.


> +	hci_res[0].start = ohci ? resource->start + F_OHCI_OFFSET :
> +		resource->start + F_EHCI_OFFSET;
> +	hci_res[0].end = ohci ?
> +			resource->start + F_OHCI_OFFSET + F_OHCI_SIZE - 1 :
> +			resource->start + F_EHCI_OFFSET + F_EHCI_SIZE - 1;
> +	hci_res[0].flags = IORESOURCE_MEM;
> +
> +	hci_res[1].start = irq;
> +	hci_res[1].flags = IORESOURCE_IRQ;
> +
> +	hci_dev = platform_device_alloc(ohci ? "ohci-platform" :
> +					"ehci-platform", 0);
> +	if (!hci_dev) {
> +		dev_err(&pdev->dev, "platform_device_alloc() failed\n");
> +		ret = -ENODEV;
> +		goto err_res;
> +	}

No need for playing games with child devices, just see how the other
drivers do it.

> +	ret = platform_device_add(hci_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "platform_device_add() failed\n");
> +		goto err_alloc;
> +	}
> +
> +	return hci_dev;
> +
> +err_alloc:
> +	platform_device_put(hci_dev);
> +err_res:
> +	return ERR_PTR(ret);
> +}
> +
> +static u64 f_usb20ho_dma_mask =  DMA_BIT_MASK(32);

The dma mask should come from the dma-ranges property of the parent bus,
as of_platform_populate now does.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arnd Bergmann Dec. 17, 2014, 3:44 p.m. UTC | #2
On Wednesday 17 December 2014 23:33:02 Sneeker. Yeh wrote:
> >
> >No need for playing games with child devices, just see how the other drivers do
> >it.
> 
> I am wondering if this is what you mentioned here? :
> Hcd21: f_usb20ho {
>         compatible = "fujitsu,f_usb20ho";
>         ...
>         Ehci {
>                 compatible = "generic-ehci";
>                 reg = <>;
>         }
>         ohci {
>                 compatible = "generic-ohci";
>                 reg = <>;
>         }
> }
> By doing that, I can just use of_platform_populate() to create platform device more gracefully,
> than using platform_device_xxx() those have been done in of_platform_populate().
> And also, managing register offset is totally done via dts description, not in the code.

What I mean is leaving out the fujitsu,f_usb20ho node entirely, just put the
two USB controllers on the bus directly and remove this driver.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/usb/fujitsu-ehci.txt b/Documentation/devicetree/bindings/usb/fujitsu-ehci.txt
new file mode 100644
index 0000000..e180860
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/fujitsu-ehci.txt
@@ -0,0 +1,22 @@ 
+FUJITSU GLUE COMPONENTS
+
+MB86S7x EHCI GLUE
+ - compatible : Should be "fujitsu,f_usb20ho_hcd"
+ - reg : Address and length of the register set for the device.
+ - interrupts : The irq number of this device that is used to interrupt the
+   CPU
+ - clocks: from common clock binding, handle to usb clock.
+ - clock-names: from common clock binding.
+ - #stream-id-cells : handle to use "arm,mmu-400" ARM IOMMU driver
+ - fujitsu,power-domain : pd_usb2h node has to be builded, details can be
+   found in:
+   Documentation/devicetree/bindings/
+
+hcd21: f_usb20ho_hcd {
+	compatible = "fujitsu,f_usb20ho_hcd";
+	reg = <0 0x34200000 0x80000>;
+	interrupts = <0 419 0x4>;
+	clocks = <&clk_main_2_4>, <&clk_main_4_5>, <&clk_usb_0_0>;
+	clock-names = "h_clk", "p_clk", "p_cryclk";
+	#stream-id-cells = <0>;
+};
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index fafc628..9482140 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -786,6 +786,17 @@  config USB_HCD_SSB
 
 	  If unsure, say N.
 
+config USB_F_USB20HO_HCD
+	tristate "F_USB20HO USB Host Controller"
+	depends on USB && ARCH_MB86S7X
+	default y
+	help
+	  This driver enables support for USB20HO USB Host Controller,
+	  the driver supports High, Full and Low Speed USB.
+
+	  To compile this driver a module, choose M here: the module
+	  will be called "f_usb20ho-hcd".
+
 config USB_HCD_TEST_MODE
 	bool "HCD test mode support"
 	---help---
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index d6216a4..b89e536 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -78,3 +78,4 @@  obj-$(CONFIG_USB_HCD_SSB)	+= ssb-hcd.o
 obj-$(CONFIG_USB_FUSBH200_HCD)	+= fusbh200-hcd.o
 obj-$(CONFIG_USB_FOTG210_HCD)	+= fotg210-hcd.o
 obj-$(CONFIG_USB_MAX3421_HCD)	+= max3421-hcd.o
+obj-$(CONFIG_USB_F_USB20HO_HCD)+= f_usb20ho_hcd.o
diff --git a/drivers/usb/host/f_usb20ho_hcd.c b/drivers/usb/host/f_usb20ho_hcd.c
new file mode 100644
index 0000000..d665487
--- /dev/null
+++ b/drivers/usb/host/f_usb20ho_hcd.c
@@ -0,0 +1,306 @@ 
+/**
+ * f_usb20ho_hcd.c - Fujitsu EHCI platform driver
+ *
+ * Copyright (c) 2013 - 2014 FUJITSU SEMICONDUCTOR LIMITED
+ *		http://jp.fujitsu.com/group/fsl
+ *
+ * based on bcma-hcd.c
+ *
+ * Author: Sneeker Yeh <Sneeker.Yeh@tw.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/usb/ehci_pdriver.h>
+#include <linux/usb/ohci_pdriver.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "f_usb20ho_hcd.h"
+
+static int f_usb20ho_clk_control(struct device *dev, bool on)
+{
+	int ret, i;
+	struct clk *clk;
+
+	if (!on)
+		goto clock_off;
+
+	for (i = 0;; i++) {
+		clk = of_clk_get(dev->of_node, i);
+		if (IS_ERR(clk))
+			break;
+
+		ret = clk_prepare_enable(clk);
+		if (ret) {
+			dev_err(dev, "failed to enable clock[%d]\n", i);
+			goto clock_off;
+		}
+	}
+
+	return 0;
+
+clock_off:
+	for (i = 0;; i++) {
+		clk = of_clk_get(dev->of_node, i);
+		if (IS_ERR(clk))
+			break;
+
+		clk_disable_unprepare(clk);
+	}
+
+	return on;
+}
+
+static struct platform_device *f_usb20ho_hcd_create_pdev(
+		struct platform_device *pdev, bool ohci)
+{
+	struct resource *resource;
+	struct platform_device *hci_dev;
+	struct resource hci_res[2];
+	int ret = -ENOMEM;
+	int irq;
+	resource_size_t resource_size;
+
+	memset(hci_res, 0, sizeof(hci_res));
+
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!resource) {
+		dev_err(&pdev->dev, "%s() platform_get_resource() failed\n"
+			, __func__);
+		ret = -ENODEV;
+		goto err_res;
+	}
+	resource_size = resource->end - resource->start + 1;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev,
+			"%s() platform_get_irq() for F_USB20HO failed at %d\n",
+			__func__, irq);
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hci_res[0].start = ohci ? resource->start + F_OHCI_OFFSET :
+		resource->start + F_EHCI_OFFSET;
+	hci_res[0].end = ohci ?
+			resource->start + F_OHCI_OFFSET + F_OHCI_SIZE - 1 :
+			resource->start + F_EHCI_OFFSET + F_EHCI_SIZE - 1;
+	hci_res[0].flags = IORESOURCE_MEM;
+
+	hci_res[1].start = irq;
+	hci_res[1].flags = IORESOURCE_IRQ;
+
+	hci_dev = platform_device_alloc(ohci ? "ohci-platform" :
+					"ehci-platform", 0);
+	if (!hci_dev) {
+		dev_err(&pdev->dev, "platform_device_alloc() failed\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	dma_set_coherent_mask(&hci_dev->dev, pdev->dev.coherent_dma_mask);
+	hci_dev->dev.parent = &pdev->dev;
+	hci_dev->dev.dma_mask = pdev->dev.dma_mask;
+
+	ret = platform_device_add_resources(hci_dev, hci_res,
+					    ARRAY_SIZE(hci_res));
+	if (ret) {
+		dev_err(&pdev->dev
+			, "platform_device_add_resources() failed\n");
+		goto err_alloc;
+	}
+
+	ret = platform_device_add(hci_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "platform_device_add() failed\n");
+		goto err_alloc;
+	}
+
+	return hci_dev;
+
+err_alloc:
+	platform_device_put(hci_dev);
+err_res:
+	return ERR_PTR(ret);
+}
+
+static u64 f_usb20ho_dma_mask =  DMA_BIT_MASK(32);
+
+static int f_usb20ho_hcd_probe(struct platform_device *pdev)
+{
+	int err;
+	struct f_usb20ho_hcd *usb_dev;
+	struct device *dev = &pdev->dev;
+
+	dev->dma_mask = &f_usb20ho_dma_mask;
+	if (!dev->coherent_dma_mask)
+		dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	usb_dev = kzalloc(sizeof(*usb_dev), GFP_KERNEL);
+	if (!usb_dev)
+		return -ENOMEM;
+
+	usb_dev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, usb_dev);
+
+	usb_dev->irq = platform_get_irq(pdev, 0);
+	if (usb_dev->irq < 0) {
+		dev_err(&pdev->dev,
+			"%s() platform_get_irq() for F_USB20HO failed at %d\n",
+			__func__, usb_dev->irq);
+		err = -ENODEV;
+		goto err_free_usb_dev;
+	}
+	disable_irq(usb_dev->irq);
+
+	/* resume driver for clock, power, irq */
+	pm_runtime_enable(&pdev->dev);
+	err = pm_runtime_get_sync(&pdev->dev);
+	if (err < 0) {
+		dev_err(&pdev->dev, "get_sync failed with err %d\n", err);
+		goto err_unregister_ohci_dev;
+	}
+
+	usb_dev->ehci_dev = f_usb20ho_hcd_create_pdev(pdev, false);
+	if (IS_ERR(usb_dev->ehci_dev)) {
+		dev_err(&pdev->dev, "failed to create EHCI driver\n");
+		err = -ENODEV;
+		goto err_free_usb_dev;
+	}
+
+	usb_dev->ohci_dev = f_usb20ho_hcd_create_pdev(pdev, true);
+	if (IS_ERR(usb_dev->ohci_dev)) {
+		dev_err(&pdev->dev, "failed to create OHCI driver\n");
+		err = -ENODEV;
+		goto err_unregister_ehci_dev;
+	}
+
+	return 0;
+
+err_unregister_ohci_dev:
+	platform_device_unregister(usb_dev->ohci_dev);
+err_unregister_ehci_dev:
+	platform_device_unregister(usb_dev->ehci_dev);
+	pm_runtime_put_sync(&pdev->dev);
+err_free_usb_dev:
+	kfree(usb_dev);
+
+	return err;
+}
+
+static int f_usb20ho_hcd_remove(struct platform_device *pdev)
+{
+	struct f_usb20ho_hcd *usb_dev = platform_get_drvdata(pdev);
+	struct platform_device *ohci_dev = usb_dev->ohci_dev;
+	struct platform_device *ehci_dev = usb_dev->ehci_dev;
+
+	if (ohci_dev)
+		platform_device_unregister(ohci_dev);
+	if (ehci_dev)
+		platform_device_unregister(ehci_dev);
+
+	/* disable power,clock,irq */
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
+static int  f_usb20ho_runtime_suspend(struct device *dev)
+{
+	struct f_usb20ho_hcd *usb_dev = dev_get_drvdata(dev);
+
+	disable_irq(usb_dev->irq);
+	f_usb20ho_clk_control(dev, false);
+
+	return 0;
+}
+
+static int  f_usb20ho_runtime_resume(struct device *dev)
+{
+	struct f_usb20ho_hcd *usb_dev = dev_get_drvdata(dev);
+
+	f_usb20ho_clk_control(dev, true);
+	enable_irq(usb_dev->irq);
+
+	return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+static int f_usb20ho_hcd_suspend(struct device *dev)
+{
+	if (pm_runtime_status_suspended(dev))
+		return 0;
+
+	return f_usb20ho_runtime_suspend(dev);
+}
+
+static int f_usb20ho_hcd_resume(struct device *dev)
+{
+	if (pm_runtime_status_suspended(dev))
+		return 0;
+
+	return f_usb20ho_runtime_resume(dev);
+}
+
+static const struct dev_pm_ops f_usb20ho_hcd_ops = {
+	.suspend =  f_usb20ho_hcd_suspend,
+	.resume = f_usb20ho_hcd_resume,
+	SET_RUNTIME_PM_OPS(f_usb20ho_runtime_suspend
+		, f_usb20ho_runtime_resume, NULL)
+};
+
+#define DEV_PM (&f_usb20ho_hcd_ops)
+#else /* !CONFIG_PM */
+#define DEV_PM	NULL
+#endif /* CONFIG_PM */
+
+static void f_usb20ho_hcd_shutdown(struct platform_device *pdev)
+{
+#ifdef CONFIG_PM
+	 f_usb20ho_hcd_suspend(&pdev->dev);
+#endif
+}
+
+static const struct of_device_id f_usb20ho_hcd_dt_ids[] = {
+	{ .compatible = "fujitsu,f_usb20ho_hcd" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, f_usb20ho_hcd_dt_ids);
+
+static struct platform_driver f_usb20ho_hcd_driver = {
+	.probe		= f_usb20ho_hcd_probe,
+	.remove		= __exit_p(f_usb20ho_hcd_remove),
+	.shutdown	= f_usb20ho_hcd_shutdown,
+	.driver = {
+		.name = "f_usb20ho_hcd",
+		.owner = THIS_MODULE,
+		.pm = DEV_PM,
+		.of_match_table = f_usb20ho_hcd_dt_ids,
+	},
+};
+
+module_platform_driver(f_usb20ho_hcd_driver);
+
+MODULE_ALIAS("platform:f_usb20ho_hcd");
+MODULE_AUTHOR("Sneeker Yeh <Sneeker.Yeh@tw.fujitsu.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB platform driver for f_usb20ho_lap IP ");
diff --git a/drivers/usb/host/f_usb20ho_hcd.h b/drivers/usb/host/f_usb20ho_hcd.h
new file mode 100644
index 0000000..046c636
--- /dev/null
+++ b/drivers/usb/host/f_usb20ho_hcd.h
@@ -0,0 +1,35 @@ 
+/*
+ * linux/drivers/usb/host/f_usb20ho_hcd.h - F_USB20HDC USB
+ * host controller driver
+ *
+ * Copyright (C) FUJITSU ELECTRONICS INC. 2011. All rights reserved.
+ * Copyright (C) 2013 - 2014 FUJITSU SEMICONDUCTOR LIMITED.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifndef _F_USB20HO_HCD_H
+#define _F_USB20HO_HCD_H
+
+#define F_EHCI_OFFSET	0x40000
+#define F_EHCI_SIZE		0x1000
+#define F_OHCI_OFFSET	0x41000
+#define F_OHCI_SIZE		0x1000
+#define F_OTHER_OFFSET	0x42000
+#define F_OTHER_SIZE		0x1000
+
+struct f_usb20ho_hcd {
+	struct device *dev;
+	struct platform_device *ehci_dev;
+	struct platform_device *ohci_dev;
+	int irq;
+};
+#endif