Felipe: We've been talking about this for a long time. Here at last is an initial attempt at splitting ehci-hcd up into a library module and multiple platform driver modules.
The first patch is just preparation. It moves a few items between .c and .h files, adds a generic ehci_hc_driver structure, and exports a bunch of formerly private routines. The second patch converts ehci-pci.c to a separate module, as an example to show how the whole thing is meant to work. The same approach should be usable for almost all the other platform drivers (ehci-tegra will be problematic). I did ehci-pci first because that's the only one I can test. :-) This isn't exactly what you had in mind because it doesn't introduce another struct device layer. Still, the end result is what you want -- we will be able to build all the EHCI platform drivers into a single kernel. What do you think? Alan Stern Index: usb-3.6/drivers/usb/host/ehci.h =================================================================== --- usb-3.6.orig/drivers/usb/host/ehci.h +++ usb-3.6/drivers/usb/host/ehci.h @@ -761,26 +761,73 @@ static inline u32 hc32_to_cpup (const st /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_PCI - -/* For working around the MosChip frame-index-register bug */ -static unsigned ehci_read_frame_index(struct ehci_hcd *ehci); +#define ehci_dbg(ehci, fmt, args...) \ + dev_dbg(ehci_to_hcd(ehci)->self.controller , fmt , ## args ) +#define ehci_err(ehci, fmt, args...) \ + dev_err(ehci_to_hcd(ehci)->self.controller , fmt , ## args ) +#define ehci_info(ehci, fmt, args...) \ + dev_info(ehci_to_hcd(ehci)->self.controller , fmt , ## args ) +#define ehci_warn(ehci, fmt, args...) \ + dev_warn(ehci_to_hcd(ehci)->self.controller , fmt , ## args ) +#ifdef VERBOSE_DEBUG +#define ehci_vdbg ehci_dbg #else - -static inline unsigned ehci_read_frame_index(struct ehci_hcd *ehci) -{ - return ehci_readl(ehci, &ehci->regs->frame_index); -} - + static inline void ehci_vdbg(struct ehci_hcd *ehci, ...) {} #endif -/*-------------------------------------------------------------------------*/ - #ifndef DEBUG #define STUB_DEBUG_FILES #endif /* DEBUG */ /*-------------------------------------------------------------------------*/ +/* Declarations of things exported for use by ehci platform drivers */ + +extern const struct hc_driver ehci_hc_driver; + +extern irqreturn_t ehci_irq(struct usb_hcd *hcd); + +extern int ehci_setup(struct usb_hcd *hcd); +extern int ehci_run(struct usb_hcd *hcd); +extern void ehci_stop(struct usb_hcd *hcd); +extern void ehci_shutdown(struct usb_hcd *hcd); + +extern int ehci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); +extern int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, + int status); +extern void ehci_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep); +extern void ehci_endpoint_reset(struct usb_hcd *hcd, + struct usb_host_endpoint *ep); + +extern int ehci_get_frame(struct usb_hcd *hcd); + +extern int ehci_hub_status_data(struct usb_hcd *hcd, char *buf); +extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, + u16 wValue, u16 wIndex, + char *buf, u16 wLength); +extern void ehci_relinquish_port(struct usb_hcd *hcd, int portnum); +extern int ehci_port_handed_over(struct usb_hcd *hcd, int portnum); +extern void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd, + struct usb_host_endpoint *ep); + +extern void ehci_port_power(struct ehci_hcd *ehci, int is_on); + +#ifdef CONFIG_PM +extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup); +extern int ehci_resume(struct usb_hcd *hcd, bool hibernated); +extern int ehci_bus_suspend(struct usb_hcd *hcd); +extern int ehci_bus_resume(struct usb_hcd *hcd); + +#else + +#define ehci_bus_suspend NULL +#define ehci_bus_resume NULL +#endif /* CONFIG_PM */ + +extern int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num); +extern int ehci_lpm_check(struct ehci_hcd *ehci, int port); + #endif /* __LINUX_EHCI_HCD_H */ Index: usb-3.6/drivers/usb/host/ehci-hcd.c =================================================================== --- usb-3.6.orig/drivers/usb/host/ehci-hcd.c +++ usb-3.6/drivers/usb/host/ehci-hcd.c @@ -118,9 +118,24 @@ MODULE_PARM_DESC(hird, "host initiated r /*-------------------------------------------------------------------------*/ #include "ehci.h" -#include "ehci-dbg.c" #include "pci-quirks.h" +#ifdef CONFIG_PCI + +/* For working around the MosChip frame-index-register bug */ +static unsigned ehci_read_frame_index(struct ehci_hcd *ehci); + +#else + +static inline unsigned ehci_read_frame_index(struct ehci_hcd *ehci) +{ + return ehci_readl(ehci, &ehci->regs->frame_index); +} + +#endif + +#include "ehci-dbg.c" + /*-------------------------------------------------------------------------*/ /* @@ -338,7 +353,7 @@ static void ehci_silence_controller(stru * This forcibly disables dma and IRQs, helping kexec and other cases * where the next system software may expect clean state. */ -static void ehci_shutdown(struct usb_hcd *hcd) +void ehci_shutdown(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -352,8 +367,9 @@ static void ehci_shutdown(struct usb_hcd hrtimer_cancel(&ehci->hrtimer); } +EXPORT_SYMBOL_GPL(ehci_shutdown); -static void ehci_port_power (struct ehci_hcd *ehci, int is_on) +void ehci_port_power(struct ehci_hcd *ehci, int is_on) { unsigned port; @@ -370,6 +386,7 @@ static void ehci_port_power (struct ehci ehci_readl(ehci, &ehci->regs->command); msleep(20); } +EXPORT_SYMBOL_GPL(ehci_port_power); /*-------------------------------------------------------------------------*/ @@ -411,7 +428,7 @@ static void ehci_work (struct ehci_hcd * /* * Called when the ehci_hcd module is removed. */ -static void ehci_stop (struct usb_hcd *hcd) +void ehci_stop(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); @@ -451,6 +468,7 @@ static void ehci_stop (struct usb_hcd *h dbg_status (ehci, "ehci_stop completed", ehci_readl(ehci, &ehci->regs->status)); } +EXPORT_SYMBOL_GPL(ehci_stop); /* one-time init, only for memory state */ static int ehci_init(struct usb_hcd *hcd) @@ -575,7 +593,7 @@ static int ehci_init(struct usb_hcd *hcd } /* start HC running; it's halted, ehci_init() has been run (once) */ -static int ehci_run (struct usb_hcd *hcd) +int ehci_run(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; @@ -659,8 +677,9 @@ static int ehci_run (struct usb_hcd *hcd return 0; } +EXPORT_SYMBOL_GPL(ehci_run); -static int ehci_setup(struct usb_hcd *hcd) +int ehci_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; @@ -691,10 +710,11 @@ static int ehci_setup(struct usb_hcd *hc return 0; } +EXPORT_SYMBOL_GPL(ehci_setup); /*-------------------------------------------------------------------------*/ -static irqreturn_t ehci_irq (struct usb_hcd *hcd) +irqreturn_t ehci_irq(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 status, masked_status, pcd_status = 0, cmd; @@ -842,6 +862,7 @@ dead: usb_hcd_poll_rh_status(hcd); return IRQ_HANDLED; } +EXPORT_SYMBOL_GPL(ehci_irq); /*-------------------------------------------------------------------------*/ @@ -857,7 +878,7 @@ dead: * NOTE: control, bulk, and interrupt share the same code to append TDs * to a (possibly active) QH, and the same QH scanning code. */ -static int ehci_urb_enqueue ( +int ehci_urb_enqueue( struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags @@ -893,12 +914,12 @@ static int ehci_urb_enqueue ( return sitd_submit (ehci, urb, mem_flags); } } +EXPORT_SYMBOL_GPL(ehci_urb_enqueue); /* remove from hardware lists * completions normally happen asynchronously */ - -static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_qh *qh; @@ -963,13 +984,13 @@ done: spin_unlock_irqrestore (&ehci->lock, flags); return rc; } +EXPORT_SYMBOL_GPL(ehci_urb_dequeue); /*-------------------------------------------------------------------------*/ // bulk qh holds the data toggle -static void -ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) +void ehci_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); unsigned long flags; @@ -1040,9 +1061,9 @@ idle_timeout: ep->hcpriv = NULL; spin_unlock_irqrestore (&ehci->lock, flags); } +EXPORT_SYMBOL_GPL(ehci_endpoint_disable); -static void -ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +void ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_qh *qh; @@ -1081,12 +1102,14 @@ ehci_endpoint_reset(struct usb_hcd *hcd, } spin_unlock_irqrestore(&ehci->lock, flags); } +EXPORT_SYMBOL_GPL(ehci_endpoint_reset); -static int ehci_get_frame (struct usb_hcd *hcd) +int ehci_get_frame(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); return (ehci_read_frame_index(ehci) >> 3) % ehci->periodic_size; } +EXPORT_SYMBOL_GPL(ehci_get_frame); /*-------------------------------------------------------------------------*/ @@ -1096,7 +1119,7 @@ static int ehci_get_frame (struct usb_hc /* These routines handle the generic parts of controller suspend/resume */ -static int __maybe_unused ehci_suspend(struct usb_hcd *hcd, bool do_wakeup) +int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -1119,9 +1142,10 @@ static int __maybe_unused ehci_suspend(s return 0; } +EXPORT_SYMBOL_GPL(ehci_suspend); /* Returns 0 if power was preserved, 1 if power was lost */ -static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) +int ehci_resume(struct usb_hcd *hcd, bool hibernated) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -1182,11 +1206,64 @@ static int __maybe_unused ehci_resume(st return 1; } +EXPORT_SYMBOL_GPL(ehci_resume); #endif /*-------------------------------------------------------------------------*/ +/* + * Generic structure; platform drivers can copy this and override individual + * entries as needed. + */ + +const struct hc_driver ehci_hc_driver = { + .description = hcd_name, + .product_desc = "EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_init, + .start = ehci_run, + .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, + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, + + /* + * 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, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; +EXPORT_SYMBOL_GPL(ehci_hc_driver); + +/*-------------------------------------------------------------------------*/ + /* * The EHCI in ChipIdea HDRC cannot be a separate module or device, * because its registers (and irq) are shared between host/gadget/otg Index: usb-3.6/drivers/usb/host/ehci-hub.c =================================================================== --- usb-3.6.orig/drivers/usb/host/ehci-hub.c +++ usb-3.6/drivers/usb/host/ehci-hub.c @@ -33,15 +33,6 @@ #ifdef CONFIG_PM -static int ehci_hub_control( - struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, - u16 wIndex, - char *buf, - u16 wLength -); - /* After a power loss, ports that were owned by the companion must be * reset so that the companion can still own them. */ @@ -205,7 +196,7 @@ static void ehci_adjust_port_wakeup_flag spin_unlock_irq(&ehci->lock); } -static int ehci_bus_suspend (struct usb_hcd *hcd) +int ehci_bus_suspend(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); int port; @@ -335,10 +326,11 @@ static int ehci_bus_suspend (struct usb_ hrtimer_cancel(&ehci->hrtimer); return 0; } +EXPORT_SYMBOL_GPL(ehci_bus_suspend); /* caller has locked the root hub, and should reset/reinit on error */ -static int ehci_bus_resume (struct usb_hcd *hcd) +int ehci_bus_resume(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; @@ -463,11 +455,7 @@ static int ehci_bus_resume (struct usb_h spin_unlock_irq(&ehci->lock); return -ESHUTDOWN; } - -#else - -#define ehci_bus_suspend NULL -#define ehci_bus_resume NULL +EXPORT_SYMBOL_GPL(ehci_bus_resume); #endif /* CONFIG_PM */ @@ -556,8 +544,7 @@ static int check_reset_complete ( /* build "status change" packet (one or two bytes) from HC registers */ -static int -ehci_hub_status_data (struct usb_hcd *hcd, char *buf) +int ehci_hub_status_data(struct usb_hcd *hcd, char *buf) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp, status; @@ -627,6 +614,7 @@ ehci_hub_status_data (struct usb_hcd *hc spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0; } +EXPORT_SYMBOL_GPL(ehci_hub_status_data); /*-------------------------------------------------------------------------*/ @@ -665,7 +653,7 @@ ehci_hub_descriptor ( /*-------------------------------------------------------------------------*/ -static int ehci_hub_control ( +int ehci_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, @@ -1087,9 +1075,9 @@ error_exit: spin_unlock_irqrestore (&ehci->lock, flags); return retval; } +EXPORT_SYMBOL_GPL(ehci_hub_control); -static void __maybe_unused ehci_relinquish_port(struct usb_hcd *hcd, - int portnum) +void ehci_relinquish_port(struct usb_hcd *hcd, int portnum) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -1097,9 +1085,9 @@ static void __maybe_unused ehci_relinqui return; set_owner(ehci, --portnum, PORT_OWNER); } +EXPORT_SYMBOL_GPL(ehci_relinquish_port); -static int __maybe_unused ehci_port_handed_over(struct usb_hcd *hcd, - int portnum) +int ehci_port_handed_over(struct usb_hcd *hcd, int portnum) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); u32 __iomem *reg; @@ -1109,3 +1097,4 @@ static int __maybe_unused ehci_port_hand reg = &ehci->regs->port_status[portnum - 1]; return ehci_readl(ehci, reg) & PORT_OWNER; } +EXPORT_SYMBOL_GPL(ehci_port_handed_over); Index: usb-3.6/drivers/usb/host/ehci-q.c =================================================================== --- usb-3.6.orig/drivers/usb/host/ehci-q.c +++ usb-3.6/drivers/usb/host/ehci-q.c @@ -141,7 +141,7 @@ qh_refresh (struct ehci_hcd *ehci, struc static void qh_link_async(struct ehci_hcd *ehci, struct ehci_qh *qh); -static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd, +void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd, struct usb_host_endpoint *ep) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -155,6 +155,7 @@ static void ehci_clear_tt_buffer_complet qh_link_async(ehci, qh); spin_unlock_irqrestore(&ehci->lock, flags); } +EXPORT_SYMBOL_GPL(ehci_clear_tt_buffer_complete); static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh, struct urb *urb, u32 token) Index: usb-3.6/drivers/usb/host/ehci-lpm.c =================================================================== --- usb-3.6.orig/drivers/usb/host/ehci-lpm.c +++ usb-3.6/drivers/usb/host/ehci-lpm.c @@ -17,8 +17,7 @@ */ /* this file is part of ehci-hcd.c */ -static int __maybe_unused ehci_lpm_set_da(struct ehci_hcd *ehci, - int dev_addr, int port_num) +int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num) { u32 __iomem portsc; @@ -33,12 +32,13 @@ static int __maybe_unused ehci_lpm_set_d ehci_writel(ehci, portsc, &ehci->regs->port_status[port_num-1]); return 0; } +EXPORT_SYMBOL_GPL(ehci_lpm_set_da); /* * this function is used to check if the device support LPM * if yes, mark the PORTSC register with PORT_LPM bit */ -static int __maybe_unused ehci_lpm_check(struct ehci_hcd *ehci, int port) +int ehci_lpm_check(struct ehci_hcd *ehci, int port) { u32 __iomem *portsc ; u32 val32; @@ -82,3 +82,4 @@ static int __maybe_unused ehci_lpm_check return retval; } +EXPORT_SYMBOL_GPL(ehci_lpm_check); Index: usb-3.6/drivers/usb/host/ehci-sched.c =================================================================== --- usb-3.6.orig/drivers/usb/host/ehci-sched.c +++ usb-3.6/drivers/usb/host/ehci-sched.c @@ -34,8 +34,6 @@ * pre-calculated schedule data to make appending to the queue be quick. */ -static int ehci_get_frame (struct usb_hcd *hcd); - #ifdef CONFIG_PCI static unsigned ehci_read_frame_index(struct ehci_hcd *ehci) Index: usb-3.6/drivers/usb/host/ehci-dbg.c =================================================================== --- usb-3.6.orig/drivers/usb/host/ehci-dbg.c +++ usb-3.6/drivers/usb/host/ehci-dbg.c @@ -18,21 +18,6 @@ /* this file is part of ehci-hcd.c */ -#define ehci_dbg(ehci, fmt, args...) \ - dev_dbg (ehci_to_hcd(ehci)->self.controller , fmt , ## args ) -#define ehci_err(ehci, fmt, args...) \ - dev_err (ehci_to_hcd(ehci)->self.controller , fmt , ## args ) -#define ehci_info(ehci, fmt, args...) \ - dev_info (ehci_to_hcd(ehci)->self.controller , fmt , ## args ) -#define ehci_warn(ehci, fmt, args...) \ - dev_warn (ehci_to_hcd(ehci)->self.controller , fmt , ## args ) - -#ifdef VERBOSE_DEBUG -# define ehci_vdbg ehci_dbg -#else - static inline void ehci_vdbg(struct ehci_hcd *ehci, ...) {} -#endif - #ifdef DEBUG /* check the values in the HCSPARAMS register -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html