2009-06-11 10:49:37

by Nicolas Ferre

[permalink] [raw]
Subject: [PATCH 1/2 v2] at91/USB: Add USB drivers for at91sam9g45 series

Add both host and gadget USB drivers for at91sam9g45 series. Those SOC embed
high speed USB interfaces.
The gadget driver is the already available atmel_usba_udc.
The host driver is an EHCI with its companion OHCI. EHCI is handled by the new
ehci-atmel.c whereas the OHCI is always handled by ohci-at91.c. This last
wrapper is modified to allow IRQ sharing between two controllers.

Signed-off-by: Nicolas Ferre <[email protected]>
---
Changes from v1:
remove useless comments and single line comments on one line
clock get individual error path

drivers/usb/Kconfig | 1 +
drivers/usb/gadget/Kconfig | 4 +-
drivers/usb/host/ehci-atmel.c | 232 +++++++++++++++++++++++++++++++++++++++++
drivers/usb/host/ehci-hcd.c | 5 +
drivers/usb/host/ohci-at91.c | 3 +-
5 files changed, 242 insertions(+), 3 deletions(-)
create mode 100644 drivers/usb/host/ehci-atmel.c

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index c6c816b..3974c9c 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -57,6 +57,7 @@ config USB_ARCH_HAS_EHCI
default y if PPC_83xx
default y if SOC_AU1200
default y if ARCH_IXP4XX
+ default y if ARCH_AT91SAM9G45
default PCI

# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 080bb1e..9beea52 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -124,7 +124,7 @@ choice

config USB_GADGET_AT91
boolean "Atmel AT91 USB Device Port"
- depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9
+ depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45
select USB_GADGET_SELECTED
help
Many Atmel AT91 processors (such as the AT91RM2000) have a
@@ -143,7 +143,7 @@ config USB_AT91
config USB_GADGET_ATMEL_USBA
boolean "Atmel USBA"
select USB_GADGET_DUALSPEED
- depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL
+ depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
USBA is the integrated high-speed USB Device controller on
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
new file mode 100644
index 0000000..3095403
--- /dev/null
+++ b/drivers/usb/host/ehci-atmel.c
@@ -0,0 +1,232 @@
+/*
+ * Driver for EHCI UHP on Atmel chips
+ *
+ * Copyright (C) 2009 Atmel Corporation,
+ * Nicolas Ferre <[email protected]>
+ *
+ * Based on various ehci-*.c drivers
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+/* interface and function clocks */
+static struct clk *iclk, *fclk;
+static int clocked;
+
+/*-------------------------------------------------------------------------*/
+
+static void atmel_start_clock(void)
+{
+ clk_enable(iclk);
+ clk_enable(fclk);
+ clocked = 1;
+}
+
+static void atmel_stop_clock(void)
+{
+ clk_disable(fclk);
+ clk_disable(iclk);
+ clocked = 0;
+}
+
+static void atmel_start_ehci(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "start\n");
+ atmel_start_clock();
+}
+
+static void atmel_stop_ehci(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "stop\n");
+ atmel_stop_clock();
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_atmel_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval = 0;
+
+ /* registers start at offset 0x0 */
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs +
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci->sbrn = 0x20;
+
+ ehci_reset(ehci);
+ ehci_port_power(ehci, 0);
+
+ return retval;
+}
+
+static const struct hc_driver ehci_atmel_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Atmel EHCI UHP HS",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /* generic hardware linkage */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /* basic lifecycle operations */
+ .reset = ehci_atmel_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /* managing i/o requests and associated device resources */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /* scheduling support */
+ .get_frame_number = ehci_get_frame,
+
+ /* root hub support */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+};
+
+static int __init ehci_atmel_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ const struct hc_driver *driver = &ehci_atmel_hc_driver;
+ struct resource *res;
+ int irq;
+ int retval;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_debug("Initializing Atmel-SoC USB Host Controller\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ retval = -ENODEV;
+ goto fail_create_hcd;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto fail_create_hcd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no register addr. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ retval = -ENODEV;
+ goto fail_request_resource;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto fail_request_resource;
+ }
+
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ if (hcd->regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto fail_ioremap;
+ }
+
+ iclk = clk_get(&pdev->dev, "ehci_clk");
+ if (IS_ERR(iclk)) {
+ dev_err(&pdev->dev, "Error getting interface clock\n");
+ retval = -ENOENT;
+ goto fail_get_iclk;
+ }
+ fclk = clk_get(&pdev->dev, "uhpck");
+ if (IS_ERR(fclk)) {
+ dev_err(&pdev->dev, "Error getting function clock\n");
+ retval = -ENOENT;
+ goto fail_get_fclk;
+ }
+
+ atmel_start_ehci(pdev);
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval)
+ goto fail_add_hcd;
+
+ return retval;
+
+fail_add_hcd:
+ atmel_stop_ehci(pdev);
+ clk_put(fclk);
+fail_get_fclk:
+ clk_put(iclk);
+fail_get_iclk:
+ iounmap(hcd->regs);
+fail_ioremap:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+fail_request_resource:
+ usb_put_hcd(hcd);
+fail_create_hcd:
+ dev_err(&pdev->dev, "init %s fail, %d\n",
+ dev_name(&pdev->dev), retval);
+
+ return retval;
+}
+
+static int __exit ehci_atmel_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ ehci_shutdown(hcd);
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ atmel_stop_ehci(pdev);
+ clk_put(fclk);
+ clk_put(iclk);
+ fclk = iclk = NULL;
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:atmel-ehci");
+
+static struct platform_driver ehci_atmel_driver = {
+ .probe = ehci_atmel_drv_probe,
+ .remove = __exit_p(ehci_atmel_drv_remove),
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver.name = "atmel-ehci",
+};
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index c637207..df2ddee 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1072,6 +1072,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ixp4xx_ehci_driver
#endif

