blob: 0b84df17b0796726bddeb082ed9c32db7e2e0012 [file] [log] [blame]
/*
* Copyright (c) 2000-2002 by Dima Epshtein
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* #define DRIVER_AUTHOR "Dima Epshtein" */
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "ehci.h"
/* Special bits in Port status regsiter */
#define PORT_SPEED_OFFS 26
#define PORT_SPEED_MASK 0x3
#define PORT_SPEED_FULL 0
#define PORT_SPEED_LOW 1
#define PORT_SPEED_HIGH 2
static int ehci_marvell_setup(struct usb_hcd *hcd);
void ehci_marvell_port_status_changed(struct ehci_hcd *ehci)
{
/* GL USB-19:USB Configuration for LS Eye Pattern Test - DD and KW */
#if defined(CONFIG_ARCH_FEROCEON_KW2) || defined(CONFIG_ARCH_FEROCEON_KW) || defined(CONFIG_ARCH_FEROCEON_MV78XX0)
u32 __iomem *reg_ptr;
u32 port_status, phy_val;
/* Change PHY Tx Control Register: offset 0x420 = 0x140 + 0x2e0 */
reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + 0x2e0);
phy_val = ehci_readl(ehci, reg_ptr);
port_status = ehci_readl(ehci, &ehci->regs->port_status[0]);
if( (port_status & PORT_CONNECT) &&
(((port_status >> PORT_SPEED_OFFS) & PORT_SPEED_MASK) == PORT_SPEED_LOW) )
{
/* If Low speed device is connected - disable autocalibration */
/* bits[27-30] = 0, bit[26] = 1 */
phy_val &= ~(0xF << 27);
phy_val |= (1 << 26);
}
else
{
/* Nothing connected or FS/HS devices connected - enable autocalibration */
phy_val &= ~(1 << 26);
}
ehci_writel(ehci, phy_val, reg_ptr);
#endif /* CONFIG_ARCH_FEROCEON_KW2 || CONFIG_ARCH_FEROCEON_KW || CONFIG_ARCH_FEROCEON_MV78XX0 */
}
static const struct hc_driver ehci_marvell_hc_driver = {
.description = hcd_name,
.product_desc = "Marvell Orion EHCI",
.hcd_priv_size = sizeof(struct ehci_hcd),
/*
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_USB2,
/*
* basic lifecycle operations
*/
.reset = ehci_marvell_setup,
.start = ehci_run,
#ifdef CONFIG_PM
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
#endif
.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,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
static int ehci_marvell_setup(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int retval;
/*
* registers start at offset
*/
ehci->caps = hcd->regs;
ehci->regs = hcd->regs +
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
/*
* 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;
hcd->has_tt = 1;
ehci->sbrn = 0x20;
ehci_reset(ehci);
ehci_port_power(ehci, 0);
return retval;
}
static int ehci_marvell_probe(struct platform_device *pdev)
{
int i, retval;
struct usb_hcd *hcd = NULL;
hcd = usb_create_hcd (&ehci_marvell_hc_driver, &pdev->dev, dev_name(&pdev->dev));
if (hcd == NULL)
{
printk("%s: hcd_alloc failed\n", __FUNCTION__);
return -ENOMEM;
}
for(i=0; i<pdev->num_resources; i++)
{
if(pdev->resource[i].flags == IORESOURCE_IRQ)
{
hcd->irq = pdev->resource[i].start;
}
else if(pdev->resource[i].flags == IORESOURCE_DMA)
{
hcd->regs = (void *)pdev->resource[i].start;
hcd->rsrc_start = pdev->resource[i].start;
hcd->rsrc_len = pdev->resource[i].end - hcd->rsrc_start + 1;
}
}
retval = usb_add_hcd (hcd, hcd->irq, IRQF_SHARED);
if (retval != 0)
{
printk("%s: usb_add_hcd failed, retval=0x%x\n", __FUNCTION__, retval);
return -ENOMEM;
}
return 0;
}
static int ehci_marvell_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
printk("USB: ehci_marvell_remove\n");
usb_remove_hcd (hcd);
usb_put_hcd (hcd);
return 0;
}
static struct platform_driver ehci_marvell_driver =
{
.driver.name = "ehci_marvell",
.probe = ehci_marvell_probe,
.remove = ehci_marvell_remove,
.shutdown = usb_hcd_platform_shutdown,
};