blob: 587c4a33c4eb693b81f38f7a0b2a1216fd42f1da [file] [log] [blame]
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* 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.
*
* 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
*/
/*
* EHCI Host Controller Driver
*
* Atheros USB Controller
*
*/
#include "../gadget/ath_defs.h"
#ifdef CONFIG_USB_ATH_OTG_MODULE
#warning "OTG enabled in host"
#define CONFIG_USB_ATH_OTG
#endif
static inline void ath_usb_host_reset(u32 ath_host_to_reset)
{
ath_usb_reg_rmw_set(ath_host_to_reset,
ATH_RESET_USBSUS_OVRIDE |
ATH_RESET_USB_PHY_ANALOG);
mdelay(10);
ath_usb_reg_wr(ath_host_to_reset,
((ath_usb_reg_rd(ath_host_to_reset) & ~(ATH_USB_RESET_USB_HOST))
| ATH_RESET_USBSUS_OVRIDE));
mdelay(10);
ath_usb_reg_wr(ath_host_to_reset,
((ath_usb_reg_rd(ath_host_to_reset) &
~(ATH_USB_RESET_USB_PHY | ATH_RESET_USB_PHY_ANALOG)) |
ATH_RESET_USBSUS_OVRIDE));
mdelay(10);
return;
}
#ifdef CONFIG_USB_ATH_OTG
static int ath_usb_start_hc(struct ehci_hcd *ehci, struct device *dev)
{
printk("%s: %p, %p\n", __func__, ehci_to_hcd(ehci),
&ehci_to_hcd(ehci)->self);
if (ehci->transceiver) {
int status = otg_set_host(ehci->transceiver,
&ehci_to_hcd(ehci)->self);
dev_info(dev, "init %s transceiver, status %d\n",
ehci->transceiver->label, status);
if (status) {
if (ehci->transceiver)
put_device(ehci->transceiver->dev);
}
return status;
} else {
dev_err(dev, "can't find transceiver\n");
return -ENODEV;
}
}
#endif
#ifdef CONFIG_USB_OTG
void start_hnp(struct ehci_hcd *ehci)
{
unsigned long flags;
otg_start_hnp(ehci->transceiver);
local_irq_save(flags);
ehci->transceiver->state = OTG_STATE_A_SUSPEND;
local_irq_restore(flags);
}
#endif
static int ath_usb_ehci_init(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int ret;
ath_usb_debug_fn("__enter %s\n", __FUNCTION__);
/*
* EHCI Capability Register offset at 0x100 - Info from ChipIdea
* EHCI Operational Register offset at 0x140 - Info from ChipIdea
*/
ehci->caps = hcd->regs + 0x100; /* Device/Host Capa Reg */
ehci->regs = hcd->regs + 0x100 + /* Device/Host Oper Reg */
HC_LENGTH(readl(&ehci->caps->hc_capbase));
/*Reading HC Structural Parameters */
ehci->hcs_params = readl(&ehci->caps->hcs_params);
ath_usb_debug_dev("HCS Params %x\n", ehci->hcs_params);
ath_usb_debug_dev("HCC Params %x\n", readl(&ehci->caps->hcc_params));
ret = ehci_init(hcd);
if (ret) {
return ret;
}
/*
* Informs USB Subsystem abt embedded TT.
* Sets to host mode
*/
hcd->has_tt = 1;
ehci->sbrn = 0x20;
ehci_reset(ehci);
return ret;
}
static struct hc_driver ath_usb_ehci_driver = {
.description = hcd_name,
.product_desc = "ATH EHCI",
.hcd_priv_size = sizeof(struct ehci_hcd),
/*
* generic hardware linkage
*/
#ifndef CONFIG_USB_ATH_OTG
.irq = ehci_irq,
#endif
.flags = HCD_MEMORY | HCD_USB2,
/*
* basic lifecycle operations
*/
.reset = ath_usb_ehci_init,
.start = ehci_run,
#ifdef CONFIG_PM
.suspend = ehci_bus_suspend,
.resume = ehci_bus_resume,
#endif
.stop = ehci_stop,
/*
* 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,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
#ifndef CONFIG_USB_ATH_OTG
int ath_usb_ehci_probe(struct hc_driver *driver,
struct usb_hcd **hcd_out,
struct platform_device *pdev)
{
struct usb_hcd *hcd;
int ret;
struct ehci_hcd *ehci;
ath_usb_debug_dev("No of Resources %d \n", pdev->num_resources);
/* Verify the Host Mode of the Driver */
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
printk("resource[1] is not IORESOURCE_IRQ");
ret = -ENOMEM;
}
hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus->name);
if (!hcd) {
ret = -ENOMEM;
goto err;
}
hcd->rsrc_start = pdev->resource[0].start;
hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
driver->description)) {
dev_dbg(&pdev->dev, "controller already in use\n");
ret = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (hcd->regs == NULL) {
dev_dbg(&pdev->dev, "error mapping memory \n");
ret = -EFAULT;
goto err2;
}
/*
* EHCI Capability Register offset at 0x100 - Info from ChipIdea
* EHCI Operational Register offset at 0x140 - Info from ChipIdea
*/
ehci = hcd_to_ehci(hcd);
ehci->caps = hcd->regs + 0x100; /* Device/Host Capa Reg */
ehci->regs = hcd->regs + 0x140; /* Device/Host Oper Reg */
ath_usb_debug_dev("hcd->regs %p \n", hcd->regs);
ath_usb_debug_dev("Host Capability Reg %p \n", ehci->caps);
ath_usb_debug_dev("Host Operational Reg %p \n", ehci->regs);
/* Added 5_29_07 */
#ifdef CONFIG_MACH_AR933x
ath_usb_reg_rmw_set(ATH_USB_RESET, ATH_RESET_USBSUS_OVRIDE);
mdelay(10);
ath_usb_reg_wr(ATH_USB_RESET,
((ath_usb_reg_rd(ATH_USB_RESET) & ~(ATH_USB_RESET_USB_HOST))
| ATH_RESET_USBSUS_OVRIDE));
mdelay(10);
ath_usb_reg_wr(ATH_USB_RESET,
((ath_usb_reg_rd(ATH_USB_RESET) & ~(ATH_USB_RESET_USB_PHY)) |
ATH_RESET_USBSUS_OVRIDE));
mdelay(10);
#elif defined(CONFIG_MACH_AR934x)
if (is_ar934x_13_or_later()) {
ath_reg_rmw_clear(PHY_CTRL1_ADDRESS, (PHY_CTRL1_USE_PLL_LOCKDETECT_MASK |
PHY_CTRL1_RX_LOWR_PDET_MASK));
}
/* USB Soft Reset Sequence - Power Down the USB PHY PLL */
ath_usb_reg_rmw_set(ATH_USB_RESET,
ATH_RESET_USBSUS_OVRIDE | ATH_RESET_USB_PHY_ANALOG |
ATH_RESET_USB_PHY_PLL_PWD_EXT);
mdelay(10);
/* USB Soft Reset Sequence - Power Up the USB PHY PLL */
ath_usb_reg_wr(ATH_USB_RESET,(ath_usb_reg_rd(ATH_USB_RESET) & ~(ATH_RESET_USB_PHY_PLL_PWD_EXT)));
mdelay(10);
ath_usb_reg_wr(ATH_USB_RESET,
((ath_usb_reg_rd(ATH_USB_RESET) & ~(ATH_USB_RESET_USB_HOST))
| ATH_RESET_USBSUS_OVRIDE));
mdelay(10);
ath_usb_reg_wr(ATH_USB_RESET,
((ath_usb_reg_rd(ATH_USB_RESET) &
~(ATH_USB_RESET_USB_PHY | ATH_RESET_USB_PHY_ANALOG)) |
ATH_RESET_USBSUS_OVRIDE));
mdelay(10);
#elif defined(CONFIG_MACH_QCA955x) || defined(CONFIG_MACH_QCA953x) || defined(CONFIG_MACH_QCA956x)
//
// This WAR is not needed for Scorpion (now). Will uncomment
// this if necessary
//
//if(pdev->id == ATH_USB_HOST0) {
// ath_reg_rmw_clear(USB_PHY_CTRL1_ADDRESS, (USB_PHY_CTRL1_USE_PLL_LOCKDETECT_MASK |
// USB_PHY_CTRL1_RX_LOWR_PDET_MASK));
// ath_usb_host_reset(ATH_USB_RESET);
//} else if(pdev->id == ATH_USB_HOST1) {
// ath_reg_rmw_clear(USB2_PHY_CTRL1_ADDRESS, (USB2_PHY_CTRL1_USE_PLL_LOCKDETECT_MASK |
// USB2_PHY_CTRL1_RX_LOWR_PDET_MASK));
// ath_usb_host_reset(ATH_USB2_RESET);
//} else {
// dev_dbg(&pdev->id, "error in host selection\n");
//}
#else
#error :Host selection need to be defined.
#endif
/*
* Reset host controller
*/
printk("Port Status %x \n", readl(&ehci->regs->port_status[0]));
ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
if (ret != 0) {
goto err3;
}
return ret;
err3:
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
err:
dev_err(&pdev->dev, "init %s fail, %d \n", pdev->dev.bus->name, ret);
return ret;
}
static int ath_usb_ehci_drv_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
if (usb_disabled()) {
ath_usb_error("USB_DISABLED \n");
return -ENODEV;
}
return ath_usb_ehci_probe(&ath_usb_ehci_driver, &hcd, pdev);
}
static int ath_usb_ehci_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
return 0;
}
static struct platform_driver ath_usb_ehci_hcd_driver = {
.probe = ath_usb_ehci_drv_probe,
.remove = ath_usb_ehci_drv_remove,
.driver = {
.name = "ath-ehci",
},
};
#if defined(CONFIG_MACH_QCA955x)||defined(CONFIG_MACH_QCA956x)
static struct platform_driver ath_usb_ehci_hcd_driver_1 = {
.probe = ath_usb_ehci_drv_probe,
.remove = ath_usb_ehci_drv_remove,
.driver = {
.name = "ath-ehci1",
.mod_name = "ath-ehci-host2",
},
};
#endif /* CONFIG_MACH_QCA955x CONFIG_MACH_QCA956x*/
#else
int usb_otg_ath_usb_probe(struct hc_driver *driver)
{
struct ath_usb_otg *ath_usb_otg;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
struct device *dev;
int ret;
printk(" %s \n", __func__);
ath_usb_otg = ath_usb_get_otg();
if (ath_usb_otg == NULL) {
return -EINVAL;
}
dev = ath_usb_otg->dev;
hcd = usb_create_hcd(driver, dev, dev->bus->name);
if (!hcd) {
ret = -ENOMEM;
goto err;
}
hcd->rsrc_start = 0;
hcd->rsrc_len = 0;
hcd->regs = ath_usb_otg->reg_base;
if (hcd->regs == NULL) {
dev_dbg(dev, "error mapping memory \n");
ret = -EFAULT;
goto err1;
}
/*
* EHCI Capability Register offset at 0x100 - Info from ChipIdea
* EHCI Operational Register offset at 0x140 - Info from ChipIdea
*/
ehci = hcd_to_ehci(hcd);
ehci->caps = hcd->regs + 0x100; /* Device/Host Capa Reg */
ehci->regs = hcd->regs + 0x140; /* Device/Host Oper Reg */
ath_usb_otg->ehci = ehci; /* Temp To Test HNP */
printk("hcd->regs %p, %p \n", hcd, hcd->regs);
printk("Host Capability Reg %p \n", ehci->caps);
printk("Host Operational Reg %p \n", ehci->regs);
ehci->transceiver = &ath_usb_otg->otg;
printk("usb_add_hcd\n");
ret = usb_add_hcd(hcd, 0, 0);
if (ret != 0) {
goto err1;
}
dev_set_drvdata(dev, hcd);
ehci_hc_ath_usb_driver.irq = ehci_irq;
ret = ath_usb_start_hc(ehci, dev);
if (ret != 0) {
goto err1;
}
return ret;
err1:
usb_put_hcd(hcd);
err:
dev_err(dev, "init %s fail, %d \n", dev->bus_id, ret);
return ret;
}
#endif