+#ifdef CONFIG_ARCH_AT91
+#include "ehci-atmel.c"
+#define PLATFORM_DRIVER ehci_atmel_driver
+#endif
+
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER)
#error "missing bus glue for ehci-hcd"
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index bb5e6f6..b29b0fe 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -148,7 +148,8 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
at91_start_hc(pdev);
ohci_hcd_init(hcd_to_ohci(hcd));

- retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, pdev->resource[1].start,
+ IRQF_DISABLED | IRQF_SHARED);
if (retval == 0)
return retval;

--
1.5.3.7


2009-06-19 07:50:47

by David Brownell

[permalink] [raw]
Subject: Re: [PATCH 1/2 v2] at91/USB: Add USB drivers for at91sam9g45 series

On Thursday 11 June 2009, Nicolas Ferre wrote:
> --- a/drivers/usb/gadget/Kconfig
> +++ b/drivers/usb/gadget/Kconfig
> @@ -124,7 +124,7 @@ choice
> ?
> ?config USB_GADGET_AT91
> ????????boolean "Atmel AT91 USB Device Port"
> -???????depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9
> +???????depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45
> ????????select USB_GADGET_SELECTED
> ????????help
> ???????? ? Many Atmel AT91 processors (such as the AT91RM2000) have a
> @@ -143,7 +143,7 @@ config USB_AT91
> ?config USB_GADGET_ATMEL_USBA
> ????????boolean "Atmel USBA"
> ????????select USB_GADGET_DUALSPEED
> -???????depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL
> +???????depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
> ????????help
> ???????? ?USBA is the integrated high-speed USB Device controller on
> ???????? ?the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.

I understand the temptation, but please keep the gadget-side and host-side
support in separate patches.

If/when high speed OTG is supported on AT91 systems, that support would need
to touch both sides of the USB stack. Otherwise, avoid doing that.


2009-06-19 07:54:26

by David Brownell

[permalink] [raw]
Subject: Re: [PATCH 1/2 v2] at91/USB: Add USB drivers for at91sam9g45 series

On Thursday 11 June 2009, Nicolas Ferre wrote:
> --- a/drivers/usb/host/ohci-at91.c
> +++ b/drivers/usb/host/ohci-at91.c

Again, please split this out: OHCI patches separate from EHCI.


> @@ -148,7 +148,8 @@ static int usb_hcd_at91_probe(const struct hc_driver
> *driver, at91_start_hc(pdev);
> ????????ohci_hcd_init(hcd_to_ohci(hcd));
> ?
> -???????retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED);
> +???????retval = usb_add_hcd(hcd, pdev->resource[1].start,
> +???????????????????????????????IRQF_DISABLED | IRQF_SHARED);

Just pass IRQF_SHARED. That combination is not going be effective.

2009-06-19 07:59:58

by David Brownell

[permalink] [raw]
Subject: Re: [PATCH 1/2 v2] at91/USB: Add USB drivers for at91sam9g45 series

On Thursday 11 June 2009, Nicolas Ferre wrote:
> Add both host and gadget USB drivers for at91sam9g45 series. Those SOC embed
> high speed USB interfaces.
> The gadget driver is the already available atmel_usba_udc.
> The host driver is an EHCI with its companion OHCI. EHCI is handled by the new
> ehci-atmel.c whereas the OHCI is always handled by ohci-at91.c. This last
> wrapper is modified to allow IRQ sharing between two controllers.
>
> Signed-off-by: Nicolas Ferre <[email protected]>

