diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2012-05-09 17:58:14 (GMT) |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2012-07-18 20:40:29 (GMT) |
commit | 47fc28bff82a4dd5f6b41c97e335d10fc78a8e9a (patch) | |
tree | 829b845956f9f5f6b2997424fa1d6d72244b0d1b /drivers/usb/host | |
parent | d1cc1732cc62034542b042a4506d7c5043bc5e5e (diff) | |
download | linux-47fc28bff82a4dd5f6b41c97e335d10fc78a8e9a.tar.xz |
usb: add host support for the tilegx architecture
This change adds OHCI and EHCI support for the tilegx's on-chip
USB hardware.
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ehci-tilegx.c | 214 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ohci-tilegx.c | 203 |
4 files changed, 427 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 800be38..1d9401e 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1349,6 +1349,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_msm_driver #endif +#ifdef CONFIG_TILE_USB +#include "ehci-tilegx.c" +#define PLATFORM_DRIVER ehci_hcd_tilegx_driver +#endif + #ifdef CONFIG_USB_EHCI_HCD_PMC_MSP #include "ehci-pmcmsp.c" #define PLATFORM_DRIVER ehci_hcd_msp_driver diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c new file mode 100644 index 0000000..1d215cd --- /dev/null +++ b/drivers/usb/host/ehci-tilegx.c @@ -0,0 +1,214 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + * 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + */ + +/* + * Tilera TILE-Gx USB EHCI host controller driver. + */ + +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/usb/tilegx.h> +#include <linux/usb.h> + +#include <asm/homecache.h> + +#include <gxio/iorpc_usb_host.h> +#include <gxio/usb_host.h> + +static void tilegx_start_ehc(void) +{ +} + +static void tilegx_stop_ehc(void) +{ +} + +static int tilegx_ehci_setup(struct usb_hcd *hcd) +{ + int ret = ehci_init(hcd); + + /* + * Some drivers do: + * + * struct ehci_hcd *ehci = hcd_to_ehci(hcd); + * ehci->need_io_watchdog = 0; + * + * here, but since this is a new driver we're going to leave the + * watchdog enabled. Later we may try to turn it off and see + * whether we run into any problems. + */ + + return ret; +} + +static const struct hc_driver ehci_tilegx_hc_driver = { + .description = hcd_name, + .product_desc = "Tile-Gx EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * Generic hardware linkage. + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * Basic lifecycle operations. + */ + .reset = tilegx_ehci_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, + .endpoint_reset = ehci_endpoint_reset, + + /* + * 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, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int ehci_hcd_tilegx_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; + pte_t pte = { 0 }; + int my_cpu = smp_processor_id(); + int ret; + + if (usb_disabled()) + return -ENODEV; + + /* + * Try to initialize our GXIO context; if we can't, the device + * doesn't exist. + */ + if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 1) != 0) + return -ENXIO; + + hcd = usb_create_hcd(&ehci_tilegx_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) + return -ENOMEM; + + /* + * We don't use rsrc_start to map in our registers, but seems like + * we ought to set it to something, so we use the register VA. + */ + hcd->rsrc_start = + (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx); + hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx); + hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx); + + tilegx_start_ehc(); + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs; + ehci->regs = + hcd->regs + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = readl(&ehci->caps->hcs_params); + + /* Create our IRQs and register them. */ + pdata->irq = create_irq(); + if (pdata->irq < 0) { + ret = -ENXIO; + goto err_no_irq; + } + + tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU); + + /* Configure interrupts. */ + ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx, + cpu_x(my_cpu), cpu_y(my_cpu), + KERNEL_PL, pdata->irq); + if (ret) { + ret = -ENXIO; + goto err_have_irq; + } + + /* Register all of our memory. */ + pte = pte_set_home(pte, PAGE_HOME_HASH); + ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0); + if (ret) { + ret = -ENXIO; + goto err_have_irq; + } + + ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); + if (ret == 0) { + platform_set_drvdata(pdev, hcd); + return ret; + } + +err_have_irq: + destroy_irq(pdata->irq); +err_no_irq: + tilegx_stop_ehc(); + usb_put_hcd(hcd); + gxio_usb_host_destroy(&pdata->usb_ctx); + return ret; +} + +static int ehci_hcd_tilegx_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + tilegx_stop_ehc(); + gxio_usb_host_destroy(&pdata->usb_ctx); + destroy_irq(pdata->irq); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void ehci_hcd_tilegx_drv_shutdown(struct platform_device *pdev) +{ + usb_hcd_platform_shutdown(pdev); + ehci_hcd_tilegx_drv_remove(pdev); +} + +static struct platform_driver ehci_hcd_tilegx_driver = { + .probe = ehci_hcd_tilegx_drv_probe, + .remove = ehci_hcd_tilegx_drv_remove, + .shutdown = ehci_hcd_tilegx_drv_shutdown, + .driver = { + .name = "tilegx-ehci", + .owner = THIS_MODULE, + } +}; + +MODULE_ALIAS("platform:tilegx-ehci"); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index e0adf5c..2b1e8d8 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1100,6 +1100,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_octeon_driver #endif +#ifdef CONFIG_TILE_USB +#include "ohci-tilegx.c" +#define PLATFORM_DRIVER ohci_hcd_tilegx_driver +#endif + #ifdef CONFIG_USB_CNS3XXX_OHCI #include "ohci-cns3xxx.c" #define PLATFORM_DRIVER ohci_hcd_cns3xxx_driver diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c new file mode 100644 index 0000000..1ae7b28 --- /dev/null +++ b/drivers/usb/host/ohci-tilegx.c @@ -0,0 +1,203 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + * 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + */ + +/* + * Tilera TILE-Gx USB OHCI host controller driver. + */ + +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/usb/tilegx.h> +#include <linux/usb.h> + +#include <asm/homecache.h> + +#include <gxio/iorpc_usb_host.h> +#include <gxio/usb_host.h> + +static void tilegx_start_ohc(void) +{ +} + +static void tilegx_stop_ohc(void) +{ +} + +static int tilegx_ohci_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + ret = ohci_init(ohci); + if (ret < 0) + return ret; + + ret = ohci_run(ohci); + if (ret < 0) { + dev_err(hcd->self.controller, "can't start %s\n", + hcd->self.bus_name); + ohci_stop(hcd); + return ret; + } + + return 0; +} + +static const struct hc_driver ohci_tilegx_hc_driver = { + .description = hcd_name, + .product_desc = "Tile-Gx OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * Generic hardware linkage. + */ + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_LOCAL_MEM | HCD_USB11, + + /* + * Basic lifecycle operations. + */ + .start = tilegx_ohci_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + /* + * Managing I/O requests and associated device resources. + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * Scheduling support. + */ + .get_frame_number = ohci_get_frame, + + /* + * Root hub support. + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + .start_port_reset = ohci_start_port_reset, +}; + +static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; + pte_t pte = { 0 }; + int my_cpu = smp_processor_id(); + int ret; + + if (usb_disabled()) + return -ENODEV; + + /* + * Try to initialize our GXIO context; if we can't, the device + * doesn't exist. + */ + if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 0) != 0) + return -ENXIO; + + hcd = usb_create_hcd(&ohci_tilegx_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) + return -ENOMEM; + + /* + * We don't use rsrc_start to map in our registers, but seems like + * we ought to set it to something, so we use the register VA. + */ + hcd->rsrc_start = + (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx); + hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx); + hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx); + + tilegx_start_ohc(); + + /* Create our IRQs and register them. */ + pdata->irq = create_irq(); + if (pdata->irq < 0) { + ret = -ENXIO; + goto err_no_irq; + } + + tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU); + + /* Configure interrupts. */ + ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx, + cpu_x(my_cpu), cpu_y(my_cpu), + KERNEL_PL, pdata->irq); + if (ret) { + ret = -ENXIO; + goto err_have_irq; + } + + /* Register all of our memory. */ + pte = pte_set_home(pte, PAGE_HOME_HASH); + ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0); + if (ret) { + ret = -ENXIO; + goto err_have_irq; + } + + ohci_hcd_init(hcd_to_ohci(hcd)); + + ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); + if (ret == 0) { + platform_set_drvdata(pdev, hcd); + return ret; + } + +err_have_irq: + destroy_irq(pdata->irq); +err_no_irq: + tilegx_stop_ohc(); + usb_put_hcd(hcd); + gxio_usb_host_destroy(&pdata->usb_ctx); + return ret; +} + +static int ohci_hcd_tilegx_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct tilegx_usb_platform_data* pdata = pdev->dev.platform_data; + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + tilegx_stop_ohc(); + gxio_usb_host_destroy(&pdata->usb_ctx); + destroy_irq(pdata->irq); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void ohci_hcd_tilegx_drv_shutdown(struct platform_device *pdev) +{ + usb_hcd_platform_shutdown(pdev); + ohci_hcd_tilegx_drv_remove(pdev); +} + +static struct platform_driver ohci_hcd_tilegx_driver = { + .probe = ohci_hcd_tilegx_drv_probe, + .remove = ohci_hcd_tilegx_drv_remove, + .shutdown = ohci_hcd_tilegx_drv_shutdown, + .driver = { + .name = "tilegx-ohci", + .owner = THIS_MODULE, + } +}; + +MODULE_ALIAS("platform:tilegx-ohci"); |