diff options
Diffstat (limited to 'target/linux/brcm47xx/patches-2.6.37/022-USB-Add-ehci-ssb-driver.patch')
| -rw-r--r-- | target/linux/brcm47xx/patches-2.6.37/022-USB-Add-ehci-ssb-driver.patch | 345 | 
1 files changed, 345 insertions, 0 deletions
diff --git a/target/linux/brcm47xx/patches-2.6.37/022-USB-Add-ehci-ssb-driver.patch b/target/linux/brcm47xx/patches-2.6.37/022-USB-Add-ehci-ssb-driver.patch new file mode 100644 index 000000000..6d15783b1 --- /dev/null +++ b/target/linux/brcm47xx/patches-2.6.37/022-USB-Add-ehci-ssb-driver.patch @@ -0,0 +1,345 @@ +From ad224c0d5fa0fc05f8aaef3c19fc9b4eb275a5d2 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens <hauke@hauke-m.de> +Date: Sun, 18 Jul 2010 21:29:40 +0200 +Subject: [PATCH 2/2] USB: Add ehci ssb driver + +Support for the Sonics Silicon Backplane (SSB) attached Broadcom USB EHCI core. + +Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> +--- + drivers/usb/host/Kconfig    |   13 ++ + drivers/usb/host/ehci-hcd.c |   22 ++++- + drivers/usb/host/ehci-ssb.c |  255 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 288 insertions(+), 2 deletions(-) + create mode 100644 drivers/usb/host/ehci-ssb.c + +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -160,6 +160,19 @@ config USB_OXU210HP_HCD + 	  To compile this driver as a module, choose M here: the + 	  module will be called oxu210hp-hcd. +  ++config USB_EHCI_HCD_SSB ++	bool "EHCI support for Broadcom SSB EHCI core" ++	depends on USB_EHCI_HCD && (SSB = y || SSB = USB_EHCI_HCD) && EXPERIMENTAL ++	default n ++	---help--- ++	  Support for the Sonics Silicon Backplane (SSB) attached ++	  Broadcom USB EHCI core. ++ ++	  This device is present in some embedded devices with ++	  Broadcom based SSB bus. ++ ++	  If unsure, say N. ++ + config USB_ISP116X_HCD + 	tristate "ISP116X HCD support" + 	depends on USB +--- a/drivers/usb/host/ehci-hcd.c ++++ b/drivers/usb/host/ehci-hcd.c +@@ -1216,9 +1216,14 @@ MODULE_LICENSE ("GPL"); + #define PLATFORM_DRIVER		ehci_octeon_driver + #endif +  ++#ifdef CONFIG_USB_EHCI_HCD_SSB ++#include "ehci-ssb.c" ++#define SSB_EHCI_DRIVER		ssb_ehci_driver ++#endif ++ + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ +     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ +-    !defined(XILINX_OF_PLATFORM_DRIVER) ++    !defined(XILINX_OF_PLATFORM_DRIVER) && !defined(SSB_EHCI_DRIVER) + #error "missing bus glue for ehci-hcd" + #endif +  +@@ -1278,10 +1283,20 @@ static int __init ehci_hcd_init(void) + 	if (retval < 0) + 		goto clean4; + #endif ++ ++#ifdef SSB_EHCI_DRIVER ++	retval = ssb_driver_register(&SSB_EHCI_DRIVER); ++	if (retval < 0) ++		goto clean5; ++#endif + 	return retval; +  ++#ifdef SSB_EHCI_DRIVER ++	/* ssb_driver_unregister(&SSB_EHCI_DRIVER); */ ++clean5: ++#endif + #ifdef XILINX_OF_PLATFORM_DRIVER +-	/* of_unregister_platform_driver(&XILINX_OF_PLATFORM_DRIVER); */ ++	of_unregister_platform_driver(&XILINX_OF_PLATFORM_DRIVER); + clean4: + #endif + #ifdef OF_PLATFORM_DRIVER +@@ -1312,6 +1327,9 @@ module_init(ehci_hcd_init); +  + static void __exit ehci_hcd_cleanup(void) + { ++#ifdef SSB_EHCI_DRIVER ++	ssb_driver_unregister(&SSB_EHCI_DRIVER); ++#endif + #ifdef XILINX_OF_PLATFORM_DRIVER + 	of_unregister_platform_driver(&XILINX_OF_PLATFORM_DRIVER); + #endif +--- /dev/null ++++ b/drivers/usb/host/ehci-ssb.c +@@ -0,0 +1,255 @@ ++/* ++ * Sonics Silicon Backplane ++ * Broadcom USB-core EHCI driver (SSB bus glue) ++ * ++ * Copyright 2007 Steven Brown <sbrown@cortland.com> ++ * Copyright 2010 Hauke Mehrtens <hauke@hauke-m.de> ++ * ++ * Derived from the OHCI-SSB driver ++ * Copyright 2007 Michael Buesch <mb@bu3sch.de> ++ * ++ * Derived from the EHCI-PCI driver ++ * Copyright (c) 2000-2004 by David Brownell ++ * ++ * Derived from the OHCI-PCI driver ++ * Copyright 1999 Roman Weissgaerber ++ * Copyright 2000-2002 David Brownell ++ * Copyright 1999 Linus Torvalds ++ * Copyright 1999 Gregory P. Smith ++ * ++ * Derived from the USBcore related parts of Broadcom-SB ++ * Copyright 2005 Broadcom Corporation ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++#include <linux/ssb/ssb.h> ++ ++ ++struct ssb_ehci_device { ++	struct ehci_hcd ehci; /* _must_ be at the beginning. */ ++}; ++ ++static inline ++struct ssb_ehci_device *hcd_to_ssb_ehci(struct usb_hcd *hcd) ++{ ++	return (struct ssb_ehci_device *)(hcd->hcd_priv); ++} ++ ++static int ssb_ehci_reset(struct usb_hcd *hcd) ++{ ++	struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++	int err; ++ ++	ehci->caps = hcd->regs; ++	ehci->regs = hcd->regs + ++		HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); ++ ++	dbg_hcs_params(ehci, "reset"); ++	dbg_hcc_params(ehci, "reset"); ++ ++	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); ++ ++	err = ehci_halt(ehci); ++ ++	if (err) ++		return err; ++ ++	err = ehci_init(hcd); ++ ++	if (err) ++		return err; ++ ++	ehci_reset(ehci); ++ ++	return err; ++} ++ ++static const struct hc_driver ssb_ehci_hc_driver = { ++	.description		= "ssb-usb-ehci", ++	.product_desc		= "SSB EHCI Controller", ++	.hcd_priv_size		= sizeof(struct ssb_ehci_device), ++ ++	.irq			= ehci_irq, ++	.flags			= HCD_MEMORY | HCD_USB2, ++ ++	.reset			= ssb_ehci_reset, ++	.start			= ehci_run, ++	.stop			= ehci_stop, ++	.shutdown		= ehci_shutdown, ++ ++	.urb_enqueue		= ehci_urb_enqueue, ++	.urb_dequeue		= ehci_urb_dequeue, ++	.endpoint_disable	= ehci_endpoint_disable, ++	.endpoint_reset		= ehci_endpoint_reset, ++ ++	.get_frame_number	= ehci_get_frame, ++ ++	.hub_status_data	= ehci_hub_status_data, ++	.hub_control		= ehci_hub_control, ++#if defined(CONFIG_PM) ++	.bus_suspend		= ehci_bus_suspend, ++	.bus_resume		= ehci_bus_resume, ++#endif ++	.relinquish_port	= ehci_relinquish_port, ++	.port_handed_over	= ehci_port_handed_over, ++ ++	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, ++}; ++ ++static void ssb_ehci_detach(struct ssb_device *dev) ++{ ++	struct usb_hcd *hcd = ssb_get_drvdata(dev); ++ ++	if (hcd->driver->shutdown) ++		hcd->driver->shutdown(hcd); ++	usb_remove_hcd(hcd); ++	iounmap(hcd->regs); ++	release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++	usb_put_hcd(hcd); ++	ssb_device_disable(dev, 0); ++} ++ ++static int ssb_ehci_attach(struct ssb_device *dev) ++{ ++	struct ssb_ehci_device *ehcidev; ++	struct usb_hcd *hcd; ++	int err = -ENOMEM; ++	u32 tmp; ++ ++	if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) || ++	    dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32))) ++		return -EOPNOTSUPP; ++ ++	/* ++	 * USB 2.0 special considerations: ++	 * ++	 * In addition to the standard SSB reset sequence, the Host Control ++	 * Register must be programmed to bring the USB core and various phy ++	 * components out of reset. ++	 */ ++	ssb_device_enable(dev, 0); ++	ssb_write32(dev, 0x200, 0x7ff); ++ ++	/* Change Flush control reg */ ++	tmp = ssb_read32(dev, 0x400); ++	tmp &= ~8; ++	ssb_write32(dev, 0x400, tmp); ++	tmp = ssb_read32(dev, 0x400); ++ ++	/* Change Shim control reg */ ++	tmp = ssb_read32(dev, 0x304); ++	tmp &= ~0x100; ++	ssb_write32(dev, 0x304, tmp); ++	tmp = ssb_read32(dev, 0x304); ++ ++	udelay(1); ++ ++	/* Work around for 5354 failures */ ++	if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) { ++		/* Change syn01 reg */ ++		tmp = 0x00fe00fe; ++		ssb_write32(dev, 0x894, tmp); ++ ++		/* Change syn03 reg */ ++		tmp = ssb_read32(dev, 0x89c); ++		tmp |= 0x1; ++		ssb_write32(dev, 0x89c, tmp); ++	} ++ ++	hcd = usb_create_hcd(&ssb_ehci_hc_driver, dev->dev, ++			     dev_name(dev->dev)); ++	if (!hcd) ++		goto err_dev_disable; ++ ++	ehcidev = hcd_to_ssb_ehci(hcd); ++	tmp = ssb_read32(dev, SSB_ADMATCH0); ++	hcd->rsrc_start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */ ++	hcd->rsrc_len = 0x100; /* ehci reg block size */ ++	/* ++	 * start & size modified per sbutils.c ++	 */ ++	hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); ++	if (!hcd->regs) ++		goto err_put_hcd; ++	err = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); ++	if (err) ++		goto err_iounmap; ++ ++	ssb_set_drvdata(dev, hcd); ++ ++	return err; ++ ++err_iounmap: ++	iounmap(hcd->regs); ++err_put_hcd: ++	usb_put_hcd(hcd); ++err_dev_disable: ++	ssb_device_disable(dev, 0); ++	return err; ++} ++ ++static int ssb_ehci_probe(struct ssb_device *dev, ++		const struct ssb_device_id *id) ++{ ++	int err; ++	u16 chipid_top; ++ ++	/* USBcores are only connected on embedded devices. */ ++	chipid_top = (dev->bus->chip_id & 0xFF00); ++	if (chipid_top != 0x4700 && chipid_top != 0x5300) ++		return -ENODEV; ++ ++	/* TODO: Probably need checks here; is the core connected? */ ++ ++	if (usb_disabled()) ++		return -ENODEV; ++ ++	err = ssb_ehci_attach(dev); ++ ++	return err; ++} ++ ++static void ssb_ehci_remove(struct ssb_device *dev) ++{ ++	ssb_ehci_detach(dev); ++} ++ ++#ifdef CONFIG_PM ++ ++static int ssb_ehci_suspend(struct ssb_device *dev, pm_message_t state) ++{ ++	ssb_device_disable(dev, 0); ++ ++	return 0; ++} ++ ++static int ssb_ehci_resume(struct ssb_device *dev) ++{ ++	struct usb_hcd *hcd = ssb_get_drvdata(dev); ++	struct ssb_ehci_device *ehcidev = hcd_to_ssb_ehci(hcd); ++ ++	ssb_device_enable(dev, 0); ++ ++	ehci_finish_controller_resume(hcd); ++	return 0; ++} ++ ++#else /* !CONFIG_PM */ ++#define ssb_ehci_suspend	NULL ++#define ssb_ehci_resume	NULL ++#endif /* CONFIG_PM */ ++ ++static const struct ssb_device_id ssb_ehci_table[] = { ++	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), ++	SSB_DEVTABLE_END ++}; ++MODULE_DEVICE_TABLE(ssb, ssb_ehci_table); ++ ++static struct ssb_driver ssb_ehci_driver = { ++	.name		= KBUILD_MODNAME, ++	.id_table	= ssb_ehci_table, ++	.probe		= ssb_ehci_probe, ++	.remove		= ssb_ehci_remove, ++	.suspend	= ssb_ehci_suspend, ++	.resume		= ssb_ehci_resume, ++};  | 