Talking only about the EHCI bits here ... it looks pretty generic
except for the clock management, which can't be.

The tricky bits here will be system suspend/resume support, with
the host controller able to trigger remote wakeup. Clearly you
don't support system suspend/resume yet.

A slightly less tricky bit would be suspending the root hub.
Sometimes the hardware needs help there; other times it can
automatically gate the various clocks and save power.

If you split out patches for each of the drivers, I can ack
each of those seaprately. And then hope to see patches which
add power management support before too long.

- Dave

2009-06-23 15:56:21

by Nicolas Ferre

[permalink] [raw]
Subject: [PATCH 2/3 v3] at91/USB: modify OHCI driver to allow shared interrupts

At91sam9g45 series has a set of high speed USB interfaces.
The host driver is an EHCI with its companion OHCI. OHCI is
always handled by ohci-at91.c.
This wrapper is just modified to allow IRQ sharing
between two controllers.

Signed-off-by: Nicolas Ferre <[email protected]>
---
v2 -> v3: split patch to:
- gadget
- ohci
- ehci

drivers/usb/host/ohci-at91.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index bb5e6f6..7ccffcb 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -148,7 +148,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
at91_start_hc(pdev);
ohci_hcd_init(hcd_to_ohci(hcd));

- retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
if (retval == 0)
return retval;

--
1.5.3.7

2009-06-23 15:56:36

by Nicolas Ferre

[permalink] [raw]
Subject: [PATCH 3/3 v3] at91/USB: Add USB EHCI driver for at91sam9g45 series

Add host USB High speed driver for at91sam9g45 series.
The host driver is an EHCI with its companion OHCI. EHCI is
handled by the new ehci-atmel.c whereas the OHCI is always
handled by ohci-at91.c.

Signed-off-by: Nicolas Ferre <[email protected]>
---
v2 -> v3: split patch to:
- gadget
- ohci
- ehci

drivers/usb/Kconfig | 1 +
drivers/usb/host/ehci-atmel.c | 232 +++++++++++++++++++++++++++++++++++++++++
drivers/usb/host/ehci-hcd.c | 5 +
3 files changed, 238 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/host/ehci-atmel.c

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index c6c816b..3974c9c 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -57,6 +57,7 @@ config USB_ARCH_HAS_EHCI
default y if PPC_83xx
default y if SOC_AU1200
default y if ARCH_IXP4XX
+ default y if ARCH_AT91SAM9G45
default PCI

# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
new file mode 100644
index 0000000..3095403
--- /dev/null
+++ b/drivers/usb/host/ehci-atmel.c
@@ -0,0 +1,232 @@
+/*
+ * Driver for EHCI UHP on Atmel chips
+ *
+ * Copyright (C) 2009 Atmel Corporation,
+ * Nicolas Ferre <[email protected]>
+ *
+ * Based on various ehci-*.c drivers
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+/* interface and function clocks */
+static struct clk *iclk, *fclk;
+static int clocked;
+
+/*-------------------------------------------------------------------------*/
+
+static void atmel_start_clock(void)
+{
+ clk_enable(iclk);
+ clk_enable(fclk);
+ clocked = 1;
+}
+
+static void atmel_stop_clock(void)
+{
+ clk_disable(fclk);
+ clk_disable(iclk);
+ clocked = 0;
+}
+
+static void atmel_start_ehci(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "start\n");
+ atmel_start_clock();
+}
+
+static void atmel_stop_ehci(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "stop\n");
+ atmel_stop_clock();
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_atmel_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval = 0;
+
+ /* registers start at offset 0x0 */
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs +
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci->sbrn = 0x20;
+
+ ehci_reset(ehci);
+ ehci_port_power(ehci, 0);
+
+ return retval;
+}
+
+static const struct hc_driver ehci_atmel_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Atmel EHCI UHP HS",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /* generic hardware linkage */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /* basic lifecycle operations */
+ .reset = ehci_atmel_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /* managing i/o requests and associated device resources */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /* scheduling support */
+ .get_frame_number = ehci_get_frame,
+
+ /* root hub support */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+};
+
+static int __init ehci_atmel_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ const struct hc_driver *driver = &ehci_atmel_hc_driver;
+ struct resource *res;
+ int irq;
+ int retval;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_debug("Initializing Atmel-SoC USB Host Controller\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ retval = -ENODEV;
+ goto fail_create_hcd;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto fail_create_hcd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no register addr. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ retval = -ENODEV;
+ goto fail_request_resource;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto fail_request_resource;
+ }
+
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ if (hcd->regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto fail_ioremap;
+ }
+
+ iclk = clk_get(&pdev->dev, "ehci_clk");
+ if (IS_ERR(iclk)) {
+ dev_err(&pdev->dev, "Error getting interface clock\n");
+ retval = -ENOENT;
+ goto fail_get_iclk;
+ }
+ fclk = clk_get(&pdev->dev, "uhpck");
+ if (IS_ERR(fclk)) {
+ dev_err(&pdev->dev, "Error getting function clock\n");
+ retval = -ENOENT;
+ goto fail_get_fclk;
+ }
+
+ atmel_start_ehci(pdev);
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval)
+ goto fail_add_hcd;
+
+ return retval;
+
+fail_add_hcd:
+ atmel_stop_ehci(pdev);
+ clk_put(fclk);
+fail_get_fclk:
+ clk_put(iclk);
+fail_get_iclk:
+ iounmap(hcd->regs);
+fail_ioremap:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+fail_request_resource:
+ usb_put_hcd(hcd);
+fail_create_hcd:
+ dev_err(&pdev->dev, "init %s fail, %d\n",
+ dev_name(&pdev->dev), retval);
+
+ return retval;
+}
+
+static int __exit ehci_atmel_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ ehci_shutdown(hcd);
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ atmel_stop_ehci(pdev);
+ clk_put(fclk);
+ clk_put(iclk);
+ fclk = iclk = NULL;
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:atmel-ehci");
+
+static struct platform_driver ehci_atmel_driver = {
+ .probe = ehci_atmel_drv_probe,
+ .remove = __exit_p(ehci_atmel_drv_remove),
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver.name = "atmel-ehci",
+};
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index c637207..df2ddee 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1072,6 +1072,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ixp4xx_ehci_driver
#endif

