| /* |
| * 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, |
| }; |
| |
| |