Return-path: Received: from xenotime.net ([66.160.160.81]:60237 "HELO xenotime.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with SMTP id S1755248AbXGLQBI (ORCPT ); Thu, 12 Jul 2007 12:01:08 -0400 Received: from midway.site ([71.117.236.25]) by xenotime.net for ; Thu, 12 Jul 2007 09:01:06 -0700 Date: Thu, 12 Jul 2007 09:04:59 -0700 From: Randy Dunlap To: mb@bu3sch.de, gregkh Cc: Andrew Morton , John Linville , Aurelien Jarno , linux-wireless@vger.kernel.org, Gary Zambrano Subject: Re: [patch 2/2] ssb: Add a driver for the Broadcom OHCI core Message-Id: <20070712090459.ed76899c.rdunlap@xenotime.net> In-Reply-To: <20070712085744.961584000@bu3sch.de> References: <20070712085432.528319000@bu3sch.de> <20070712085744.961584000@bu3sch.de> Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Sender: linux-wireless-owner@vger.kernel.org List-ID: On Thu, 12 Jul 2007 10:54:34 +0200 mb@bu3sch.de wrote: > This adds a driver for the Broadcom USB OHCI HCD device. > These devices are found in various embedded Broadcom-47xx machines. I would expect USB host drivers to be submitted to the linux-usb-devel@lists.sourceforge.net mailing list... > 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. > > - > To unsubscribe from this list: send the line "unsubscribe linux-wireless" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > --- ~Randy *** Remember to use Documentation/SubmitChecklist when testing your code ***