+#ifdef CONFIG_ARCH_AT91
+#include "ehci-atmel.c"
+#define PLATFORM_DRIVER ehci_atmel_driver
+#endif
+
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER)
#error "missing bus glue for ehci-hcd"
--
1.5.3.7

2009-06-23 15:57:24

by Nicolas Ferre

[permalink] [raw]
Subject: [PATCH 1/3 v3] at91/USB: Add USB gadget driver selection for at91sam9g45 series

Add gadget USB drivers for at91sam9g45 series. Those SOC include
high speed USB interfaces.
The gadget driver is the already available atmel_usba_udc.

Signed-off-by: Nicolas Ferre <[email protected]>
---
v2 -> v3: split patch to:
- gadget
- ohci
- ehci

drivers/usb/gadget/Kconfig | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 080bb1e..9beea52 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -124,7 +124,7 @@ choice

config USB_GADGET_AT91
boolean "Atmel AT91 USB Device Port"
- depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9
+ depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45
select USB_GADGET_SELECTED
help
Many Atmel AT91 processors (such as the AT91RM2000) have a
@@ -143,7 +143,7 @@ config USB_AT91
config USB_GADGET_ATMEL_USBA
boolean "Atmel USBA"
select USB_GADGET_DUALSPEED
- depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL
+ depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
USBA is the integrated high-speed USB Device controller on
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
--
1.5.3.7

Subject: Re: [PATCH 1/3 v3] at91/USB: Add USB gadget driver selection for at91sam9g45 series

On 18:58 Tue 23 Jun , Nicolas Ferre wrote:
> Add gadget USB drivers for at91sam9g45 series. Those SOC include
> high speed USB interfaces.
> The gadget driver is the already available atmel_usba_udc.
>
> Signed-off-by: Nicolas Ferre <[email protected]>
> ---
> v2 -> v3: split patch to:
> - gadget
> - ohci
> - ehci
>
> drivers/usb/gadget/Kconfig | 4 ++--
> 1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> index 080bb1e..9beea52 100644
> --- a/drivers/usb/gadget/Kconfig
> +++ b/drivers/usb/gadget/Kconfig
> @@ -124,7 +124,7 @@ choice
>
> config USB_GADGET_AT91
> boolean "Atmel AT91 USB Device Port"
> - depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9
> + depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45
same as ATMEL_FB
it will be really better to define it at the soc level

Best Regards,
J.

2009-06-24 07:23:54

by Nicolas Ferre

[permalink] [raw]
Subject: Re: [PATCH 1/3 v3] at91/USB: Add USB gadget driver selection for at91sam9g45 series

Jean-Christophe PLAGNIOL-VILLARD :
> On 18:58 Tue 23 Jun , Nicolas Ferre wrote:
>> Add gadget USB drivers for at91sam9g45 series. Those SOC include
>> high speed USB interfaces.
>> The gadget driver is the already available atmel_usba_udc.
>>
>> Signed-off-by: Nicolas Ferre <[email protected]>
>> ---
>> v2 -> v3: split patch to:
>> - gadget
>> - ohci
>> - ehci
>>
>> drivers/usb/gadget/Kconfig | 4 ++--
>> 1 files changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
>> index 080bb1e..9beea52 100644
>> --- a/drivers/usb/gadget/Kconfig
>> +++ b/drivers/usb/gadget/Kconfig
>> @@ -124,7 +124,7 @@ choice
>>
>> config USB_GADGET_AT91
>> boolean "Atmel AT91 USB Device Port"
>> - depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9
>> + depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45
> same as ATMEL_FB
> it will be really better to define it at the soc level

