Return-path: Received: from static-ip-62-75-166-246.inaddr.intergenia.de ([62.75.166.246]:50064 "EHLO vs166246.vserver.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752621AbXGLJDR (ORCPT ); Thu, 12 Jul 2007 05:03:17 -0400 Message-Id: <20070712085744.961584000@bu3sch.de> References: <20070712085432.528319000@bu3sch.de> Date: Thu, 12 Jul 2007 10:54:34 +0200 From: mb@bu3sch.de To: Andrew Morton Cc: John Linville , Aurelien Jarno , linux-wireless@vger.kernel.org, Gary Zambrano Subject: [patch 2/2] ssb: Add a driver for the Broadcom OHCI core Sender: linux-wireless-owner@vger.kernel.org List-ID: This adds a driver for the Broadcom USB OHCI HCD device. These devices are found in various embedded Broadcom-47xx machines. Signed-off-by: Michael Buesch Index: linux-2.6/drivers/usb/host/ohci-ssb.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6/drivers/usb/host/ohci-ssb.c 2007-07-12 10:52:45.000000000 +0200 @@ -0,0 +1,254 @@ +/* + * Sonics Silicon Backplane + * Broadcom USB-core OHCI driver + * + * Copyright 2007 Michael Buesch + * + * 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 + + +#define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29) + +struct ssb_ohci_device { + struct ohci_hcd ohci; /* _must_ be at the beginning. */ + + u32 enable_flags; +}; + + +static inline +struct ssb_ohci_device * hcd_to_ssb_ohci(struct usb_hcd *hcd) +{ + return (struct ssb_ohci_device *)(hcd->hcd_priv); +} + + +static const struct ssb_device_id ssb_ohci_table[] = { + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), + SSB_DEVTABLE_END +}; +MODULE_DEVICE_TABLE(ssb, ssb_ohci_table); + + +static int ssb_ohci_reset(struct usb_hcd *hcd) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + int err; + + ohci_hcd_init(ohci); + err = ohci_init(ohci); + + return err; +} + +static int ssb_ohci_start(struct usb_hcd *hcd) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + int err; + + err = ohci_run(ohci); + if (err < 0) { + ohci_err(ohci, "can't start\n"); + ohci_stop(hcd); + } + + return err; +} + +#ifdef CONFIG_PM +static int ssb_ohci_hcd_suspend(struct usb_hcd *hcd, pm_message_t message) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + unsigned long flags; + + spin_lock_irqsave(&ohci->lock, flags); + + ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); + ohci_readl(ohci, &ohci->regs->intrdisable); /* commit write */ + + /* make sure snapshot being resumed re-enumerates everything */ + if (message.event == PM_EVENT_PRETHAW) + ohci_usb_reset(ohci); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + spin_unlock_irqrestore(&ohci->lock, flags); + + return 0; +} + +static int ssb_ohci_hcd_resume(struct usb_hcd *hcd) +{ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + usb_hcd_resume_root_hub(hcd); + return 0; +} +#endif /* CONFIG_PM */ + +static const struct hc_driver ssb_ohci_hc_driver = { + .description = "ssb-usb-ohci", + .product_desc = "SSB OHCI Controller", + .hcd_priv_size = sizeof(struct ssb_ohci_device), + + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + + .reset = ssb_ohci_reset, + .start = ssb_ohci_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + +#ifdef CONFIG_PM + .suspend = ssb_ohci_hcd_suspend, + .resume = ssb_ohci_hcd_resume, +#endif + + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + .get_frame_number = ohci_get_frame, + + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, + +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + + .start_port_reset = ohci_start_port_reset, +}; + + +static void ssb_ohci_detach(struct ssb_device *dev) +{ + struct usb_hcd *hcd = ssb_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + usb_put_hcd(hcd); + ssb_device_disable(dev, 0); +} + +static int ssb_ohci_attach(struct ssb_device *dev) +{ + struct ssb_ohci_device *ohcidev; + struct usb_hcd *hcd; + int err = -ENOMEM; + u32 tmp, flags = 0; + + if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) + flags |= SSB_OHCI_TMSLOW_HOSTMODE; + + ssb_device_enable(dev, flags); + + hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev, + dev->dev->bus_id); + if (!hcd) + goto err_dev_disable; + ohcidev = hcd_to_ssb_ohci(hcd); + ohcidev->enable_flags = flags; + + tmp = ssb_read32(dev, SSB_ADMATCH0); + hcd->rsrc_start = ssb_admatch_base(tmp); + hcd->rsrc_len = ssb_admatch_size(tmp); + 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_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, flags); + return err; +} + +static int ssb_ohci_probe(struct ssb_device *dev, + const struct ssb_device_id *id) +{ + int err; + u16 chipid_top; + + chipid_top = (dev->bus->chip_id & 0xFF00); + if (chipid_top != 0x4700 && + chipid_top != 0x5300) { + /* USBcores are only connected on embedded devices. */ + return -ENODEV; + } + /* TODO: Probably need more checks here whether the core is connected. */ + + if (usb_disabled()) + return -ENODEV; + + /* We currently always attach SSB_DEV_USB11_HOSTDEV + * as HOST OHCI. If we want to attach it as Client device, + * we must branch here and call into the (yet to + * be written) Client mode driver. Same for remove(). */ + + err = ssb_ohci_attach(dev); + + return err; +} + +static void ssb_ohci_remove(struct ssb_device *dev) +{ + ssb_ohci_detach(dev); +} + +#ifdef CONFIG_PM +static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state) +{ + ssb_device_disable(dev, 0); + + return 0; +} + +static int ssb_ohci_resume(struct ssb_device *dev) +{ + struct usb_hcd *hcd = ssb_get_drvdata(dev); + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + + ssb_device_enable(dev, ohcidev->enable_flags); + + return 0; +} +#else /* CONFIG_PM */ +# define ssb_ohci_suspend NULL +# define ssb_ohci_resume NULL +#endif /* CONFIG_PM */ + +static struct ssb_driver ssb_ohci_driver = { + .name = KBUILD_MODNAME, + .id_table = ssb_ohci_table, + .probe = ssb_ohci_probe, + .remove = ssb_ohci_remove, + .suspend = ssb_ohci_suspend, + .resume = ssb_ohci_resume, +}; Index: linux-2.6/drivers/usb/host/ohci-hcd.c =================================================================== --- linux-2.6.orig/drivers/usb/host/ohci-hcd.c 2007-07-12 10:51:46.000000000 +0200 +++ linux-2.6/drivers/usb/host/ohci-hcd.c 2007-07-12 10:52:45.000000000 +0200 @@ -920,11 +920,17 @@ MODULE_LICENSE ("GPL"); #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_sb_driver #endif +#ifdef CONFIG_USB_OHCI_HCD_SSB +#include "ohci-ssb.c" +#define SSB_OHCI_DRIVER ssb_ohci_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ !defined(SA1111_DRIVER) && \ - !defined(PS3_SYSTEM_BUS_DRIVER) + !defined(PS3_SYSTEM_BUS_DRIVER) && \ + !defined(SSB_OHCI_DRIVER) #error "missing bus glue for ohci-hcd" #endif @@ -972,10 +978,20 @@ static int __init ohci_hcd_mod_init(void goto error_pci; #endif +#ifdef SSB_OHCI_DRIVER + retval = ssb_driver_register(&SSB_OHCI_DRIVER); + if (retval) + goto error_ssb; +#endif + return retval; /* Error path */ +#ifdef SSB_OHCI_DRIVER + error_ssb: +#endif #ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); error_pci: #endif #ifdef SA1111_DRIVER @@ -1001,6 +1017,9 @@ module_init(ohci_hcd_mod_init); static void __exit ohci_hcd_mod_exit(void) { +#ifdef SSB_OHCI_DRIVER + ssb_driver_unregister(&SSB_OHCI_DRIVER); +#endif #ifdef PCI_DRIVER pci_unregister_driver(&PCI_DRIVER); #endif Index: linux-2.6/drivers/usb/host/Kconfig =================================================================== --- linux-2.6.orig/drivers/usb/host/Kconfig 2007-07-12 10:51:46.000000000 +0200 +++ linux-2.6/drivers/usb/host/Kconfig 2007-07-12 10:52:45.000000000 +0200 @@ -142,6 +142,19 @@ config USB_OHCI_HCD_PCI Enables support for PCI-bus plug-in USB controller cards. If unsure, say Y. +config USB_OHCI_HCD_SSB + bool "OHCI support for the Broadcom SSB OHCI core (embedded systems only)" + depends on USB_OHCI_HCD && ((USB_OHCI_HCD=m && SSB) || (USB_OHCI_HCD=y && SSB=y)) && EXPERIMENTAL + default n + ---help--- + Support for the Sonics Silicon Backplane (SSB) attached + Broadcom USB OHCI core. + + This device is only present in some embedded devices with + Broadcom based SSB bus. + + If unsure, say N. + config USB_OHCI_BIG_ENDIAN_DESC bool depends on USB_OHCI_HCD -- Greetings Michael.