#include <asm/mach-types.h>
#include <asm/hardware.h>
+#include <asm/gpio.h>
+
#include <asm/arch/board.h>
+#include <asm/arch/cpu.h>
#ifndef CONFIG_ARCH_AT91
#error "CONFIG_ARCH_AT91 must be defined."
#endif
-/* interface and function clocks */
-static struct clk *iclk, *fclk;
+/* interface and function clocks; sometimes also an AHB clock */
+static struct clk *iclk, *fclk, *hclk;
static int clocked;
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
+static void at91_start_clock(void)
+{
+ if (cpu_is_at91sam9261())
+ clk_enable(hclk);
+ clk_enable(iclk);
+ clk_enable(fclk);
+ clocked = 1;
+}
+
+static void at91_stop_clock(void)
+{
+ clk_disable(fclk);
+ clk_disable(iclk);
+ if (cpu_is_at91sam9261())
+ clk_disable(hclk);
+ clocked = 0;
+}
+
static void at91_start_hc(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
/*
* Start the USB clocks.
*/
- clk_enable(iclk);
- clk_enable(fclk);
- clocked = 1;
+ at91_start_clock();
/*
* The USB host controller must remain in reset.
/*
* Stop the USB clocks.
*/
- clk_disable(fclk);
- clk_disable(iclk);
- clocked = 0;
+ at91_stop_clock();
}
iclk = clk_get(&pdev->dev, "ohci_clk");
fclk = clk_get(&pdev->dev, "uhpck");
+ if (cpu_is_at91sam9261())
+ hclk = clk_get(&pdev->dev, "hck0");
at91_start_hc(pdev);
ohci_hcd_init(hcd_to_ohci(hcd));
/* Error handling */
at91_stop_hc(pdev);
+ if (cpu_is_at91sam9261())
+ clk_put(hclk);
clk_put(fclk);
clk_put(iclk);
at91_stop_hc(pdev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
- disable_irq_wake(hcd->irq);
+ if (cpu_is_at91sam9261())
+ clk_put(hclk);
clk_put(fclk);
clk_put(iclk);
- fclk = iclk = NULL;
+ fclk = iclk = hclk = NULL;
dev_set_drvdata(&pdev->dev, NULL);
return 0;
static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
{
+ struct at91_usbh_data *pdata = pdev->dev.platform_data;
+ int i;
+
+ if (pdata) {
+ /* REVISIT make the driver support per-port power switching,
+ * and also overcurrent detection. Here we assume the ports
+ * are always powered while this driver is active, and use
+ * active-low power switches.
+ */
+ for (i = 0; i < pdata->ports; i++) {
+ if (pdata->vbus_pin[i] <= 0)
+ continue;
+ gpio_request(pdata->vbus_pin[i], "ohci_vbus");
+ gpio_direction_output(pdata->vbus_pin[i], 0);
+ }
+ }
+
device_init_wakeup(&pdev->dev, 1);
return usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev);
}
static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
{
+ struct at91_usbh_data *pdata = pdev->dev.platform_data;
+ int i;
+
+ if (pdata) {
+ for (i = 0; i < pdata->ports; i++) {
+ if (pdata->vbus_pin[i] <= 0)
+ continue;
+ gpio_direction_output(pdata->vbus_pin[i], 1);
+ gpio_free(pdata->vbus_pin[i]);
+ }
+ }
+
device_init_wakeup(&pdev->dev, 0);
return usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev);
}
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(hcd->irq);
- else
- disable_irq_wake(hcd->irq);
/*
* The integrated transceivers seem unable to notice disconnect,
*/
if (at91_suspend_entering_slow_clock()) {
ohci_usb_reset (ohci);
- clk_disable(fclk);
- clk_disable(iclk);
- clocked = 0;
+ at91_stop_clock();
}
return 0;
static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
{
- if (!clocked) {
- clk_enable(iclk);
- clk_enable(fclk);
- clocked = 1;
- }
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(hcd->irq);
+
+ if (!clocked)
+ at91_start_clock();
return 0;
}
#define ohci_hcd_at91_drv_resume NULL
#endif
-MODULE_ALIAS("at91_ohci");
+MODULE_ALIAS("platform:at91_ohci");
static struct platform_driver ohci_hcd_at91_driver = {
.probe = ohci_hcd_at91_drv_probe,
.owner = THIS_MODULE,
},
};
-
-static int __init ohci_hcd_at91_init (void)
-{
- if (usb_disabled())
- return -ENODEV;
-
- return platform_driver_register(&ohci_hcd_at91_driver);
-}
-
-static void __exit ohci_hcd_at91_cleanup (void)
-{
- platform_driver_unregister(&ohci_hcd_at91_driver);
-}
-
-module_init (ohci_hcd_at91_init);
-module_exit (ohci_hcd_at91_cleanup);