No, I do not think this is relevant here.

For now, only 4 dependency on a line is totally acceptable. The cost of
the adding of a new configuration variable is too high in this case
(readability, simplicity, ease of use, etc.).

Best regards,
--
Nicolas Ferre

2009-06-26 05:22:18

by David Brownell

[permalink] [raw]
Subject: Re: [PATCH 1/3 v3] at91/USB: Add USB gadget driver selection for at91sam9g45 series

On Tuesday 23 June 2009, Nicolas Ferre wrote:
> Add gadget USB drivers for at91sam9g45 series. Those SOC include
> high speed USB interfaces.
> The gadget driver is the already available atmel_usba_udc.
>
> Signed-off-by: Nicolas Ferre <[email protected]>

Acked-by: David Brownell <[email protected]>

SAM9G45 patches aren't in mainine yet, right? So I
assume these can wait (maybe until 2.6.32-early).


> ---
> v2 -> v3: split patch to:
> - gadget
> - ohci
> - ehci
>
> drivers/usb/gadget/Kconfig | 4 ++--
> 1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> index 080bb1e..9beea52 100644
> --- a/drivers/usb/gadget/Kconfig
> +++ b/drivers/usb/gadget/Kconfig
> @@ -124,7 +124,7 @@ choice
>
> config USB_GADGET_AT91
> boolean "Atmel AT91 USB Device Port"
> - depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9
> + depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45
> select USB_GADGET_SELECTED
> help
> Many Atmel AT91 processors (such as the AT91RM2000) have a
> @@ -143,7 +143,7 @@ config USB_AT91
> config USB_GADGET_ATMEL_USBA
> boolean "Atmel USBA"
> select USB_GADGET_DUALSPEED
> - depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL
> + depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
> help
> USBA is the integrated high-speed USB Device controller on
> the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
> --
> 1.5.3.7
>
>

2009-06-26 05:22:41

by David Brownell

[permalink] [raw]
Subject: Re: [PATCH 2/3 v3] at91/USB: modify OHCI driver to allow shared interrupts

On Tuesday 23 June 2009, Nicolas Ferre wrote:
> At91sam9g45 series has a set of high speed USB interfaces.
> The host driver is an EHCI with its companion OHCI. OHCI is
> always handled by ohci-at91.c.
> This wrapper is just modified to allow IRQ sharing
> between two controllers.
>
> Signed-off-by: Nicolas Ferre <[email protected]>

Acked-by: David Brownell <[email protected]>


> ---
> v2 -> v3: split patch to:
> - gadget
> - ohci
> - ehci
>
> drivers/usb/host/ohci-at91.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
> index bb5e6f6..7ccffcb 100644
> --- a/drivers/usb/host/ohci-at91.c
> +++ b/drivers/usb/host/ohci-at91.c
> @@ -148,7 +148,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
> at91_start_hc(pdev);
> ohci_hcd_init(hcd_to_ohci(hcd));
>
> - retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED);
> + retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
> if (retval == 0)
> return retval;
>
> --
> 1.5.3.7
>
>

2009-06-26 05:25:39

by David Brownell

[permalink] [raw]
Subject: Re: [PATCH 3/3 v3] at91/USB: Add USB EHCI driver for at91sam9g45 series

On Tuesday 23 June 2009, Nicolas Ferre wrote:
> Add host USB High speed driver for at91sam9g45 series.
> The host driver is an EHCI with its companion OHCI. EHCI is
> handled by the new ehci-atmel.c whereas the OHCI is always
> handled by ohci-at91.c.
>
> Signed-off-by: Nicolas Ferre <[email protected]>

Acked-by: David Brownell <[email protected]>

.... although a followup patch would be nice to remove
the MODULE_ALIAS() thing now that it's no longer needed.


