blob: 856fb644a7ba27d5df56c29d29b8d37c4fd3f687 [file] [log] [blame]
/*
* Copyright (C) 2010 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/usb.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/clk.h>
#include <linux/version.h>
#include <linux/module.h>
#include <asm/brcmstb/brcmstb.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
#include <linux/usb/hcd.h>
#else
#include "../core/hcd.h"
#endif
#define MAX_HCD 8
static struct clk *usb_clk;
int brcm_usb_probe(struct platform_device *pdev, char *hcd_name,
const struct hc_driver *hc_driver)
{
struct resource *res = NULL;
struct usb_hcd *hcd = NULL;
int irq, ret, len;
if (usb_disabled())
return -ENODEV;
if (!usb_clk)
usb_clk = clk_get(NULL, "usb");
clk_enable(usb_clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
err("platform_get_resource error.");
return -ENODEV;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
err("platform_get_irq error.");
return -ENODEV;
}
/* initialize hcd */
hcd = usb_create_hcd(hc_driver, &pdev->dev, (char *)hcd_name);
if (!hcd) {
err("Failed to create hcd");
return -ENOMEM;
}
len = res->end - res->start + 1;
hcd->regs = ioremap(res->start, len);
hcd->rsrc_start = res->start;
hcd->rsrc_len = len;
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
if (ret != 0) {
err("Failed to add hcd");
iounmap(hcd->regs);
usb_put_hcd(hcd);
clk_disable(usb_clk);
return ret;
}
#ifdef CONFIG_PM
/* disable autosuspend by default to preserve
* original behavior
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
usb_disable_autosuspend(hcd->self.root_hub);
#else
hcd->self.root_hub->autosuspend_disabled = 1;
#endif
#endif
return ret;
}
EXPORT_SYMBOL(brcm_usb_probe);
int brcm_usb_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
clk_disable(usb_clk);
usb_remove_hcd(hcd);
iounmap(hcd->regs);
usb_put_hcd(hcd);
return 0;
}
EXPORT_SYMBOL(brcm_usb_remove);
void brcm_usb_suspend(struct usb_hcd *hcd)
{
/* Since all HCs share clock source, once we enable USB clock, all
controllers are capable to generate interrupts if enabled. Since some
controllers at this time are still marked as non-accessible, this
leads to spurious interrupts.
To avoid this, disable controller interrupts.
*/
disable_irq(hcd->irq);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
clk_disable(usb_clk);
}
EXPORT_SYMBOL(brcm_usb_suspend);
void brcm_usb_resume(struct usb_hcd *hcd)
{
clk_enable(usb_clk);
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
enable_irq(hcd->irq);
}
EXPORT_SYMBOL(brcm_usb_resume);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Broadcom Corporation");
MODULE_DESCRIPTION("Broadcom USB common functions");