> ---
> v2 -> v3: split patch to:
> - gadget
> - ohci
> - ehci
>
> drivers/usb/Kconfig | 1 +
> drivers/usb/host/ehci-atmel.c | 232 +++++++++++++++++++++++++++++++++++++++++
> drivers/usb/host/ehci-hcd.c | 5 +
> 3 files changed, 238 insertions(+), 0 deletions(-)
> create mode 100644 drivers/usb/host/ehci-atmel.c
>
> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index c6c816b..3974c9c 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -57,6 +57,7 @@ config USB_ARCH_HAS_EHCI
> default y if PPC_83xx
> default y if SOC_AU1200
> default y if ARCH_IXP4XX
> + default y if ARCH_AT91SAM9G45
> default PCI
>
> # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
> diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
> new file mode 100644
> index 0000000..3095403
> --- /dev/null
> +++ b/drivers/usb/host/ehci-atmel.c
> @@ -0,0 +1,232 @@
> +/*
> + * Driver for EHCI UHP on Atmel chips
> + *
> + * Copyright (C) 2009 Atmel Corporation,
> + * Nicolas Ferre <[email protected]>
> + *
> + * Based on various ehci-*.c drivers
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +
> +/* interface and function clocks */
> +static struct clk *iclk, *fclk;
> +static int clocked;
> +
> +/*-------------------------------------------------------------------------*/
> +
> +static void atmel_start_clock(void)
> +{
> + clk_enable(iclk);
> + clk_enable(fclk);
> + clocked = 1;
> +}
> +
> +static void atmel_stop_clock(void)
> +{
> + clk_disable(fclk);
> + clk_disable(iclk);
> + clocked = 0;
> +}
> +
> +static void atmel_start_ehci(struct platform_device *pdev)
> +{
> + dev_dbg(&pdev->dev, "start\n");
> + atmel_start_clock();
> +}
> +
> +static void atmel_stop_ehci(struct platform_device *pdev)
> +{
> + dev_dbg(&pdev->dev, "stop\n");
> + atmel_stop_clock();
> +}
> +
> +/*-------------------------------------------------------------------------*/
> +
> +static int ehci_atmel_setup(struct usb_hcd *hcd)
> +{
> + struct ehci_hcd *ehci = hcd_to_ehci(hcd);
> + int retval = 0;
> +
> + /* registers start at offset 0x0 */
> + ehci->caps = hcd->regs;
> + ehci->regs = hcd->regs +
> + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
> + dbg_hcs_params(ehci, "reset");
> + dbg_hcc_params(ehci, "reset");
> +
> + /* cache this readonly data; minimize chip reads */
> + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
> +
> + retval = ehci_halt(ehci);
> + if (retval)
> + return retval;
> +
> + /* data structure init */
> + retval = ehci_init(hcd);
> + if (retval)
> + return retval;
> +
> + ehci->sbrn = 0x20;
> +
> + ehci_reset(ehci);
> + ehci_port_power(ehci, 0);
> +
> + return retval;
> +}
> +
> +static const struct hc_driver ehci_atmel_hc_driver = {
> + .description = hcd_name,
> + .product_desc = "Atmel EHCI UHP HS",
> + .hcd_priv_size = sizeof(struct ehci_hcd),
> +
> + /* generic hardware linkage */
> + .irq = ehci_irq,
> + .flags = HCD_MEMORY | HCD_USB2,
> +
> + /* basic lifecycle operations */
> + .reset = ehci_atmel_setup,
> + .start = ehci_run,
> + .stop = ehci_stop,
> + .shutdown = ehci_shutdown,
> +
> + /* managing i/o requests and associated device resources */
> + .urb_enqueue = ehci_urb_enqueue,
> + .urb_dequeue = ehci_urb_dequeue,
> + .endpoint_disable = ehci_endpoint_disable,
> +
> + /* scheduling support */
> + .get_frame_number = ehci_get_frame,
> +
> + /* root hub support */
> + .hub_status_data = ehci_hub_status_data,
> + .hub_control = ehci_hub_control,
> + .bus_suspend = ehci_bus_suspend,
> + .bus_resume = ehci_bus_resume,
> + .relinquish_port = ehci_relinquish_port,
> + .port_handed_over = ehci_port_handed_over,
> +};
> +
> +static int __init ehci_atmel_drv_probe(struct platform_device *pdev)
> +{
> + struct usb_hcd *hcd;
> + const struct hc_driver *driver = &ehci_atmel_hc_driver;
> + struct resource *res;
> + int irq;
> + int retval;
> +
> + if (usb_disabled())
> + return -ENODEV;
> +
> + pr_debug("Initializing Atmel-SoC USB Host Controller\n");
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq <= 0) {
> + dev_err(&pdev->dev,
> + "Found HC with no IRQ. Check %s setup!\n",
> + dev_name(&pdev->dev));
> + retval = -ENODEV;
> + goto fail_create_hcd;
> + }
> +
> + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
> + if (!hcd) {
> + retval = -ENOMEM;
> + goto fail_create_hcd;
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(&pdev->dev,
> + "Found HC with no register addr. Check %s setup!\n",
> + dev_name(&pdev->dev));
> + retval = -ENODEV;
> + goto fail_request_resource;
> + }
> + hcd->rsrc_start = res->start;
> + hcd->rsrc_len = res->end - res->start + 1;
> +
> + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
> + driver->description)) {
> + dev_dbg(&pdev->dev, "controller already in use\n");
> + retval = -EBUSY;
> + goto fail_request_resource;
> + }
> +
> + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
> + if (hcd->regs == NULL) {
> + dev_dbg(&pdev->dev, "error mapping memory\n");
> + retval = -EFAULT;
> + goto fail_ioremap;
> + }
> +
> + iclk = clk_get(&pdev->dev, "ehci_clk");
> + if (IS_ERR(iclk)) {
> + dev_err(&pdev->dev, "Error getting interface clock\n");
> + retval = -ENOENT;
> + goto fail_get_iclk;
> + }
> + fclk = clk_get(&pdev->dev, "uhpck");
> + if (IS_ERR(fclk)) {
> + dev_err(&pdev->dev, "Error getting function clock\n");
> + retval = -ENOENT;
> + goto fail_get_fclk;
> + }
> +
> + atmel_start_ehci(pdev);
> +
> + retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
> + if (retval)
> + goto fail_add_hcd;
> +
> + return retval;
> +
> +fail_add_hcd:
> + atmel_stop_ehci(pdev);
> + clk_put(fclk);
> +fail_get_fclk:
> + clk_put(iclk);
> +fail_get_iclk:
> + iounmap(hcd->regs);
> +fail_ioremap:
> + release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
> +fail_request_resource:
> + usb_put_hcd(hcd);
> +fail_create_hcd:
> + dev_err(&pdev->dev, "init %s fail, %d\n",
> + dev_name(&pdev->dev), retval);
> +
> + return retval;
> +}
> +
> +static int __exit ehci_atmel_drv_remove(struct platform_device *pdev)
> +{
> + struct usb_hcd *hcd = platform_get_drvdata(pdev);
> +
> + ehci_shutdown(hcd);
> + usb_remove_hcd(hcd);
> + iounmap(hcd->regs);
> + release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
> + usb_put_hcd(hcd);
> +
> + atmel_stop_ehci(pdev);
> + clk_put(fclk);
> + clk_put(iclk);
> + fclk = iclk = NULL;
> +
> + return 0;
> +}
> +
> +MODULE_ALIAS("platform:atmel-ehci");
> +
> +static struct platform_driver ehci_atmel_driver = {
> + .probe = ehci_atmel_drv_probe,
> + .remove = __exit_p(ehci_atmel_drv_remove),
> + .shutdown = usb_hcd_platform_shutdown,
> + .driver.name = "atmel-ehci",
> +};
> diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
> index c637207..df2ddee 100644
> --- a/drivers/usb/host/ehci-hcd.c
> +++ b/drivers/usb/host/ehci-hcd.c
> @@ -1072,6 +1072,11 @@ MODULE_LICENSE ("GPL");
> #define PLATFORM_DRIVER ixp4xx_ehci_driver
> #endif
>
> +#ifdef CONFIG_ARCH_AT91
> +#include "ehci-atmel.c"
> +#define PLATFORM_DRIVER ehci_atmel_driver
> +#endif
> +
> #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
> !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER)
> #error "missing bus glue for ehci-hcd"
> --
> 1.5.3.7
>
>


2009-06-26 09:51:11

by Nicolas Ferre

[permalink] [raw]
Subject: [PATCH] at91/USB: Add USB EHCI driver for at91sam9g45 series

Add host USB High speed driver for at91sam9g45 series.
The host driver is an EHCI with its companion OHCI. EHCI is
handled by the new ehci-atmel.c whereas the OHCI is always
handled by ohci-at91.c.

Signed-off-by: Nicolas Ferre <[email protected]>
---
Hi David,

Here is a followup of my previous patch with MODULE_ALIAS() removed.

drivers/usb/Kconfig | 1 +
drivers/usb/host/ehci-atmel.c | 230 +++++++++++++++++++++++++++++++++++++++++
drivers/usb/host/ehci-hcd.c | 5 +
3 files changed, 236 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/host/ehci-atmel.c

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index c6c816b..3974c9c 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -57,6 +57,7 @@ config USB_ARCH_HAS_EHCI
default y if PPC_83xx
default y if SOC_AU1200
default y if ARCH_IXP4XX
+ default y if ARCH_AT91SAM9G45
default PCI

# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
new file mode 100644
index 0000000..87c1b7c
--- /dev/null
+++ b/drivers/usb/host/ehci-atmel.c
@@ -0,0 +1,230 @@
+/*
+ * Driver for EHCI UHP on Atmel chips
+ *
+ * Copyright (C) 2009 Atmel Corporation,
+ * Nicolas Ferre <[email protected]>
+ *
+ * Based on various ehci-*.c drivers
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+/* interface and function clocks */
+static struct clk *iclk, *fclk;
+static int clocked;
+
+/*-------------------------------------------------------------------------*/
+
+static void atmel_start_clock(void)
+{
+ clk_enable(iclk);
+ clk_enable(fclk);
+ clocked = 1;
+}
+
+static void atmel_stop_clock(void)
+{
+ clk_disable(fclk);
+ clk_disable(iclk);
+ clocked = 0;
+}
+
+static void atmel_start_ehci(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "start\n");
+ atmel_start_clock();
+}
+
+static void atmel_stop_ehci(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "stop\n");
+ atmel_stop_clock();
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_atmel_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval = 0;
+
+ /* registers start at offset 0x0 */
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs +
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci->sbrn = 0x20;
+
+ ehci_reset(ehci);
+ ehci_port_power(ehci, 0);
+
+ return retval;
+}
+
+static const struct hc_driver ehci_atmel_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Atmel EHCI UHP HS",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /* generic hardware linkage */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /* basic lifecycle operations */
+ .reset = ehci_atmel_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /* managing i/o requests and associated device resources */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /* scheduling support */
+ .get_frame_number = ehci_get_frame,
+
+ /* root hub support */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+};
+
+static int __init ehci_atmel_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ const struct hc_driver *driver = &ehci_atmel_hc_driver;
+ struct resource *res;
+ int irq;
+ int retval;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_debug("Initializing Atmel-SoC USB Host Controller\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ retval = -ENODEV;
+ goto fail_create_hcd;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto fail_create_hcd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no register addr. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ retval = -ENODEV;
+ goto fail_request_resource;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto fail_request_resource;
+ }
+
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ if (hcd->regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto fail_ioremap;
+ }
+
+ iclk = clk_get(&pdev->dev, "ehci_clk");
+ if (IS_ERR(iclk)) {
+ dev_err(&pdev->dev, "Error getting interface clock\n");
+ retval = -ENOENT;
+ goto fail_get_iclk;
+ }
+ fclk = clk_get(&pdev->dev, "uhpck");
+ if (IS_ERR(fclk)) {
+ dev_err(&pdev->dev, "Error getting function clock\n");
+ retval = -ENOENT;
+ goto fail_get_fclk;
+ }
+
+ atmel_start_ehci(pdev);
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval)
+ goto fail_add_hcd;
+
+ return retval;
+
+fail_add_hcd:
+ atmel_stop_ehci(pdev);
+ clk_put(fclk);
+fail_get_fclk:
+ clk_put(iclk);
+fail_get_iclk:
+ iounmap(hcd->regs);
+fail_ioremap:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+fail_request_resource:
+ usb_put_hcd(hcd);
+fail_create_hcd:
+ dev_err(&pdev->dev, "init %s fail, %d\n",
+ dev_name(&pdev->dev), retval);
+
+ return retval;
+}
+
+static int __exit ehci_atmel_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ ehci_shutdown(hcd);
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ atmel_stop_ehci(pdev);
+ clk_put(fclk);
+ clk_put(iclk);
+ fclk = iclk = NULL;
+
+ return 0;
+}
+
+static struct platform_driver ehci_atmel_driver = {
+ .probe = ehci_atmel_drv_probe,
+ .remove = __exit_p(ehci_atmel_drv_remove),
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver.name = "atmel-ehci",
+};
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index c637207..df2ddee 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1072,6 +1072,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ixp4xx_ehci_driver
#endif

+#ifdef CONFIG_ARCH_AT91
+#include "ehci-atmel.c"
+#define PLATFORM_DRIVER ehci_atmel_driver
+#endif
+
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER)
#error "missing bus glue for ehci-hcd"
--
1.5.3.7

2009-06-26 12:39:14

by Nicolas Ferre

[permalink] [raw]
Subject: Re: [PATCH 1/3 v3] at91/USB: Add USB gadget driver selection for at91sam9g45 series

David Brownell :
> On Tuesday 23 June 2009, Nicolas Ferre wrote:
>> Add gadget USB drivers for at91sam9g45 series. Those SOC include
>> high speed USB interfaces.
>> The gadget driver is the already available atmel_usba_udc.
>>
>> Signed-off-by: Nicolas Ferre <[email protected]>
>
> Acked-by: David Brownell <[email protected]>
>
> SAM9G45 patches aren't in mainine yet, right? So I
> assume these can wait (maybe until 2.6.32-early).

You are right. I submit 9g45 patches to Russell patch tracking system today.

My intention is to be able to sort dependencies in linux-next tree soon.

Best regards,
--
Nicolas Ferre